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