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