Remove "early routine kmsg" context
[akaros.git] / kern / src / net / ipv6.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-2016 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
30 #include <error.h>
31 #include <net/ip.h>
32 #include <kmalloc.h>
33
34 enum {
35         IP4HDR = 20,                            /* sizeof(Ip4hdr) */
36         IP6HDR = 40,    /* sizeof(Ip6hdr) */
37         IP_HLEN4 = 0x05,        /* Header length in words */
38         IP_DF = 0x4000, /* Don't fragment */
39         IP_MF = 0x2000, /* More fragments */
40         IP6FHDR = 8,    /* sizeof(Fraghdr6) */
41         IP_MAX = (32 * 1024),   /* Maximum Internet packet size */
42 };
43
44 #define IPV6CLASS(hdr) ((hdr->vcf[0]&0x0F)<<2 | (hdr->vcf[1]&0xF0)>>2)
45 #define BLKIPVER(xp)    (((struct ip6hdr*)((xp)->rp))->vcf[0]&0xF0)
46 #define NEXT_ID(x) (__sync_add_and_fetch(&(x), 1))
47 /*
48  * This sleazy macro is stolen shamelessly from ip.c, see comment there.
49  */
50 #define BKFG(xp)        ((struct Ipfrag*)((xp)->base))
51 struct fragment6;
52
53 struct block *ip6reassemble(struct IP *, int unused_int, struct block *,
54                             struct ip6hdr *);
55 void ipfragfree6(struct IP *, struct fragment6 *);
56 struct fragment6 *ipfragallo6(struct IP *);
57 static struct block *procxtns(struct IP *ip, struct block *bp, int doreasm);
58 int unfraglen(struct block *bp, uint8_t * nexthdr, int setfh);
59 struct block *procopts(struct block *bp);
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 static char *statnames[] = {
87         [Forwarding] "Forwarding",
88         [DefaultTTL] "DefaultTTL",
89         [InReceives] "InReceives",
90         [InHdrErrors] "InHdrErrors",
91         [InAddrErrors] "InAddrErrors",
92         [ForwDatagrams] "ForwDatagrams",
93         [InUnknownProtos] "InUnknownProtos",
94         [InDiscards] "InDiscards",
95         [InDelivers] "InDelivers",
96         [OutRequests] "OutRequests",
97         [OutDiscards] "OutDiscards",
98         [OutNoRoutes] "OutNoRoutes",
99         [ReasmTimeout] "ReasmTimeout",
100         [ReasmReqds] "ReasmReqds",
101         [ReasmOKs] "ReasmOKs",
102         [ReasmFails] "ReasmFails",
103         [FragOKs] "FragOKs",
104         [FragFails] "FragFails",
105         [FragCreates] "FragCreates",
106 };
107
108 struct Fragment4 {
109         struct block *blist;
110         struct fragment4 *next;
111         uint32_t src;
112         uint32_t dst;
113         uint16_t id;
114         uint64_t age;
115 };
116
117 struct fragment6 {
118         struct block *blist;
119         struct fragment6 *next;
120         uint8_t src[IPaddrlen];
121         uint8_t dst[IPaddrlen];
122         unsigned int id;
123         uint64_t age;
124 };
125
126 struct Ipfrag {
127         uint16_t foff;
128         uint16_t flen;
129 };
130
131 /* an instance of IP */
132 struct IP {
133         uint32_t stats[Nstats];
134
135         qlock_t fraglock4;
136         struct fragment4 *flisthead4;
137         struct fragment4 *fragfree4;
138         int id4;
139
140         qlock_t fraglock6;
141         struct fragment6 *flisthead6;
142         struct fragment6 *fragfree6;
143         int id6;
144
145         int iprouting;                          /* true if we route like a gateway */
146 };
147
148 int ipoput6(struct Fs *f, struct block *bp,
149             int gating, int ttl, int tos, struct conv *c)
150 {
151         ERRSTACK(1);
152         int tentative;
153         struct Ipifc *ifc;
154         uint8_t *gate, nexthdr;
155         struct ip6hdr *eh;
156         int medialen, len, chunk, uflen, flen, seglen, lid, offset, fragoff;
157         int morefrags, blklen;
158         struct route *r, *sr;
159         struct fraghdr6 fraghdr;
160         struct block *xp, *nb;
161         struct IP *ip;
162         int rv = 0;
163
164         ip = f->ip;
165
166         /* Sanity check for our transport protocols. */
167         if (bp->mss)
168                 assert(bp->flag & Btso);
169         /* Fill out the ip header */
170         eh = (struct ip6hdr *)(bp->rp);
171
172         ip->stats[OutRequests]++;
173
174         /* Number of uint8_ts in data and ip header to write */
175         len = blocklen(bp);
176
177         tentative = iptentative(f, eh->src);
178         if (tentative) {
179                 netlog(f, Logip, "reject tx of packet with tentative src address\n");
180                 goto free;
181         }
182
183         if (gating) {
184                 chunk = nhgets(eh->ploadlen);
185                 if (chunk > len) {
186                         ip->stats[OutDiscards]++;
187                         netlog(f, Logip, "short gated packet\n");
188                         goto free;
189                 }
190                 if (chunk + IPV6HDR_LEN < len)
191                         len = chunk + IPV6HDR_LEN;
192         }
193
194         if (len >= IP_MAX) {
195                 ip->stats[OutDiscards]++;
196                 netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
197                 goto free;
198         }
199
200         r = v6lookup(f, eh->dst, c);
201         if (r == NULL) {
202                 ip->stats[OutNoRoutes]++;
203                 netlog(f, Logip, "no interface %I\n", eh->dst);
204                 rv = -1;
205                 goto free;
206         }
207
208         ifc = r->rt.ifc;
209         if (r->rt.type & (Rifc | Runi))
210                 gate = eh->dst;
211         else if (r->rt.type & (Rbcast | Rmulti)) {
212                 gate = eh->dst;
213                 sr = v6lookup(f, eh->src, NULL);
214                 if (sr != NULL && (sr->rt.type & Runi))
215                         ifc = sr->rt.ifc;
216         } else
217                 gate = r->v6.gate;
218
219         if (!gating)
220                 eh->vcf[0] = IP_VER6;
221         eh->ttl = ttl;
222         if (!gating) {
223                 eh->vcf[0] |= (tos >> 4);
224                 eh->vcf[1] = (tos << 4);
225         }
226
227         if (!canrlock(&ifc->rwlock)) {
228                 goto free;
229         }
230
231         if (waserror()) {
232                 runlock(&ifc->rwlock);
233                 nexterror();
234         }
235
236         if (ifc->m == NULL) {
237                 goto raise;
238         }
239
240         /* If we dont need to fragment just send it */
241         medialen = ifc->maxtu - ifc->m->hsize;
242         if (len <= medialen) {
243                 hnputs(eh->ploadlen, len - IPV6HDR_LEN);
244                 ifc->m->bwrite(ifc, bp, V6, gate);
245                 runlock(&ifc->rwlock);
246                 poperror();
247                 return 0;
248         }
249
250         if (gating)
251                 if (ifc->reassemble <= 0) {
252                         /*
253                          * v6 intermediate nodes are not supposed to fragment pkts;
254                          * we fragment if ifc->reassemble is turned on; an exception
255                          * needed for nat.
256                          */
257                         ip->stats[OutDiscards]++;
258                         icmppkttoobig6(f, ifc, bp);
259                         netlog(f, Logip, "%I: gated pkts not fragmented\n", eh->dst);
260                         goto raise;
261                 }
262
263         /* start v6 fragmentation */
264         uflen = unfraglen(bp, &nexthdr, 1);
265         if (uflen > medialen) {
266                 ip->stats[FragFails]++;
267                 ip->stats[OutDiscards]++;
268                 netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
269                 goto raise;
270         }
271
272         flen = len - uflen;
273         seglen = (medialen - (uflen + IP6FHDR)) & ~7;
274         if (seglen < 8) {
275                 ip->stats[FragFails]++;
276                 ip->stats[OutDiscards]++;
277                 netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
278                 goto raise;
279         }
280
281         lid = NEXT_ID(ip->id6);
282         fraghdr.nexthdr = nexthdr;
283         fraghdr.res = 0;
284         hnputl(fraghdr.id, lid);
285
286         xp = bp;
287         offset = uflen;
288         while (xp != NULL && offset && offset >= BLEN(xp)) {
289                 offset -= BLEN(xp);
290                 xp = xp->next;
291         }
292         xp->rp += offset;
293
294         fragoff = 0;
295         morefrags = 1;
296
297         for (; fragoff < flen; fragoff += seglen) {
298                 nb = block_alloc(uflen + IP6FHDR + seglen, MEM_WAIT);
299
300                 if (fragoff + seglen >= flen) {
301                         seglen = flen - fragoff;
302                         morefrags = 0;
303                 }
304
305                 hnputs(eh->ploadlen, seglen + IP6FHDR);
306                 memmove(nb->wp, eh, uflen);
307                 nb->wp += uflen;
308
309                 hnputs(fraghdr.offsetRM, fragoff);      // last 3 bits must be 0
310                 fraghdr.offsetRM[1] |= morefrags;
311                 memmove(nb->wp, &fraghdr, IP6FHDR);
312                 nb->wp += IP6FHDR;
313
314                 /* Copy data */
315                 chunk = seglen;
316                 while (chunk) {
317                         if (!xp) {
318                                 ip->stats[OutDiscards]++;
319                                 ip->stats[FragFails]++;
320                                 freeblist(nb);
321                                 netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
322                                 goto raise;
323                         }
324                         blklen = chunk;
325                         if (BLEN(xp) < chunk)
326                                 blklen = BLEN(xp);
327                         memmove(nb->wp, xp->rp, blklen);
328
329                         nb->wp += blklen;
330                         xp->rp += blklen;
331                         chunk -= blklen;
332                         if (xp->rp == xp->wp)
333                                 xp = xp->next;
334                 }
335
336                 ifc->m->bwrite(ifc, nb, V6, gate);
337                 ip->stats[FragCreates]++;
338         }
339         ip->stats[FragOKs]++;
340
341 raise:
342         runlock(&ifc->rwlock);
343         poperror();
344 free:
345         freeblist(bp);
346         return rv;
347 }
348
349 void ipiput6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
350 {
351         int hl;
352         int hop, tos;
353         uint8_t proto;
354         struct ip6hdr *h;
355         struct Proto *p;
356         int notforme;
357         int tentative;
358         uint8_t v6dst[IPaddrlen];
359         struct IP *ip;
360         struct route *r, *sr;
361
362         ip = f->ip;
363         ip->stats[InReceives]++;
364
365         /*
366          *  Ensure we have all the header info in the first
367          *  block.  Make life easier for other protocols by
368          *  collecting up to the first 64 bytes in the first block.
369          */
370         if (BLEN(bp) < 64) {
371                 hl = blocklen(bp);
372                 if (hl < IP6HDR)
373                         hl = IP6HDR;
374                 if (hl > 64)
375                         hl = 64;
376                 bp = pullupblock(bp, hl);
377                 if (bp == NULL)
378                         return;
379         }
380
381         h = (struct ip6hdr *)(bp->rp);
382
383         memmove(&v6dst[0], &(h->dst)[0], IPaddrlen);
384         notforme = ipforme(f, v6dst) == 0;
385         tentative = iptentative(f, v6dst);
386
387         if (tentative && (h->proto != ICMPv6)) {
388                 printd("tentative addr, drop\n");
389                 freeblist(bp);
390                 return;
391         }
392
393         /* Check header version */
394         if (BLKIPVER(bp) != IP_VER6) {
395                 ip->stats[InHdrErrors]++;
396                 netlog(f, Logip, "ip: bad version 0x%x\n", (h->vcf[0] & 0xF0) >> 2);
397                 freeblist(bp);
398                 return;
399         }
400
401         /* route */
402         if (notforme) {
403                 if (!ip->iprouting) {
404                         freeb(bp);
405                         return;
406                 }
407                 /* don't forward to source's network */
408                 sr = v6lookup(f, h->src, NULL);
409                 r = v6lookup(f, h->dst, NULL);
410
411                 if (r == NULL || sr == r) {
412                         ip->stats[OutDiscards]++;
413                         freeblist(bp);
414                         return;
415                 }
416
417                 /* don't forward if packet has timed out */
418                 hop = h->ttl;
419                 if (hop < 1) {
420                         ip->stats[InHdrErrors]++;
421                         icmpttlexceeded6(f, ifc, bp);
422                         freeblist(bp);
423                         return;
424                 }
425
426                 /* process headers & reassemble if the interface expects it */
427                 bp = procxtns(ip, bp, r->rt.ifc->reassemble);
428
429                 if (bp == NULL)
430                         return;
431
432                 ip->stats[ForwDatagrams]++;
433                 h = (struct ip6hdr *)(bp->rp);
434                 tos = IPV6CLASS(h);
435                 hop = h->ttl;
436                 ipoput6(f, bp, 1, hop - 1, tos, NULL);
437                 return;
438         }
439
440         /* reassemble & process headers if needed */
441         bp = procxtns(ip, bp, 1);
442
443         if (bp == NULL)
444                 return;
445
446         h = (struct ip6hdr *)(bp->rp);
447         proto = h->proto;
448         p = Fsrcvpcol(f, proto);
449         if (p != NULL && p->rcv != NULL) {
450                 ip->stats[InDelivers]++;
451                 (*p->rcv) (p, ifc, bp);
452                 return;
453         }
454
455         ip->stats[InDiscards]++;
456         ip->stats[InUnknownProtos]++;
457         freeblist(bp);
458 }
459
460 /*
461  * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
462  */
463 void ipfragfree6(struct IP *ip, struct fragment6 *frag)
464 {
465         struct fragment6 *fl, **l;
466
467         if (frag->blist)
468                 freeblist(frag->blist);
469
470         memset(frag->src, 0, IPaddrlen);
471         frag->id = 0;
472         frag->blist = NULL;
473
474         l = &ip->flisthead6;
475         for (fl = *l; fl; fl = fl->next) {
476                 if (fl == frag) {
477                         *l = frag->next;
478                         break;
479                 }
480                 l = &fl->next;
481         }
482
483         frag->next = ip->fragfree6;
484         ip->fragfree6 = frag;
485
486 }
487
488 /*
489  * ipfragallo6 - copied from ipfragalloc4
490  */
491 struct fragment6 *ipfragallo6(struct IP *ip)
492 {
493         struct fragment6 *f;
494
495         while (ip->fragfree6 == NULL) {
496                 /* free last entry on fraglist */
497                 for (f = ip->flisthead6; f->next; f = f->next) ;
498                 ipfragfree6(ip, f);
499         }
500         f = ip->fragfree6;
501         ip->fragfree6 = f->next;
502         f->next = ip->flisthead6;
503         ip->flisthead6 = f;
504         f->age = NOW + 30000;
505
506         return f;
507 }
508
509 static struct block *procxtns(struct IP *ip, struct block *bp, int doreasm)
510 {
511
512         int offset;
513         uint8_t proto;
514         struct ip6hdr *h;
515
516         h = (struct ip6hdr *)(bp->rp);
517         offset = unfraglen(bp, &proto, 0);
518
519         if ((proto == FH) && (doreasm != 0)) {
520                 bp = ip6reassemble(ip, offset, bp, h);
521                 if (bp == NULL)
522                         return NULL;
523                 offset = unfraglen(bp, &proto, 0);
524         }
525
526         if (proto == DOH || offset > IP6HDR)
527                 bp = procopts(bp);
528
529         return bp;
530 }
531
532 /*      returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr,
533  *      hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value
534  *      of the last header in the "Unfragmentable part"; if setfh != 0, nexthdr
535  *      field of the last header in the "Unfragmentable part" is set to FH.
536  */
537 int unfraglen(struct block *bp, uint8_t * nexthdr, int setfh)
538 {
539         uint8_t *p, *q;
540         int ufl, hs;
541
542         p = bp->rp;
543         q = p + 6;      /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
544         *nexthdr = *q;
545         ufl = IP6HDR;
546         p += ufl;
547
548         for (;;) {
549                 if (*nexthdr == HBH || *nexthdr == RH) {
550                         *nexthdr = *p;
551                         hs = ((int)*(p + 1) + 1) * 8;
552                         ufl += hs;
553                         q = p;
554                         p += hs;
555                 } else
556                         break;
557         }
558
559         if (*nexthdr == FH)
560                 *q = *p;
561
562         if (setfh)
563                 *q = FH;
564
565         return ufl;
566 }
567
568 struct block *procopts(struct block *bp)
569 {
570         return bp;
571 }
572
573 struct block *ip6reassemble(struct IP *ip, int uflen, struct block *bp,
574                             struct ip6hdr *ih)
575 {
576
577         int fend, offset;
578         unsigned int id;
579         struct fragment6 *f, *fnext;
580         struct fraghdr6 *fraghdr;
581         uint8_t src[IPaddrlen], dst[IPaddrlen];
582         struct block *bl, **l, *last, *prev;
583         int ovlap, len, fragsize, pktposn;
584
585         fraghdr = (struct fraghdr6 *)(bp->rp + uflen);
586         memmove(src, ih->src, IPaddrlen);
587         memmove(dst, ih->dst, IPaddrlen);
588         id = nhgetl(fraghdr->id);
589         offset = nhgets(fraghdr->offsetRM) & ~7;
590
591         /*
592          *  block lists are too hard, pullupblock into a single block
593          */
594         if (bp->next) {
595                 bp = pullupblock(bp, blocklen(bp));
596                 ih = (struct ip6hdr *)(bp->rp);
597         }
598
599         qlock(&ip->fraglock6);
600
601         /*
602          *  find a reassembly queue for this fragment
603          */
604         for (f = ip->flisthead6; f; f = fnext) {
605                 fnext = f->next;
606                 if (ipcmp(f->src, src) == 0 && ipcmp(f->dst, dst) == 0 && f->id == id)
607                         break;
608                 if (f->age < NOW) {
609                         ip->stats[ReasmTimeout]++;
610                         ipfragfree6(ip, f);
611                 }
612         }
613
614         /*
615          *  if this isn't a fragmented packet, accept it
616          *  and get rid of any fragments that might go
617          *  with it.
618          */
619         if (nhgets(fraghdr->offsetRM) == 0) {   // first frag is also the last
620                 if (f != NULL) {
621                         ipfragfree6(ip, f);
622                         ip->stats[ReasmFails]++;
623                 }
624                 qunlock(&ip->fraglock6);
625                 return bp;
626         }
627
628         if (bp->base + sizeof(struct Ipfrag) >= bp->rp) {
629                 bp = padblock(bp, sizeof(struct Ipfrag));
630                 bp->rp += sizeof(struct Ipfrag);
631         }
632
633         BKFG(bp)->foff = offset;
634         BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
635
636         /* First fragment allocates a reassembly queue */
637         if (f == NULL) {
638                 f = ipfragallo6(ip);
639                 f->id = id;
640                 memmove(f->src, src, IPaddrlen);
641                 memmove(f->dst, dst, IPaddrlen);
642
643                 f->blist = bp;
644
645                 qunlock(&ip->fraglock6);
646                 ip->stats[ReasmReqds]++;
647                 return NULL;
648         }
649
650         /*
651          *  find the new fragment's position in the queue
652          */
653         prev = NULL;
654         l = &f->blist;
655         bl = f->blist;
656         while (bl != NULL && BKFG(bp)->foff > BKFG(bl)->foff) {
657                 prev = bl;
658                 l = &bl->next;
659                 bl = bl->next;
660         }
661
662         /* Check overlap of a previous fragment - trim away as necessary */
663         if (prev) {
664                 ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
665                 if (ovlap > 0) {
666                         if (ovlap >= BKFG(bp)->flen) {
667                                 freeblist(bp);
668                                 qunlock(&ip->fraglock6);
669                                 return NULL;
670                         }
671                         BKFG(prev)->flen -= ovlap;
672                 }
673         }
674
675         /* Link onto assembly queue */
676         bp->next = *l;
677         *l = bp;
678
679         /* Check to see if succeeding segments overlap */
680         if (bp->next) {
681                 l = &bp->next;
682                 fend = BKFG(bp)->foff + BKFG(bp)->flen;
683
684                 /* Take completely covered segments out */
685
686                 while (*l) {
687                         ovlap = fend - BKFG(*l)->foff;
688
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, uflen);
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          *  with the trailing bit of fraghdr->offsetRM[1] 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
715                 fraghdr = (struct fraghdr6 *)(bl->rp + uflen);
716                 if ((fraghdr->offsetRM[1] & 1) == 0) {
717                         bl = f->blist;
718
719                         /* get rid of frag header in first fragment */
720
721                         memmove(bl->rp + IP6FHDR, bl->rp, uflen);
722                         bl->rp += IP6FHDR;
723                         len = nhgets(((struct ip6hdr *)(bl->rp))->ploadlen) - IP6FHDR;
724                         bl->wp = bl->rp + len + IP6HDR;
725
726                         /* Pullup all the fragment headers and
727                          * return a complete packet
728                          */
729                         for (bl = bl->next; bl; bl = bl->next) {
730                                 fragsize = BKFG(bl)->flen;
731                                 len += fragsize;
732                                 bl->rp += uflen + IP6FHDR;
733                                 bl->wp = bl->rp + fragsize;
734                         }
735
736                         bl = f->blist;
737                         f->blist = NULL;
738                         ipfragfree6(ip, f);
739                         ih = (struct ip6hdr *)(bl->rp);
740                         hnputs(ih->ploadlen, len);
741                         qunlock(&ip->fraglock6);
742                         ip->stats[ReasmOKs]++;
743                         return bl;
744                 }
745                 pktposn += BKFG(bl)->flen;
746         }
747         qunlock(&ip->fraglock6);
748
749         return NULL;
750 }