Netaux functions and devmnt.
[akaros.git] / kern / src / net / ip.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        "ip.h"
9
10 typedef struct Ip4hdr           Ip4hdr;
11 typedef struct IP               IP;
12 typedef struct Fragment4        Fragment4;
13 typedef struct Fragment6        Fragment6;
14 typedef struct Ipfrag           Ipfrag;
15
16 enum
17 {
18         IP4HDR          = 20,           /* sizeof(Ip4hdr) */
19         IP6HDR          = 40,           /* sizeof(Ip6hdr) */
20         IP_HLEN4        = 0x05,         /* Header length in words */
21         IP_DF           = 0x4000,       /* Don't fragment */
22         IP_MF           = 0x2000,       /* More fragments */
23         IP6FHDR         = 8,            /* sizeof(Fraghdr6) */
24         IP_MAX          = 64*1024,      /* Maximum Internet packet size */
25 };
26
27 #define BLKIPVER(xp)    (((Ip4hdr*)((xp)->rp))->vihl&0xF0)
28
29 struct Ip4hdr
30 {
31         uchar   vihl;           /* Version and header length */
32         uchar   tos;            /* Type of service */
33         uchar   length[2];      /* packet length */
34         uchar   id[2];          /* ip->identification */
35         uchar   frag[2];        /* Fragment information */
36         uchar   ttl;            /* Time to live */
37         uchar   proto;          /* Protocol */
38         uchar   cksum[2];       /* Header checksum */
39         uchar   src[4];         /* IP source */
40         uchar   dst[4];         /* IP destination */
41 };
42
43 /* MIB II counters */
44 enum
45 {
46         Forwarding,
47         DefaultTTL,
48         InReceives,
49         InHdrErrors,
50         InAddrErrors,
51         ForwDatagrams,
52         InUnknownProtos,
53         InDiscards,
54         InDelivers,
55         OutRequests,
56         OutDiscards,
57         OutNoRoutes,
58         ReasmTimeout,
59         ReasmReqds,
60         ReasmOKs,
61         ReasmFails,
62         FragOKs,
63         FragFails,
64         FragCreates,
65
66         Nstats,
67 };
68
69 struct Fragment4
70 {
71         Block*  blist;
72         Fragment4*      next;
73         ulong   src;
74         ulong   dst;
75         ushort  id;
76         ulong   age;
77 };
78
79 struct Fragment6
80 {
81         Block*  blist;
82         Fragment6*      next;
83         uchar   src[IPaddrlen];
84         uchar   dst[IPaddrlen];
85         uint    id;
86         ulong   age;
87 };
88
89 struct Ipfrag
90 {
91         ushort  foff;
92         ushort  flen;
93 };
94
95 /* an instance of IP */
96 struct IP
97 {
98         ulong           stats[Nstats];
99
100         QLock           fraglock4;
101         Fragment4*      flisthead4;
102         Fragment4*      fragfree4;
103         Ref             id4;
104
105         QLock           fraglock6;
106         Fragment6*      flisthead6;
107         Fragment6*      fragfree6;
108         Ref             id6;
109
110         int             iprouting;      /* true if we route like a gateway */
111 };
112
113 static char *statnames[] =
114 {
115 [Forwarding]    "Forwarding",
116 [DefaultTTL]    "DefaultTTL",
117 [InReceives]    "InReceives",
118 [InHdrErrors]   "InHdrErrors",
119 [InAddrErrors]  "InAddrErrors",
120 [ForwDatagrams] "ForwDatagrams",
121 [InUnknownProtos]       "InUnknownProtos",
122 [InDiscards]    "InDiscards",
123 [InDelivers]    "InDelivers",
124 [OutRequests]   "OutRequests",
125 [OutDiscards]   "OutDiscards",
126 [OutNoRoutes]   "OutNoRoutes",
127 [ReasmTimeout]  "ReasmTimeout",
128 [ReasmReqds]    "ReasmReqds",
129 [ReasmOKs]      "ReasmOKs",
130 [ReasmFails]    "ReasmFails",
131 [FragOKs]       "FragOKs",
132 [FragFails]     "FragFails",
133 [FragCreates]   "FragCreates",
134 };
135
136 #define BLKIP(xp)       ((Ip4hdr*)((xp)->rp))
137 /*
138  * This sleazy macro relies on the media header size being
139  * larger than sizeof(Ipfrag). ipreassemble checks this is true
140  */
141 #define BKFG(xp)        ((Ipfrag*)((xp)->base))
142
143 ushort          ipcsum(uchar*);
144 Block*          ip4reassemble(IP*, int, Block*, Ip4hdr*);
145 void            ipfragfree4(IP*, Fragment4*);
146 Fragment4*      ipfragallo4(IP*);
147
148
149 void
150 ip_init_6(Fs *f)
151 {
152         V6params *v6p;
153
154         v6p = smalloc(sizeof(V6params));
155         
156         v6p->rp.mflag           = 0;            // default not managed
157         v6p->rp.oflag           = 0;
158         v6p->rp.maxraint        = 600000;       // millisecs
159         v6p->rp.minraint        = 200000;
160         v6p->rp.linkmtu         = 0;            // no mtu sent
161         v6p->rp.reachtime       = 0;
162         v6p->rp.rxmitra         = 0;
163         v6p->rp.ttl             = MAXTTL;
164         v6p->rp.routerlt        = 3*(v6p->rp.maxraint); 
165
166         v6p->hp.rxmithost       = 1000;         // v6 RETRANS_TIMER
167
168         v6p->cdrouter           = -1;
169
170         f->v6p                  = v6p;
171
172 }
173
174 void
175 initfrag(IP *ip, int size)
176 {
177         Fragment4 *fq4, *eq4;
178         Fragment6 *fq6, *eq6;
179
180         ip->fragfree4 = (Fragment4*)malloc(sizeof(Fragment4) * size);
181         if(ip->fragfree4 == nil)
182                 panic("initfrag");
183
184         eq4 = &ip->fragfree4[size];
185         for(fq4 = ip->fragfree4; fq4 < eq4; fq4++)
186                 fq4->next = fq4+1;
187
188         ip->fragfree4[size-1].next = nil;
189
190         ip->fragfree6 = (Fragment6*)malloc(sizeof(Fragment6) * size);
191         if(ip->fragfree6 == nil)
192                 panic("initfrag");
193
194         eq6 = &ip->fragfree6[size];
195         for(fq6 = ip->fragfree6; fq6 < eq6; fq6++)
196                 fq6->next = fq6+1;
197
198         ip->fragfree6[size-1].next = nil;
199 }
200
201 void
202 ip_init(Fs *f)
203 {
204         IP *ip;
205
206         ip = smalloc(sizeof(IP));
207         initfrag(ip, 100);
208         f->ip = ip;
209
210         ip_init_6(f);
211 }
212
213 void
214 iprouting(Fs *f, int on)
215 {
216         f->ip->iprouting = on;
217         if(f->ip->iprouting==0)
218                 f->ip->stats[Forwarding] = 2;
219         else
220                 f->ip->stats[Forwarding] = 1;   
221 }
222
223 int
224 ipoput4(Fs *f, Block *bp, int gating, int ttl, int tos, Conv *c)
225 {
226         Ipifc *ifc;
227         uchar *gate;
228         ulong fragoff;
229         Block *xp, *nb;
230         Ip4hdr *eh, *feh;
231         int lid, len, seglen, chunk, dlen, blklen, offset, medialen;
232         Route *r, *sr;
233         IP *ip;
234         int rv = 0;
235
236         ip = f->ip;
237
238         /* Fill out the ip header */
239         eh = (Ip4hdr*)(bp->rp);
240
241         ip->stats[OutRequests]++;
242
243         /* Number of uchars in data and ip header to write */
244         len = blocklen(bp);
245
246         if(gating){
247                 chunk = nhgets(eh->length);
248                 if(chunk > len){
249                         ip->stats[OutDiscards]++;
250                         netlog(f, Logip, "short gated packet\n");
251                         goto free;
252                 }
253                 if(chunk < len)
254                         len = chunk;
255         }
256         if(len >= IP_MAX){
257                 ip->stats[OutDiscards]++;
258                 netlog(f, Logip, "exceeded ip max size %V\n", eh->dst);
259                 goto free;
260         }
261
262         r = v4lookup(f, eh->dst, c);
263         if(r == nil){
264                 ip->stats[OutNoRoutes]++;
265                 netlog(f, Logip, "no interface %V\n", eh->dst);
266                 rv = -1;
267                 goto free;
268         }
269
270         ifc = r->ifc;
271         if(r->type & (Rifc|Runi))
272                 gate = eh->dst;
273         else
274         if(r->type & (Rbcast|Rmulti)) {
275                 gate = eh->dst;
276                 sr = v4lookup(f, eh->src, nil);
277                 if(sr != nil && (sr->type & Runi))
278                         ifc = sr->ifc;
279         }
280         else
281                 gate = r->v4.gate;
282
283         if(!gating)
284                 eh->vihl = IP_VER4|IP_HLEN4;
285         eh->ttl = ttl;
286         if(!gating)
287                 eh->tos = tos;
288
289         if(!canrlock(ifc))
290                 goto free;
291         if(waserror()){
292                 runlock(ifc);
293                 nexterror();
294         }
295         if(ifc->m == nil)
296                 goto raise;
297
298         /* If we dont need to fragment just send it */
299         medialen = ifc->maxtu - ifc->m->hsize;
300         if(len <= medialen) {
301                 if(!gating)
302                         hnputs(eh->id, incref(&ip->id4));
303                 hnputs(eh->length, len);
304                 if(!gating){
305                         eh->frag[0] = 0;
306                         eh->frag[1] = 0;
307                 }
308                 eh->cksum[0] = 0;
309                 eh->cksum[1] = 0;
310                 hnputs(eh->cksum, ipcsum(&eh->vihl));
311                 ifc->m->bwrite(ifc, bp, V4, gate);
312                 runlock(ifc);
313                 poperror();
314                 return 0;
315         }
316
317 if((eh->frag[0] & (IP_DF>>8)) && !gating) print("%V: DF set\n", eh->dst);
318
319         if(eh->frag[0] & (IP_DF>>8)){
320                 ip->stats[FragFails]++;
321                 ip->stats[OutDiscards]++;
322                 icmpcantfrag(f, bp, medialen);
323                 netlog(f, Logip, "%V: eh->frag[0] & (IP_DF>>8)\n", eh->dst);
324                 goto raise;
325         }
326
327         seglen = (medialen - IP4HDR) & ~7;
328         if(seglen < 8){
329                 ip->stats[FragFails]++;
330                 ip->stats[OutDiscards]++;
331                 netlog(f, Logip, "%V seglen < 8\n", eh->dst);
332                 goto raise;
333         }
334
335         dlen = len - IP4HDR;
336         xp = bp;
337         if(gating)
338                 lid = nhgets(eh->id);
339         else
340                 lid = incref(&ip->id4);
341
342         offset = IP4HDR;
343         while(xp != nil && offset && offset >= BLEN(xp)) {
344                 offset -= BLEN(xp);
345                 xp = xp->next;
346         }
347         xp->rp += offset;
348
349         if(gating)
350                 fragoff = nhgets(eh->frag)<<3;
351         else
352                 fragoff = 0;
353         dlen += fragoff;
354         for(; fragoff < dlen; fragoff += seglen) {
355                 nb = allocb(IP4HDR+seglen);
356                 feh = (Ip4hdr*)(nb->rp);
357
358                 memmove(nb->wp, eh, IP4HDR);
359                 nb->wp += IP4HDR;
360
361                 if((fragoff + seglen) >= dlen) {
362                         seglen = dlen - fragoff;
363                         hnputs(feh->frag, fragoff>>3);
364                 }
365                 else    
366                         hnputs(feh->frag, (fragoff>>3)|IP_MF);
367
368                 hnputs(feh->length, seglen + IP4HDR);
369                 hnputs(feh->id, lid);
370
371                 /* Copy up the data area */
372                 chunk = seglen;
373                 while(chunk) {
374                         if(!xp) {
375                                 ip->stats[OutDiscards]++;
376                                 ip->stats[FragFails]++;
377                                 freeblist(nb);
378                                 netlog(f, Logip, "!xp: chunk %d\n", chunk);
379                                 goto raise;
380                         }
381                         blklen = chunk;
382                         if(BLEN(xp) < chunk)
383                                 blklen = BLEN(xp);
384                         memmove(nb->wp, xp->rp, blklen);
385                         nb->wp += blklen;
386                         xp->rp += blklen;
387                         chunk -= blklen;
388                         if(xp->rp == xp->wp)
389                                 xp = xp->next;
390                 } 
391
392                 feh->cksum[0] = 0;
393                 feh->cksum[1] = 0;
394                 hnputs(feh->cksum, ipcsum(&feh->vihl));
395                 ifc->m->bwrite(ifc, nb, V4, gate);
396                 ip->stats[FragCreates]++;
397         }
398         ip->stats[FragOKs]++;
399 raise:
400         runlock(ifc);
401         poperror();
402 free:
403         freeblist(bp);
404         return rv;
405 }
406
407 void
408 ipiput4(Fs *f, Ipifc *ifc, Block *bp)
409 {
410         int hl;
411         int hop, tos, proto, olen;
412         Ip4hdr *h;
413         Proto *p;
414         ushort frag;
415         int notforme;
416         uchar *dp, v6dst[IPaddrlen];
417         IP *ip;
418         Route *r;
419
420         if(BLKIPVER(bp) != IP_VER4) {
421                 ipiput6(f, ifc, bp);
422                 return;
423         }
424
425         ip = f->ip;
426         ip->stats[InReceives]++;
427
428         /*
429          *  Ensure we have all the header info in the first
430          *  block.  Make life easier for other protocols by
431          *  collecting up to the first 64 bytes in the first block.
432          */
433         if(BLEN(bp) < 64) {
434                 hl = blocklen(bp);
435                 if(hl < IP4HDR)
436                         hl = IP4HDR;
437                 if(hl > 64)
438                         hl = 64;
439                 bp = pullupblock(bp, hl);
440                 if(bp == nil)
441                         return;
442         }
443
444         h = (Ip4hdr*)(bp->rp);
445
446         /* dump anything that whose header doesn't checksum */
447         if((bp->flag & Bipck) == 0 && ipcsum(&h->vihl)) {
448                 ip->stats[InHdrErrors]++;
449                 netlog(f, Logip, "ip: checksum error %V\n", h->src);
450                 freeblist(bp);
451                 return;
452         }
453         v4tov6(v6dst, h->dst);
454         notforme = ipforme(f, v6dst) == 0;
455
456         /* Check header length and version */
457         if((h->vihl&0x0F) != IP_HLEN4) {
458                 hl = (h->vihl&0xF)<<2;
459                 if(hl < (IP_HLEN4<<2)) {
460                         ip->stats[InHdrErrors]++;
461                         netlog(f, Logip, "ip: %V bad hivl %ux\n", h->src, h->vihl);
462                         freeblist(bp);
463                         return;
464                 }
465           /* If this is not routed strip off the options */
466                 if(notforme == 0) {
467                         olen = nhgets(h->length);
468                         dp = bp->rp + (hl - (IP_HLEN4<<2));
469                         memmove(dp, h, IP_HLEN4<<2);
470                         bp->rp = dp;
471                         h = (Ip4hdr*)(bp->rp);
472                         h->vihl = (IP_VER4|IP_HLEN4);
473                         hnputs(h->length, olen-hl+(IP_HLEN4<<2));
474                 }
475         }
476
477         /* route */
478         if(notforme) {
479                 Conv conv;
480
481                 if(!ip->iprouting){
482                         freeb(bp);
483                         return;
484                 }
485
486                 /* don't forward to source's network */
487                 conv.r = nil;
488                 r = v4lookup(f, h->dst, &conv);
489                 if(r == nil || r->ifc == ifc){
490                         ip->stats[OutDiscards]++;
491                         freeblist(bp);
492                         return;
493                 }
494
495                 /* don't forward if packet has timed out */
496                 hop = h->ttl;
497                 if(hop < 1) {
498                         ip->stats[InHdrErrors]++;
499                         icmpttlexceeded(f, ifc->lifc->local, bp);
500                         freeblist(bp);
501                         return;
502                 }
503
504                 /* reassemble if the interface expects it */
505 if(r->ifc == nil) panic("nil route rfc");
506                 if(r->ifc->reassemble){
507                         frag = nhgets(h->frag);
508                         if(frag) {
509                                 h->tos = 0;
510                                 if(frag & IP_MF)
511                                         h->tos = 1;
512                                 bp = ip4reassemble(ip, frag, bp, h);
513                                 if(bp == nil)
514                                         return;
515                                 h = (Ip4hdr*)(bp->rp);
516                         }
517                 }
518
519                 ip->stats[ForwDatagrams]++;
520                 tos = h->tos;
521                 hop = h->ttl;
522                 ipoput4(f, bp, 1, hop - 1, tos, &conv);
523                 return;
524         }
525
526         frag = nhgets(h->frag);
527         if(frag) {
528                 h->tos = 0;
529                 if(frag & IP_MF)
530                         h->tos = 1;
531                 bp = ip4reassemble(ip, frag, bp, h);
532                 if(bp == nil)
533                         return;
534                 h = (Ip4hdr*)(bp->rp);
535         }
536
537         /* don't let any frag info go up the stack */
538         h->frag[0] = 0;
539         h->frag[1] = 0;
540
541         proto = h->proto;
542         p = Fsrcvpcol(f, proto);
543         if(p != nil && p->rcv != nil) {
544                 ip->stats[InDelivers]++;
545                 (*p->rcv)(p, ifc, bp);
546                 return;
547         }
548         ip->stats[InDiscards]++;
549         ip->stats[InUnknownProtos]++;
550         freeblist(bp);
551 }
552
553 int
554 ipstats(Fs *f, char *buf, int len)
555 {
556         IP *ip;
557         char *p, *e;
558         int i;
559
560         ip = f->ip;
561         ip->stats[DefaultTTL] = MAXTTL;
562
563         p = buf;
564         e = p+len;
565         for(i = 0; i < Nstats; i++)
566                 p = seprint(p, e, "%s: %lud\n", statnames[i], ip->stats[i]);
567         return p - buf;
568 }
569
570 Block*
571 ip4reassemble(IP *ip, int offset, Block *bp, Ip4hdr *ih)
572 {
573         int fend;
574         ushort id;
575         Fragment4 *f, *fnext;
576         ulong src, dst;
577         Block *bl, **l, *last, *prev;
578         int ovlap, len, fragsize, pktposn;
579
580         src = nhgetl(ih->src);
581         dst = nhgetl(ih->dst);
582         id = nhgets(ih->id);
583
584         /*
585          *  block lists are too hard, pullupblock into a single block
586          */
587         if(bp->next){
588                 bp = pullupblock(bp, blocklen(bp));
589                 ih = (Ip4hdr*)(bp->rp);
590         }
591
592         qlock(&ip->fraglock4);
593
594         /*
595          *  find a reassembly queue for this fragment
596          */
597         for(f = ip->flisthead4; f; f = fnext){
598                 fnext = f->next;        /* because ipfragfree4 changes the list */
599                 if(f->src == src && f->dst == dst && f->id == id)
600                         break;
601                 if(f->age < NOW){
602                         ip->stats[ReasmTimeout]++;
603                         ipfragfree4(ip, f);
604                 }
605         }
606
607         /*
608          *  if this isn't a fragmented packet, accept it
609          *  and get rid of any fragments that might go
610          *  with it.
611          */
612         if(!ih->tos && (offset & ~(IP_MF|IP_DF)) == 0) {
613                 if(f != nil) {
614                         ipfragfree4(ip, f);
615                         ip->stats[ReasmFails]++;
616                 }
617                 qunlock(&ip->fraglock4);
618                 return bp;
619         }
620
621         if(bp->base+sizeof(Ipfrag) >= bp->rp){
622                 bp = padblock(bp, sizeof(Ipfrag));
623                 bp->rp += sizeof(Ipfrag);
624         }
625
626         BKFG(bp)->foff = offset<<3;
627         BKFG(bp)->flen = nhgets(ih->length)-IP4HDR;
628
629         /* First fragment allocates a reassembly queue */
630         if(f == nil) {
631                 f = ipfragallo4(ip);
632                 f->id = id;
633                 f->src = src;
634                 f->dst = dst;
635
636                 f->blist = bp;
637
638                 qunlock(&ip->fraglock4);
639                 ip->stats[ReasmReqds]++;
640                 return nil;
641         }
642
643         /*
644          *  find the new fragment's position in the queue
645          */
646         prev = nil;
647         l = &f->blist;
648         bl = f->blist;
649         while(bl != nil && BKFG(bp)->foff > BKFG(bl)->foff) {
650                 prev = bl;
651                 l = &bl->next;
652                 bl = bl->next;
653         }
654
655         /* Check overlap of a previous fragment - trim away as necessary */
656         if(prev) {
657                 ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
658                 if(ovlap > 0) {
659                         if(ovlap >= BKFG(bp)->flen) {
660                                 freeblist(bp);
661                                 qunlock(&ip->fraglock4);
662                                 return nil;
663                         }
664                         BKFG(prev)->flen -= ovlap;
665                 }
666         }
667
668         /* Link onto assembly queue */
669         bp->next = *l;
670         *l = bp;
671
672         /* Check to see if succeeding segments overlap */
673         if(bp->next) {
674                 l = &bp->next;
675                 fend = BKFG(bp)->foff + BKFG(bp)->flen;
676                 /* Take completely covered segments out */
677                 while(*l) {
678                         ovlap = fend - BKFG(*l)->foff;
679                         if(ovlap <= 0)
680                                 break;
681                         if(ovlap < BKFG(*l)->flen) {
682                                 BKFG(*l)->flen -= ovlap;
683                                 BKFG(*l)->foff += ovlap;
684                                 /* move up ih hdrs */
685                                 memmove((*l)->rp + ovlap, (*l)->rp, IP4HDR);
686                                 (*l)->rp += ovlap;
687                                 break;
688                         }
689                         last = (*l)->next;
690                         (*l)->next = nil;
691                         freeblist(*l);
692                         *l = last;
693                 }
694         }
695
696         /*
697          *  look for a complete packet.  if we get to a fragment
698          *  without IP_MF set, we're done.
699          */
700         pktposn = 0;
701         for(bl = f->blist; bl; bl = bl->next) {
702                 if(BKFG(bl)->foff != pktposn)
703                         break;
704                 if((BLKIP(bl)->frag[0]&(IP_MF>>8)) == 0) {
705                         bl = f->blist;
706                         len = nhgets(BLKIP(bl)->length);
707                         bl->wp = bl->rp + len;
708
709                         /* Pullup all the fragment headers and
710                          * return a complete packet
711                          */
712                         for(bl = bl->next; bl; bl = bl->next) {
713                                 fragsize = BKFG(bl)->flen;
714                                 len += fragsize;
715                                 bl->rp += IP4HDR;
716                                 bl->wp = bl->rp + fragsize;
717                         }
718
719                         bl = f->blist;
720                         f->blist = nil;
721                         ipfragfree4(ip, f);
722                         ih = BLKIP(bl);
723                         hnputs(ih->length, len);
724                         qunlock(&ip->fraglock4);
725                         ip->stats[ReasmOKs]++;
726                         return bl;              
727                 }
728                 pktposn += BKFG(bl)->flen;
729         }
730         qunlock(&ip->fraglock4);
731         return nil;
732 }
733
734 /*
735  * ipfragfree4 - Free a list of fragments - assume hold fraglock4
736  */
737 void
738 ipfragfree4(IP *ip, Fragment4 *frag)
739 {
740         Fragment4 *fl, **l;
741
742         if(frag->blist)
743                 freeblist(frag->blist);
744
745         frag->src = 0;
746         frag->id = 0;
747         frag->blist = nil;
748
749         l = &ip->flisthead4;
750         for(fl = *l; fl; fl = fl->next) {
751                 if(fl == frag) {
752                         *l = frag->next;
753                         break;
754                 }
755                 l = &fl->next;
756         }
757
758         frag->next = ip->fragfree4;
759         ip->fragfree4 = frag;
760
761 }
762
763 /*
764  * ipfragallo4 - allocate a reassembly queue - assume hold fraglock4
765  */
766 Fragment4 *
767 ipfragallo4(IP *ip)
768 {
769         Fragment4 *f;
770
771         while(ip->fragfree4 == nil) {
772                 /* free last entry on fraglist */
773                 for(f = ip->flisthead4; f->next; f = f->next)
774                         ;
775                 ipfragfree4(ip, f);
776         }
777         f = ip->fragfree4;
778         ip->fragfree4 = f->next;
779         f->next = ip->flisthead4;
780         ip->flisthead4 = f;
781         f->age = NOW + 30000;
782
783         return f;
784 }
785
786 ushort
787 ipcsum(uchar *addr)
788 {
789         int len;
790         ulong sum;
791
792         sum = 0;
793         len = (addr[0]&0xf)<<2;
794
795         while(len > 0) {
796                 sum += addr[0]<<8 | addr[1] ;
797                 len -= 2;
798                 addr += 2;
799         }
800
801         sum = (sum & 0xffff) + (sum >> 16);
802         sum = (sum & 0xffff) + (sum >> 16);
803
804         return (sum^0xffff);
805 }