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