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