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