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