]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ip/ethermedium.c
kernel: dont use atomic increment for Proc.nlocks, maintain Lock.m for lock(), use...
[plan9front.git] / sys / src / 9 / ip / ethermedium.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/error.h"
7
8 #include "../port/netif.h"
9 #include "ip.h"
10 #include "ipv6.h"
11
12 typedef struct Etherhdr Etherhdr;
13 struct Etherhdr
14 {
15         uchar   d[6];
16         uchar   s[6];
17         uchar   t[2];
18 };
19
20 static uchar ipbroadcast[IPaddrlen] = {
21         0xff,0xff,0xff,0xff,
22         0xff,0xff,0xff,0xff,
23         0xff,0xff,0xff,0xff,
24         0xff,0xff,0xff,0xff,
25 };
26
27 static uchar etherbroadcast[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
28
29 static void     etherread4(void *a);
30 static void     etherread6(void *a);
31 static void     etherbind(Ipifc *ifc, int argc, char **argv);
32 static void     etherunbind(Ipifc *ifc);
33 static void     etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip);
34 static void     etheraddmulti(Ipifc *ifc, uchar *a, uchar *ia);
35 static void     etherremmulti(Ipifc *ifc, uchar *a, uchar *ia);
36 static Block*   multicastarp(Fs *f, Arpent *a, Medium*, uchar *mac);
37 static void     sendarp(Ipifc *ifc, Arpent *a);
38 static void     sendgarp(Ipifc *ifc, uchar*);
39 static int      multicastea(uchar *ea, uchar *ip);
40 static void     recvarpproc(void*);
41 static void     resolveaddr6(Ipifc *ifc, Arpent *a);
42 static void     etherpref2addr(uchar *pref, uchar *ea);
43
44 Medium ethermedium =
45 {
46 .name=          "ether",
47 .hsize=         14,
48 .mintu=         60,
49 .maxtu=         1514,
50 .maclen=        6,
51 .bind=          etherbind,
52 .unbind=        etherunbind,
53 .bwrite=        etherbwrite,
54 .addmulti=      etheraddmulti,
55 .remmulti=      etherremmulti,
56 .ares=          arpenter,
57 .areg=          sendgarp,
58 .pref2addr=     etherpref2addr,
59 };
60
61 Medium gbemedium =
62 {
63 .name=          "gbe",
64 .hsize=         14,
65 .mintu=         60,
66 .maxtu=         9014,
67 .maclen=        6,
68 .bind=          etherbind,
69 .unbind=        etherunbind,
70 .bwrite=        etherbwrite,
71 .addmulti=      etheraddmulti,
72 .remmulti=      etherremmulti,
73 .ares=          arpenter,
74 .areg=          sendgarp,
75 .pref2addr=     etherpref2addr,
76 };
77
78 typedef struct  Etherrock Etherrock;
79 struct Etherrock
80 {
81         Fs      *f;             /* file system we belong to */
82         Proc    *arpp;          /* arp process */
83         Proc    *read4p;        /* reading process (v4)*/
84         Proc    *read6p;        /* reading process (v6)*/
85         Chan    *mchan4;        /* Data channel for v4 */
86         Chan    *achan;         /* Arp channel */
87         Chan    *cchan4;        /* Control channel for v4 */
88         Chan    *mchan6;        /* Data channel for v6 */
89         Chan    *cchan6;        /* Control channel for v6 */
90 };
91
92 /*
93  *  ethernet arp request
94  */
95 enum
96 {
97         ARPREQUEST      = 1,
98         ARPREPLY        = 2,
99 };
100
101 typedef struct Etherarp Etherarp;
102 struct Etherarp
103 {
104         uchar   d[6];
105         uchar   s[6];
106         uchar   type[2];
107         uchar   hrd[2];
108         uchar   pro[2];
109         uchar   hln;
110         uchar   pln;
111         uchar   op[2];
112         uchar   sha[6];
113         uchar   spa[4];
114         uchar   tha[6];
115         uchar   tpa[4];
116 };
117
118 static char *nbmsg = "nonblocking";
119
120 /*
121  *  called to bind an IP ifc to an ethernet device
122  *  called with ifc wlock'd
123  */
124 static void
125 etherbind(Ipifc *ifc, int argc, char **argv)
126 {
127         Chan *mchan4, *cchan4, *achan, *mchan6, *cchan6, *schan;
128         char addr[Maxpath];     //char addr[2*KNAMELEN];
129         char dir[Maxpath];      //char dir[2*KNAMELEN];
130         char *buf;
131         int n;
132         char *ptr;
133         Etherrock *er;
134
135         if(argc < 2)
136                 error(Ebadarg);
137
138         mchan4 = cchan4 = achan = mchan6 = cchan6 = nil;
139         buf = nil;
140         if(waserror()){
141                 if(mchan4 != nil)
142                         cclose(mchan4);
143                 if(cchan4 != nil)
144                         cclose(cchan4);
145                 if(achan != nil)
146                         cclose(achan);
147                 if(mchan6 != nil)
148                         cclose(mchan6);
149                 if(cchan6 != nil)
150                         cclose(cchan6);
151                 if(buf != nil)
152                         free(buf);
153                 nexterror();
154         }
155
156         /*
157          *  open ipv4 conversation
158          *
159          *  the dial will fail if the type is already open on
160          *  this device.
161          */
162         snprint(addr, sizeof(addr), "%s!0x800", argv[2]);       /* ETIP4 */
163         mchan4 = chandial(addr, nil, dir, &cchan4);
164
165         /*
166          *  make it non-blocking
167          */
168         devtab[cchan4->type]->write(cchan4, nbmsg, strlen(nbmsg), 0);
169
170         /*
171          *  get mac address and speed
172          */
173         snprint(addr, sizeof(addr), "%s/stats", argv[2]);
174         buf = smalloc(512);
175         schan = namec(addr, Aopen, OREAD, 0);
176         if(waserror()){
177                 cclose(schan);
178                 nexterror();
179         }
180         n = devtab[schan->type]->read(schan, buf, 511, 0);
181         cclose(schan);
182         poperror();
183         buf[n] = 0;
184
185         ptr = strstr(buf, "addr: ");
186         if(!ptr)
187                 error(Eio);
188         ptr += 6;
189         parsemac(ifc->mac, ptr, 6);
190
191         ptr = strstr(buf, "mbps: ");
192         if(ptr){
193                 ptr += 6;
194                 ifc->mbps = atoi(ptr);
195         } else
196                 ifc->mbps = 100;
197
198         /*
199          *  open arp conversation
200          */
201         snprint(addr, sizeof(addr), "%s!0x806", argv[2]);       /* ETARP */
202         achan = chandial(addr, nil, nil, nil);
203
204         /*
205          *  open ipv6 conversation
206          *
207          *  the dial will fail if the type is already open on
208          *  this device.
209          */
210         snprint(addr, sizeof(addr), "%s!0x86DD", argv[2]);      /* ETIP6 */
211         mchan6 = chandial(addr, nil, dir, &cchan6);
212
213         /*
214          *  make it non-blocking
215          */
216         devtab[cchan6->type]->write(cchan6, nbmsg, strlen(nbmsg), 0);
217
218         er = smalloc(sizeof(*er));
219         er->mchan4 = mchan4;
220         er->cchan4 = cchan4;
221         er->achan = achan;
222         er->mchan6 = mchan6;
223         er->cchan6 = cchan6;
224         er->f = ifc->conv->p->f;
225         ifc->arg = er;
226
227         free(buf);
228         poperror();
229
230         kproc("etherread4", etherread4, ifc);
231         kproc("recvarpproc", recvarpproc, ifc);
232         kproc("etherread6", etherread6, ifc);
233 }
234
235 /*
236  *  called with ifc wlock'd
237  */
238 static void
239 etherunbind(Ipifc *ifc)
240 {
241         Etherrock *er = ifc->arg;
242
243         if(er->read4p)
244                 postnote(er->read4p, 1, "unbind", 0);
245         if(er->read6p)
246                 postnote(er->read6p, 1, "unbind", 0);
247         if(er->arpp)
248                 postnote(er->arpp, 1, "unbind", 0);
249
250         /* wait for readers to die */
251         while(er->arpp != 0 || er->read4p != 0 || er->read6p != 0)
252                 tsleep(&up->sleep, return0, 0, 300);
253
254         if(er->mchan4 != nil)
255                 cclose(er->mchan4);
256         if(er->achan != nil)
257                 cclose(er->achan);
258         if(er->cchan4 != nil)
259                 cclose(er->cchan4);
260         if(er->mchan6 != nil)
261                 cclose(er->mchan6);
262         if(er->cchan6 != nil)
263                 cclose(er->cchan6);
264
265         free(er);
266 }
267
268 /*
269  *  called by ipoput with a single block to write with ifc rlock'd
270  */
271 static void
272 etherbwrite(Ipifc *ifc, Block *bp, int version, uchar *ip)
273 {
274         Etherhdr *eh;
275         Arpent *a;
276         uchar mac[6];
277         Etherrock *er = ifc->arg;
278
279         /* get mac address of destination */
280         a = arpget(er->f->arp, bp, version, ifc, ip, mac);
281         if(a){
282                 /* check for broadcast or multicast */
283                 bp = multicastarp(er->f, a, ifc->m, mac);
284                 if(bp==nil){
285                         switch(version){
286                         case V4:
287                                 sendarp(ifc, a);
288                                 break;
289                         case V6:
290                                 resolveaddr6(ifc, a);
291                                 break;
292                         default:
293                                 panic("etherbwrite: version %d", version);
294                         }
295                         return;
296                 }
297         }
298
299         /* make it a single block with space for the ether header */
300         bp = padblock(bp, ifc->m->hsize);
301         if(bp->next)
302                 bp = concatblock(bp);
303         if(BLEN(bp) < ifc->mintu)
304                 bp = adjustblock(bp, ifc->mintu);
305         eh = (Etherhdr*)bp->rp;
306
307         /* copy in mac addresses and ether type */
308         memmove(eh->s, ifc->mac, sizeof(eh->s));
309         memmove(eh->d, mac, sizeof(eh->d));
310
311         switch(version){
312         case V4:
313                 eh->t[0] = 0x08;
314                 eh->t[1] = 0x00;
315                 devtab[er->mchan4->type]->bwrite(er->mchan4, bp, 0);
316                 break;
317         case V6:
318                 eh->t[0] = 0x86;
319                 eh->t[1] = 0xDD;
320                 devtab[er->mchan6->type]->bwrite(er->mchan6, bp, 0);
321                 break;
322         default:
323                 panic("etherbwrite2: version %d", version);
324         }
325         ifc->out++;
326 }
327
328
329 /*
330  *  process to read from the ethernet
331  */
332 static void
333 etherread4(void *a)
334 {
335         Ipifc *ifc;
336         Block *bp;
337         Etherrock *er;
338
339         ifc = a;
340         er = ifc->arg;
341         er->read4p = up;        /* hide identity under a rock for unbind */
342         if(waserror()){
343                 er->read4p = 0;
344                 pexit("hangup", 1);
345         }
346         for(;;){
347                 bp = devtab[er->mchan4->type]->bread(er->mchan4, ifc->maxtu, 0);
348                 if(!canrlock(ifc)){
349                         freeb(bp);
350                         continue;
351                 }
352                 if(waserror()){
353                         runlock(ifc);
354                         nexterror();
355                 }
356                 ifc->in++;
357                 if(ifc->lifc == nil || BLEN(bp) <= ifc->m->hsize)
358                         freeb(bp);
359                 else {
360                         bp->rp += ifc->m->hsize;
361                         ipiput4(er->f, ifc, bp);
362                 }
363                 runlock(ifc);
364                 poperror();
365         }
366 }
367
368
369 /*
370  *  process to read from the ethernet, IPv6
371  */
372 static void
373 etherread6(void *a)
374 {
375         Ipifc *ifc;
376         Block *bp;
377         Etherrock *er;
378
379         ifc = a;
380         er = ifc->arg;
381         er->read6p = up;        /* hide identity under a rock for unbind */
382         if(waserror()){
383                 er->read6p = 0;
384                 pexit("hangup", 1);
385         }
386         for(;;){
387                 bp = devtab[er->mchan6->type]->bread(er->mchan6, ifc->maxtu, 0);
388                 if(!canrlock(ifc)){
389                         freeb(bp);
390                         continue;
391                 }
392                 if(waserror()){
393                         runlock(ifc);
394                         nexterror();
395                 }
396                 ifc->in++;
397                 if(ifc->lifc == nil || BLEN(bp) <= ifc->m->hsize)
398                         freeb(bp);
399                 else {
400                         bp->rp += ifc->m->hsize;
401                         ipiput6(er->f, ifc, bp);
402                 }
403                 runlock(ifc);
404                 poperror();
405         }
406 }
407
408 static void
409 etheraddmulti(Ipifc *ifc, uchar *a, uchar *)
410 {
411         uchar mac[6];
412         char buf[64];
413         Etherrock *er = ifc->arg;
414         int version;
415
416         version = multicastea(mac, a);
417         sprint(buf, "addmulti %E", mac);
418         switch(version){
419         case V4:
420                 devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);
421                 break;
422         case V6:
423                 devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);
424                 break;
425         default:
426                 panic("etheraddmulti: version %d", version);
427         }
428 }
429
430 static void
431 etherremmulti(Ipifc *ifc, uchar *a, uchar *)
432 {
433         uchar mac[6];
434         char buf[64];
435         Etherrock *er = ifc->arg;
436         int version;
437
438         version = multicastea(mac, a);
439         sprint(buf, "remmulti %E", mac);
440         switch(version){
441         case V4:
442                 devtab[er->cchan4->type]->write(er->cchan4, buf, strlen(buf), 0);
443                 break;
444         case V6:
445                 devtab[er->cchan6->type]->write(er->cchan6, buf, strlen(buf), 0);
446                 break;
447         default:
448                 panic("etherremmulti: version %d", version);
449         }
450 }
451
452 /*
453  *  send an ethernet arp
454  *  (only v4, v6 uses the neighbor discovery, rfc1970)
455  */
456 static void
457 sendarp(Ipifc *ifc, Arpent *a)
458 {
459         int n;
460         Block *bp;
461         Etherarp *e;
462         Etherrock *er = ifc->arg;
463
464         /* don't do anything if it's been less than a second since the last */
465         if(NOW - a->ctime < 1000){
466                 arprelease(er->f->arp, a);
467                 return;
468         }
469
470         /* remove all but the last message */
471         while((bp = a->hold) != nil){
472                 if(bp == a->last)
473                         break;
474                 a->hold = bp->list;
475                 freeblist(bp);
476         }
477
478         /* try to keep it around for a second more */
479         a->ctime = NOW;
480         arprelease(er->f->arp, a);
481
482         n = sizeof(Etherarp);
483         if(n < a->type->mintu)
484                 n = a->type->mintu;
485         bp = allocb(n);
486         memset(bp->rp, 0, n);
487         e = (Etherarp*)bp->rp;
488         memmove(e->tpa, a->ip+IPv4off, sizeof(e->tpa));
489         ipv4local(ifc, e->spa);
490         memmove(e->sha, ifc->mac, sizeof(e->sha));
491         memset(e->d, 0xff, sizeof(e->d));               /* ethernet broadcast */
492         memmove(e->s, ifc->mac, sizeof(e->s));
493
494         hnputs(e->type, ETARP);
495         hnputs(e->hrd, 1);
496         hnputs(e->pro, ETIP4);
497         e->hln = sizeof(e->sha);
498         e->pln = sizeof(e->spa);
499         hnputs(e->op, ARPREQUEST);
500         bp->wp += n;
501
502         devtab[er->achan->type]->bwrite(er->achan, bp, 0);
503 }
504
505 static void
506 resolveaddr6(Ipifc *ifc, Arpent *a)
507 {
508         int sflag;
509         Block *bp;
510         Etherrock *er = ifc->arg;
511         uchar ipsrc[IPaddrlen];
512
513         /* don't do anything if it's been less than a second since the last */
514         if(NOW - a->ctime < ReTransTimer){
515                 arprelease(er->f->arp, a);
516                 return;
517         }
518
519         /* remove all but the last message */
520         while((bp = a->hold) != nil){
521                 if(bp == a->last)
522                         break;
523                 a->hold = bp->list;
524                 freeblist(bp);
525         }
526
527         /* try to keep it around for a second more */
528         a->ctime = NOW;
529         a->rtime = NOW + ReTransTimer;
530         if(a->rxtsrem <= 0) {
531                 arprelease(er->f->arp, a);
532                 return;
533         }
534
535         a->rxtsrem--;
536         arprelease(er->f->arp, a);
537
538         if(sflag = ipv6anylocal(ifc, ipsrc))
539                 icmpns(er->f, ipsrc, sflag, a->ip, TARG_MULTI, ifc->mac);
540 }
541
542 /*
543  *  send a gratuitous arp to refresh arp caches
544  */
545 static void
546 sendgarp(Ipifc *ifc, uchar *ip)
547 {
548         int n;
549         Block *bp;
550         Etherarp *e;
551         Etherrock *er = ifc->arg;
552
553         /* don't arp for our initial non address */
554         if(ipcmp(ip, IPnoaddr) == 0)
555                 return;
556
557         n = sizeof(Etherarp);
558         if(n < ifc->m->mintu)
559                 n = ifc->m->mintu;
560         bp = allocb(n);
561         memset(bp->rp, 0, n);
562         e = (Etherarp*)bp->rp;
563         memmove(e->tpa, ip+IPv4off, sizeof(e->tpa));
564         memmove(e->spa, ip+IPv4off, sizeof(e->spa));
565         memmove(e->sha, ifc->mac, sizeof(e->sha));
566         memset(e->d, 0xff, sizeof(e->d));               /* ethernet broadcast */
567         memmove(e->s, ifc->mac, sizeof(e->s));
568
569         hnputs(e->type, ETARP);
570         hnputs(e->hrd, 1);
571         hnputs(e->pro, ETIP4);
572         e->hln = sizeof(e->sha);
573         e->pln = sizeof(e->spa);
574         hnputs(e->op, ARPREQUEST);
575         bp->wp += n;
576
577         devtab[er->achan->type]->bwrite(er->achan, bp, 0);
578 }
579
580 static void
581 recvarp(Ipifc *ifc)
582 {
583         int n;
584         Block *ebp, *rbp;
585         Etherarp *e, *r;
586         uchar ip[IPaddrlen];
587         static uchar eprinted[4];
588         Etherrock *er = ifc->arg;
589
590         ebp = devtab[er->achan->type]->bread(er->achan, ifc->maxtu, 0);
591         if(ebp == nil)
592                 return;
593
594         e = (Etherarp*)ebp->rp;
595         switch(nhgets(e->op)) {
596         default:
597                 break;
598
599         case ARPREPLY:
600                 /* check for machine using my ip address */
601                 v4tov6(ip, e->spa);
602                 if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
603                         if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
604                                 print("arprep: 0x%E/0x%E also has ip addr %V\n",
605                                         e->s, e->sha, e->spa);
606                                 break;
607                         }
608                 }
609
610                 /* make sure we're not entering broadcast addresses */
611                 if(ipcmp(ip, ipbroadcast) == 0 ||
612                         !memcmp(e->sha, etherbroadcast, sizeof(e->sha))){
613                         print("arprep: 0x%E/0x%E cannot register broadcast address %I\n",
614                                 e->s, e->sha, e->spa);
615                         break;
616                 }
617
618                 arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 0);
619                 break;
620
621         case ARPREQUEST:
622                 /* don't answer arps till we know who we are */
623                 if(ifc->lifc == 0)
624                         break;
625
626                 /* check for machine using my ip or ether address */
627                 v4tov6(ip, e->spa);
628                 if(iplocalonifc(ifc, ip) || ipproxyifc(er->f, ifc, ip)){
629                         if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) != 0){
630                                 if (memcmp(eprinted, e->spa, sizeof(e->spa))){
631                                         /* print only once */
632                                         print("arpreq: 0x%E also has ip addr %V\n", e->sha, e->spa);
633                                         memmove(eprinted, e->spa, sizeof(e->spa));
634                                 }
635                         }
636                 } else {
637                         if(memcmp(e->sha, ifc->mac, sizeof(e->sha)) == 0){
638                                 print("arpreq: %V also has ether addr %E\n", e->spa, e->sha);
639                                 break;
640                         }
641                 }
642
643                 /* refresh what we know about sender */
644                 arpenter(er->f, V4, e->spa, e->sha, sizeof(e->sha), 1);
645
646                 /* answer only requests for our address or systems we're proxying for */
647                 v4tov6(ip, e->tpa);
648                 if(!iplocalonifc(ifc, ip))
649                 if(!ipproxyifc(er->f, ifc, ip))
650                         break;
651
652                 n = sizeof(Etherarp);
653                 if(n < ifc->mintu)
654                         n = ifc->mintu;
655                 rbp = allocb(n);
656                 r = (Etherarp*)rbp->rp;
657                 memset(r, 0, sizeof(Etherarp));
658                 hnputs(r->type, ETARP);
659                 hnputs(r->hrd, 1);
660                 hnputs(r->pro, ETIP4);
661                 r->hln = sizeof(r->sha);
662                 r->pln = sizeof(r->spa);
663                 hnputs(r->op, ARPREPLY);
664                 memmove(r->tha, e->sha, sizeof(r->tha));
665                 memmove(r->tpa, e->spa, sizeof(r->tpa));
666                 memmove(r->sha, ifc->mac, sizeof(r->sha));
667                 memmove(r->spa, e->tpa, sizeof(r->spa));
668                 memmove(r->d, e->sha, sizeof(r->d));
669                 memmove(r->s, ifc->mac, sizeof(r->s));
670                 rbp->wp += n;
671
672                 devtab[er->achan->type]->bwrite(er->achan, rbp, 0);
673         }
674         freeb(ebp);
675 }
676
677 static void
678 recvarpproc(void *v)
679 {
680         Ipifc *ifc = v;
681         Etherrock *er = ifc->arg;
682
683         er->arpp = up;
684         if(waserror()){
685                 er->arpp = 0;
686                 pexit("hangup", 1);
687         }
688         for(;;)
689                 recvarp(ifc);
690 }
691
692 static int
693 multicastea(uchar *ea, uchar *ip)
694 {
695         int x;
696
697         switch(x = ipismulticast(ip)){
698         case V4:
699                 ea[0] = 0x01;
700                 ea[1] = 0x00;
701                 ea[2] = 0x5e;
702                 ea[3] = ip[13] & 0x7f;
703                 ea[4] = ip[14];
704                 ea[5] = ip[15];
705                 break;
706         case V6:
707                 ea[0] = 0x33;
708                 ea[1] = 0x33;
709                 ea[2] = ip[12];
710                 ea[3] = ip[13];
711                 ea[4] = ip[14];
712                 ea[5] = ip[15];
713                 break;
714         }
715         return x;
716 }
717
718 /*
719  *  fill in an arp entry for broadcast or multicast
720  *  addresses.  Return the first queued packet for the
721  *  IP address.
722  */
723 static Block*
724 multicastarp(Fs *f, Arpent *a, Medium *medium, uchar *mac)
725 {
726         /* is it broadcast? */
727         switch(ipforme(f, a->ip)){
728         case Runi:
729                 return nil;
730         case Rbcast:
731                 memset(mac, 0xff, 6);
732                 return arpresolve(f->arp, a, medium, mac);
733         default:
734                 break;
735         }
736
737         /* if multicast, fill in mac */
738         switch(multicastea(mac, a->ip)){
739         case V4:
740         case V6:
741                 return arpresolve(f->arp, a, medium, mac);
742         }
743
744         /* let arp take care of it */
745         return nil;
746 }
747
748 void
749 ethermediumlink(void)
750 {
751         addipmedium(&ethermedium);
752         addipmedium(&gbemedium);
753 }
754
755
756 static void
757 etherpref2addr(uchar *pref, uchar *ea)
758 {
759         pref[8] = ea[0] | 0x2;
760         pref[9] = ea[1];
761         pref[10] = ea[2];
762         pref[11] = 0xFF;
763         pref[12] = 0xFE;
764         pref[13] = ea[3];
765         pref[14] = ea[4];
766         pref[15] = ea[5];
767 }