kconfig: use pkg-config for ncurses detection
[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,
180                        "reject tx of packet with tentative src address\n");
181                 goto free;
182         }
183
184         if (gating) {
185                 chunk = nhgets(eh->ploadlen);
186                 if (chunk > len) {
187                         ip->stats[OutDiscards]++;
188                         netlog(f, Logip, "short gated packet\n");
189                         goto free;
190                 }
191                 if (chunk + IPV6HDR_LEN < len)
192                         len = chunk + IPV6HDR_LEN;
193         }
194
195         if (len >= IP_MAX) {
196                 ip->stats[OutDiscards]++;
197                 netlog(f, Logip, "exceeded ip max size %I\n", eh->dst);
198                 goto free;
199         }
200
201         r = v6lookup(f, eh->dst, c);
202         if (r == NULL) {
203                 ip->stats[OutNoRoutes]++;
204                 netlog(f, Logip, "no interface %I\n", eh->dst);
205                 rv = -1;
206                 goto free;
207         }
208
209         ifc = r->rt.ifc;
210         if (r->rt.type & (Rifc | Runi))
211                 gate = eh->dst;
212         else if (r->rt.type & (Rbcast | Rmulti)) {
213                 gate = eh->dst;
214                 sr = v6lookup(f, eh->src, NULL);
215                 if (sr != NULL && (sr->rt.type & Runi))
216                         ifc = sr->rt.ifc;
217         } else
218                 gate = r->v6.gate;
219
220         if (!gating)
221                 eh->vcf[0] = IP_VER6;
222         eh->ttl = ttl;
223         if (!gating) {
224                 eh->vcf[0] |= (tos >> 4);
225                 eh->vcf[1] = (tos << 4);
226         }
227
228         if (!canrlock(&ifc->rwlock)) {
229                 goto free;
230         }
231
232         if (waserror()) {
233                 runlock(&ifc->rwlock);
234                 nexterror();
235         }
236
237         if (ifc->m == NULL) {
238                 goto raise;
239         }
240
241         /* If we dont need to fragment just send it */
242         medialen = ifc->maxtu - ifc->m->hsize;
243         if (len <= medialen) {
244                 hnputs(eh->ploadlen, len - IPV6HDR_LEN);
245                 ifc->m->bwrite(ifc, bp, V6, gate);
246                 runlock(&ifc->rwlock);
247                 poperror();
248                 return 0;
249         }
250
251         if (gating)
252                 if (ifc->reassemble <= 0) {
253                         /*
254                          * v6 intermediate nodes are not supposed to fragment
255                          * pkts; we fragment if ifc->reassemble is turned on; an
256                          * exception needed for nat.
257                          */
258                         ip->stats[OutDiscards]++;
259                         icmppkttoobig6(f, ifc, bp);
260                         netlog(f, Logip, "%I: gated pkts not fragmented\n",
261                                eh->dst);
262                         goto raise;
263                 }
264
265         /* start v6 fragmentation */
266         uflen = unfraglen(bp, &nexthdr, 1);
267         if (uflen > medialen) {
268                 ip->stats[FragFails]++;
269                 ip->stats[OutDiscards]++;
270                 netlog(f, Logip, "%I: unfragmentable part too big\n", eh->dst);
271                 goto raise;
272         }
273
274         flen = len - uflen;
275         seglen = (medialen - (uflen + IP6FHDR)) & ~7;
276         if (seglen < 8) {
277                 ip->stats[FragFails]++;
278                 ip->stats[OutDiscards]++;
279                 netlog(f, Logip, "%I: seglen < 8\n", eh->dst);
280                 goto raise;
281         }
282
283         lid = NEXT_ID(ip->id6);
284         fraghdr.nexthdr = nexthdr;
285         fraghdr.res = 0;
286         hnputl(fraghdr.id, lid);
287
288         xp = bp;
289         offset = uflen;
290         while (xp != NULL && offset && offset >= BLEN(xp)) {
291                 offset -= BLEN(xp);
292                 xp = xp->next;
293         }
294         xp->rp += offset;
295
296         fragoff = 0;
297         morefrags = 1;
298
299         for (; fragoff < flen; fragoff += seglen) {
300                 nb = block_alloc(uflen + IP6FHDR + seglen, MEM_WAIT);
301
302                 if (fragoff + seglen >= flen) {
303                         seglen = flen - fragoff;
304                         morefrags = 0;
305                 }
306
307                 hnputs(eh->ploadlen, seglen + IP6FHDR);
308                 memmove(nb->wp, eh, uflen);
309                 nb->wp += uflen;
310
311                 hnputs(fraghdr.offsetRM, fragoff);      // last 3 bits must be 0
312                 fraghdr.offsetRM[1] |= morefrags;
313                 memmove(nb->wp, &fraghdr, IP6FHDR);
314                 nb->wp += IP6FHDR;
315
316                 /* Copy data */
317                 chunk = seglen;
318                 while (chunk) {
319                         if (!xp) {
320                                 ip->stats[OutDiscards]++;
321                                 ip->stats[FragFails]++;
322                                 freeblist(nb);
323                                 netlog(f, Logip, "!xp: chunk in v6%d\n", chunk);
324                                 goto raise;
325                         }
326                         blklen = chunk;
327                         if (BLEN(xp) < chunk)
328                                 blklen = BLEN(xp);
329                         memmove(nb->wp, xp->rp, blklen);
330
331                         nb->wp += blklen;
332                         xp->rp += blklen;
333                         chunk -= blklen;
334                         if (xp->rp == xp->wp)
335                                 xp = xp->next;
336                 }
337
338                 ifc->m->bwrite(ifc, nb, V6, gate);
339                 ip->stats[FragCreates]++;
340         }
341         ip->stats[FragOKs]++;
342
343 raise:
344         runlock(&ifc->rwlock);
345         poperror();
346 free:
347         freeblist(bp);
348         return rv;
349 }
350
351 void ipiput6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
352 {
353         int hl;
354         int hop, tos;
355         uint8_t proto;
356         struct ip6hdr *h;
357         struct Proto *p;
358         int notforme;
359         int tentative;
360         uint8_t v6dst[IPaddrlen];
361         struct IP *ip;
362         struct route *r, *sr;
363
364         ip = f->ip;
365         ip->stats[InReceives]++;
366
367         /*
368          *  Ensure we have all the header info in the first
369          *  block.  Make life easier for other protocols by
370          *  collecting up to the first 64 bytes in the first block.
371          */
372         if (BLEN(bp) < 64) {
373                 hl = blocklen(bp);
374                 if (hl < IP6HDR)
375                         hl = IP6HDR;
376                 if (hl > 64)
377                         hl = 64;
378                 bp = pullupblock(bp, hl);
379                 if (bp == NULL)
380                         return;
381         }
382
383         h = (struct ip6hdr *)(bp->rp);
384
385         memmove(&v6dst[0], &(h->dst)[0], IPaddrlen);
386         notforme = ipforme(f, v6dst) == 0;
387         tentative = iptentative(f, v6dst);
388
389         if (tentative && (h->proto != ICMPv6)) {
390                 printd("tentative addr, drop\n");
391                 freeblist(bp);
392                 return;
393         }
394
395         /* Check header version */
396         if (BLKIPVER(bp) != IP_VER6) {
397                 ip->stats[InHdrErrors]++;
398                 netlog(f, Logip, "ip: bad version 0x%x\n",
399                        (h->vcf[0] & 0xF0) >> 2);
400                 freeblist(bp);
401                 return;
402         }
403
404         /* route */
405         if (notforme) {
406                 if (!ip->iprouting) {
407                         freeb(bp);
408                         return;
409                 }
410                 /* don't forward to source's network */
411                 sr = v6lookup(f, h->src, NULL);
412                 r = v6lookup(f, h->dst, NULL);
413
414                 if (r == NULL || sr == r) {
415                         ip->stats[OutDiscards]++;
416                         freeblist(bp);
417                         return;
418                 }
419
420                 /* don't forward if packet has timed out */
421                 hop = h->ttl;
422                 if (hop < 1) {
423                         ip->stats[InHdrErrors]++;
424                         icmpttlexceeded6(f, ifc, bp);
425                         freeblist(bp);
426                         return;
427                 }
428
429                 /* process headers & reassemble if the interface expects it */
430                 bp = procxtns(ip, bp, r->rt.ifc->reassemble);
431
432                 if (bp == NULL)
433                         return;
434
435                 ip->stats[ForwDatagrams]++;
436                 h = (struct ip6hdr *)(bp->rp);
437                 tos = IPV6CLASS(h);
438                 hop = h->ttl;
439                 ipoput6(f, bp, 1, hop - 1, tos, NULL);
440                 return;
441         }
442
443         /* reassemble & process headers if needed */
444         bp = procxtns(ip, bp, 1);
445
446         if (bp == NULL)
447                 return;
448
449         h = (struct ip6hdr *)(bp->rp);
450         proto = h->proto;
451         p = Fsrcvpcol(f, proto);
452         if (p != NULL && p->rcv != NULL) {
453                 ip->stats[InDelivers]++;
454                 (*p->rcv) (p, ifc, bp);
455                 return;
456         }
457
458         ip->stats[InDiscards]++;
459         ip->stats[InUnknownProtos]++;
460         freeblist(bp);
461 }
462
463 /*
464  * ipfragfree6 - copied from ipfragfree4 - assume hold fraglock6
465  */
466 void ipfragfree6(struct IP *ip, struct fragment6 *frag)
467 {
468         struct fragment6 *fl, **l;
469
470         if (frag->blist)
471                 freeblist(frag->blist);
472
473         memset(frag->src, 0, IPaddrlen);
474         frag->id = 0;
475         frag->blist = NULL;
476
477         l = &ip->flisthead6;
478         for (fl = *l; fl; fl = fl->next) {
479                 if (fl == frag) {
480                         *l = frag->next;
481                         break;
482                 }
483                 l = &fl->next;
484         }
485
486         frag->next = ip->fragfree6;
487         ip->fragfree6 = frag;
488
489 }
490
491 /*
492  * ipfragallo6 - copied from ipfragalloc4
493  */
494 struct fragment6 *ipfragallo6(struct IP *ip)
495 {
496         struct fragment6 *f;
497
498         while (ip->fragfree6 == NULL) {
499                 /* free last entry on fraglist */
500                 for (f = ip->flisthead6; f->next; f = f->next) ;
501                 ipfragfree6(ip, f);
502         }
503         f = ip->fragfree6;
504         ip->fragfree6 = f->next;
505         f->next = ip->flisthead6;
506         ip->flisthead6 = f;
507         f->age = NOW + 30000;
508
509         return f;
510 }
511
512 static struct block *procxtns(struct IP *ip, struct block *bp, int doreasm)
513 {
514
515         int offset;
516         uint8_t proto;
517         struct ip6hdr *h;
518
519         h = (struct ip6hdr *)(bp->rp);
520         offset = unfraglen(bp, &proto, 0);
521
522         if ((proto == FH) && (doreasm != 0)) {
523                 bp = ip6reassemble(ip, offset, bp, h);
524                 if (bp == NULL)
525                         return NULL;
526                 offset = unfraglen(bp, &proto, 0);
527         }
528
529         if (proto == DOH || offset > IP6HDR)
530                 bp = procopts(bp);
531
532         return bp;
533 }
534
535 /* returns length of "Unfragmentable part", i.e., sum of lengths of ipv6 hdr,
536  * hop-by-hop & routing headers if present; *nexthdr is set to nexthdr value of
537  * the last header in the "Unfragmentable part"; if setfh != 0, nexthdr field of
538  * the last header in the "Unfragmentable part" is set to FH.
539  */
540 int unfraglen(struct block *bp, uint8_t * nexthdr, int setfh)
541 {
542         uint8_t *p, *q;
543         int ufl, hs;
544
545         p = bp->rp;
546         q = p + 6; /* proto, = p+sizeof(Ip6hdr.vcf)+sizeof(Ip6hdr.ploadlen) */
547         *nexthdr = *q;
548         ufl = IP6HDR;
549         p += ufl;
550
551         for (;;) {
552                 if (*nexthdr == HBH || *nexthdr == RH) {
553                         *nexthdr = *p;
554                         hs = ((int)*(p + 1) + 1) * 8;
555                         ufl += hs;
556                         q = p;
557                         p += hs;
558                 } else
559                         break;
560         }
561
562         if (*nexthdr == FH)
563                 *q = *p;
564
565         if (setfh)
566                 *q = FH;
567
568         return ufl;
569 }
570
571 struct block *procopts(struct block *bp)
572 {
573         return bp;
574 }
575
576 struct block *ip6reassemble(struct IP *ip, int uflen, struct block *bp,
577                             struct ip6hdr *ih)
578 {
579
580         int fend, offset;
581         unsigned int id;
582         struct fragment6 *f, *fnext;
583         struct fraghdr6 *fraghdr;
584         uint8_t src[IPaddrlen], dst[IPaddrlen];
585         struct block *bl, **l, *last, *prev;
586         int ovlap, len, fragsize, pktposn;
587
588         fraghdr = (struct fraghdr6 *)(bp->rp + uflen);
589         memmove(src, ih->src, IPaddrlen);
590         memmove(dst, ih->dst, IPaddrlen);
591         id = nhgetl(fraghdr->id);
592         offset = nhgets(fraghdr->offsetRM) & ~7;
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 ip6hdr *)(bp->rp);
600         }
601
602         qlock(&ip->fraglock6);
603
604         /*
605          *  find a reassembly queue for this fragment
606          */
607         for (f = ip->flisthead6; f; f = fnext) {
608                 fnext = f->next;
609                 if (ipcmp(f->src, src) == 0 && ipcmp(f->dst, dst) == 0
610                     && f->id == id)
611                         break;
612                 if (f->age < NOW) {
613                         ip->stats[ReasmTimeout]++;
614                         ipfragfree6(ip, f);
615                 }
616         }
617
618         /*
619          *  if this isn't a fragmented packet, accept it
620          *  and get rid of any fragments that might go
621          *  with it.
622          */
623         if (nhgets(fraghdr->offsetRM) == 0) {   // first frag is also the last
624                 if (f != NULL) {
625                         ipfragfree6(ip, f);
626                         ip->stats[ReasmFails]++;
627                 }
628                 qunlock(&ip->fraglock6);
629                 return bp;
630         }
631
632         if (bp->base + sizeof(struct Ipfrag) >= bp->rp) {
633                 bp = padblock(bp, sizeof(struct Ipfrag));
634                 bp->rp += sizeof(struct Ipfrag);
635         }
636
637         BKFG(bp)->foff = offset;
638         BKFG(bp)->flen = nhgets(ih->ploadlen) + IP6HDR - uflen - IP6FHDR;
639
640         /* First fragment allocates a reassembly queue */
641         if (f == NULL) {
642                 f = ipfragallo6(ip);
643                 f->id = id;
644                 memmove(f->src, src, IPaddrlen);
645                 memmove(f->dst, dst, IPaddrlen);
646
647                 f->blist = bp;
648
649                 qunlock(&ip->fraglock6);
650                 ip->stats[ReasmReqds]++;
651                 return NULL;
652         }
653
654         /*
655          *  find the new fragment's position in the queue
656          */
657         prev = NULL;
658         l = &f->blist;
659         bl = f->blist;
660         while (bl != NULL && BKFG(bp)->foff > BKFG(bl)->foff) {
661                 prev = bl;
662                 l = &bl->next;
663                 bl = bl->next;
664         }
665
666         /* Check overlap of a previous fragment - trim away as necessary */
667         if (prev) {
668                 ovlap = BKFG(prev)->foff + BKFG(prev)->flen - BKFG(bp)->foff;
669                 if (ovlap > 0) {
670                         if (ovlap >= BKFG(bp)->flen) {
671                                 freeblist(bp);
672                                 qunlock(&ip->fraglock6);
673                                 return NULL;
674                         }
675                         BKFG(prev)->flen -= ovlap;
676                 }
677         }
678
679         /* Link onto assembly queue */
680         bp->next = *l;
681         *l = bp;
682
683         /* Check to see if succeeding segments overlap */
684         if (bp->next) {
685                 l = &bp->next;
686                 fend = BKFG(bp)->foff + BKFG(bp)->flen;
687
688                 /* Take completely covered segments out */
689
690                 while (*l) {
691                         ovlap = fend - BKFG(*l)->foff;
692
693                         if (ovlap <= 0)
694                                 break;
695                         if (ovlap < BKFG(*l)->flen) {
696                                 BKFG(*l)->flen -= ovlap;
697                                 BKFG(*l)->foff += ovlap;
698                                 /* move up ih hdrs */
699                                 memmove((*l)->rp + ovlap, (*l)->rp, uflen);
700                                 (*l)->rp += ovlap;
701                                 break;
702                         }
703                         last = (*l)->next;
704                         (*l)->next = NULL;
705                         freeblist(*l);
706                         *l = last;
707                 }
708         }
709
710         /*
711          *  look for a complete packet.  if we get to a fragment
712          *  with the trailing bit of fraghdr->offsetRM[1] set, we're done.
713          */
714         pktposn = 0;
715         for (bl = f->blist; bl; bl = bl->next) {
716                 if (BKFG(bl)->foff != pktposn)
717                         break;
718
719                 fraghdr = (struct fraghdr6 *)(bl->rp + uflen);
720                 if ((fraghdr->offsetRM[1] & 1) == 0) {
721                         bl = f->blist;
722
723                         /* get rid of frag header in first fragment */
724
725                         memmove(bl->rp + IP6FHDR, bl->rp, uflen);
726                         bl->rp += IP6FHDR;
727                         len = nhgets(((struct ip6hdr *)(bl->rp))->ploadlen) -
728                                 IP6FHDR;
729                         bl->wp = bl->rp + len + IP6HDR;
730
731                         /* Pullup all the fragment headers and
732                          * return a complete packet
733                          */
734                         for (bl = bl->next; bl; bl = bl->next) {
735                                 fragsize = BKFG(bl)->flen;
736                                 len += fragsize;
737                                 bl->rp += uflen + IP6FHDR;
738                                 bl->wp = bl->rp + fragsize;
739                         }
740
741                         bl = f->blist;
742                         f->blist = NULL;
743                         ipfragfree6(ip, f);
744                         ih = (struct ip6hdr *)(bl->rp);
745                         hnputs(ih->ploadlen, len);
746                         qunlock(&ip->fraglock6);
747                         ip->stats[ReasmOKs]++;
748                         return bl;
749                 }
750                 pktposn += BKFG(bl)->flen;
751         }
752         qunlock(&ip->fraglock6);
753
754         return NULL;
755 }