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