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