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