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