akaros/kern/src/net/icmp6.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-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 <kmalloc.h>
  30#include <string.h>
  31#include <stdio.h>
  32#include <syscall.h>
  33#include <error.h>
  34#include <net/ip.h>
  35
  36struct ICMPpkt {
  37        uint8_t type;
  38        uint8_t code;
  39        uint8_t cksum[2];
  40        uint8_t icmpid[2];
  41        uint8_t seq[2];
  42};
  43
  44/* plan 9 uses anon struct members.
  45 * We have been naming the struct
  46 * members and just using the extra level of deref
  47 * e.g. i->x becomes i->i6->x.
  48 */
  49struct IPICMP {
  50/*
  51        Ip6hdr;
  52        ICMPpkt;
  53*/
  54        uint8_t vcf[4];         // version:4, traffic class:8, flow label:20
  55        uint8_t ploadlen[2];    // payload length: packet length - 40
  56        uint8_t proto;          // next header type
  57        uint8_t ttl;            // hop limit
  58        uint8_t src[IPaddrlen];
  59        uint8_t dst[IPaddrlen];
  60        uint8_t type;
  61        uint8_t code;
  62        uint8_t cksum[2];
  63        uint8_t icmpid[2];
  64        uint8_t seq[2];
  65
  66};
  67
  68struct NdiscC {
  69        //IPICMP;
  70        uint8_t vcf[4];         // version:4, traffic class:8, flow label:20
  71        uint8_t ploadlen[2];    // payload length: packet length - 40
  72        uint8_t proto;          // next header type
  73        uint8_t ttl;            // hop limit
  74        uint8_t src[IPaddrlen];
  75        uint8_t dst[IPaddrlen];
  76        uint8_t type;
  77        uint8_t code;
  78        uint8_t cksum[2];
  79        uint8_t icmpid[2];
  80        uint8_t seq[2];
  81
  82        uint8_t target[IPaddrlen];
  83};
  84
  85struct Ndpkt {
  86        //NdiscC;
  87        uint8_t vcf[4];         // version:4, traffic class:8, flow label:20
  88        uint8_t ploadlen[2];    // payload length: packet length - 40
  89        uint8_t proto;          // next header type
  90        uint8_t ttl;            // hop limit
  91        uint8_t src[IPaddrlen];
  92        uint8_t dst[IPaddrlen];
  93        uint8_t type;
  94        uint8_t code;
  95        uint8_t cksum[2];
  96        uint8_t icmpid[2];
  97        uint8_t seq[2];
  98
  99        uint8_t target[IPaddrlen];
 100
 101        uint8_t otype;
 102        uint8_t olen;           // length in units of 8 octets(incl type, code),
 103        // 1 for IEEE 802 addresses
 104        uint8_t lnaddr[6];      // link-layer address
 105};
 106
 107enum {
 108        // ICMPv6 types
 109        EchoReply = 0,
 110        UnreachableV6 = 1,
 111        PacketTooBigV6 = 2,
 112        TimeExceedV6 = 3,
 113        SrcQuench = 4,
 114        ParamProblemV6 = 4,
 115        Redirect = 5,
 116        EchoRequest = 8,
 117        TimeExceed = 11,
 118        InParmProblem = 12,
 119        Timestamp = 13,
 120        TimestampReply = 14,
 121        InfoRequest = 15,
 122        InfoReply = 16,
 123        AddrMaskRequest = 17,
 124        AddrMaskReply = 18,
 125        EchoRequestV6 = 128,
 126        EchoReplyV6 = 129,
 127        RouterSolicit = 133,
 128        RouterAdvert = 134,
 129        NbrSolicit = 135,
 130        NbrAdvert = 136,
 131        RedirectV6 = 137,
 132
 133        Maxtype6 = 137,
 134};
 135
 136char *icmpnames6[Maxtype6 + 1] = {
 137        [EchoReply] "EchoReply",
 138        [UnreachableV6] "UnreachableV6",
 139        [PacketTooBigV6] "PacketTooBigV6",
 140        [TimeExceedV6] "TimeExceedV6",
 141        [SrcQuench] "SrcQuench",
 142        [Redirect] "Redirect",
 143        [EchoRequest] "EchoRequest",
 144        [TimeExceed] "TimeExceed",
 145        [InParmProblem] "InParmProblem",
 146        [Timestamp] "Timestamp",
 147        [TimestampReply] "TimestampReply",
 148        [InfoRequest] "InfoRequest",
 149        [InfoReply] "InfoReply",
 150        [AddrMaskRequest] "AddrMaskRequest",
 151        [AddrMaskReply] "AddrMaskReply",
 152        [EchoRequestV6] "EchoRequestV6",
 153        [EchoReplyV6] "EchoReplyV6",
 154        [RouterSolicit] "RouterSolicit",
 155        [RouterAdvert] "RouterAdvert",
 156        [NbrSolicit] "NbrSolicit",
 157        [NbrAdvert] "NbrAdvert",
 158        [RedirectV6] "RedirectV6",
 159};
 160
 161enum {
 162        InMsgs6,
 163        InErrors6,
 164        OutMsgs6,
 165        CsumErrs6,
 166        LenErrs6,
 167        HlenErrs6,
 168        HoplimErrs6,
 169        IcmpCodeErrs6,
 170        TargetErrs6,
 171        OptlenErrs6,
 172        AddrmxpErrs6,
 173        RouterAddrErrs6,
 174
 175        Nstats6,
 176};
 177
 178static char *statnames6[Nstats6] = {
 179        [InMsgs6] "InMsgs",
 180        [InErrors6] "InErrors",
 181        [OutMsgs6] "OutMsgs",
 182        [CsumErrs6] "CsumErrs",
 183        [LenErrs6] "LenErrs",
 184        [HlenErrs6] "HlenErrs",
 185        [HoplimErrs6] "HoplimErrs",
 186        [IcmpCodeErrs6] "IcmpCodeErrs",
 187        [TargetErrs6] "TargetErrs",
 188        [OptlenErrs6] "OptlenErrs",
 189        [AddrmxpErrs6] "AddrmxpErrs",
 190        [RouterAddrErrs6] "RouterAddrErrs",
 191};
 192
 193typedef struct Icmppriv6 {
 194        uint32_t stats[Nstats6];
 195
 196        /* message counts */
 197        uint32_t in[Maxtype6 + 1];
 198        uint32_t out[Maxtype6 + 1];
 199} Icmppriv6;
 200
 201typedef struct Icmpcb6 {
 202        uint8_t headers;
 203} Icmpcb6;
 204
 205static char *unreachcode[] = {
 206        [icmp6_no_route] "no route to destination",
 207        [icmp6_ad_prohib] "comm with destination administratively prohibited",
 208        [icmp6_unassigned] "icmp unreachable: unassigned error code (2)",
 209        [icmp6_adr_unreach] "address unreachable",
 210        [icmp6_port_unreach] "port unreachable",
 211        [icmp6_unkn_code] "icmp unreachable: unknown code",
 212};
 213
 214enum {
 215        ICMP_USEAD6 = 40,
 216};
 217
 218enum {
 219        Oflag = 1 << 5,
 220        Sflag = 1 << 6,
 221        Rflag = 1 << 7,
 222};
 223
 224enum {
 225        slladd = 1,
 226        tlladd = 2,
 227        prfinfo = 3,
 228        redhdr = 4,
 229        mtuopt = 5,
 230};
 231
 232static void icmpkick6(void *x, struct block *bp);
 233
 234static void icmpcreate6(struct conv *c)
 235{
 236        c->rq = qopen(64 * 1024, Qmsg, 0, c);
 237        c->wq = qbypass(icmpkick6, c);
 238}
 239
 240static void set_cksum(struct block *bp)
 241{
 242        struct IPICMP *p = (struct IPICMP *)(bp->rp);
 243
 244        hnputl(p->vcf, 0);      // borrow IP header as pseudoheader
 245        hnputs(p->ploadlen, blocklen(bp) - IPV6HDR_LEN);
 246        p->proto = 0;
 247        p->ttl = ICMPv6;        // ttl gets set later
 248        hnputs(p->cksum, 0);
 249        hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
 250        p->proto = ICMPv6;
 251}
 252
 253static struct block *newIPICMP(int packetlen)
 254{
 255        struct block *nbp;
 256
 257        nbp = block_alloc(packetlen, MEM_WAIT);
 258        nbp->wp += packetlen;
 259        memset(nbp->rp, 0, packetlen);
 260        return nbp;
 261}
 262
 263void icmpadvise6(struct Proto *icmp, struct block *bp, char *msg)
 264{
 265        struct conv **c, *s;
 266        struct IPICMP *p;
 267        uint16_t recid;
 268
 269        p = (struct IPICMP *)bp->rp;
 270        recid = nhgets(p->icmpid);
 271
 272        for (c = icmp->conv; *c; c++) {
 273                s = *c;
 274                if (s->lport == recid)
 275                        if (ipcmp(s->raddr, p->dst) == 0) {
 276                                qhangup(s->rq, msg);
 277                                qhangup(s->wq, msg);
 278                                break;
 279                        }
 280        }
 281        freeblist(bp);
 282}
 283
 284static void icmpkick6(void *x, struct block *bp)
 285{
 286        struct conv *c = x;
 287        struct IPICMP *p;
 288        uint8_t laddr[IPaddrlen], raddr[IPaddrlen];
 289        Icmppriv6 *ipriv = c->p->priv;
 290        Icmpcb6 *icb = (struct Icmpcb6 *)c->ptcl;
 291
 292        if (bp == NULL)
 293                return;
 294
 295        if (icb->headers == 6) {
 296                /* get user specified addresses */
 297                bp = pullupblock(bp, ICMP_USEAD6);
 298                if (bp == NULL)
 299                        return;
 300                bp->rp += 8;
 301                ipmove(laddr, bp->rp);
 302                bp->rp += IPaddrlen;
 303                ipmove(raddr, bp->rp);
 304                bp->rp += IPaddrlen;
 305                bp = padblock(bp, sizeof(struct ip6hdr));
 306
 307                if (blocklen(bp) < sizeof(struct IPICMP)) {
 308                        freeblist(bp);
 309                        return;
 310                }
 311                p = (struct IPICMP *)(bp->rp);
 312
 313                ipmove(p->dst, raddr);
 314                ipmove(p->src, laddr);
 315
 316        } else {
 317                if (blocklen(bp) < sizeof(struct IPICMP)) {
 318                        freeblist(bp);
 319                        return;
 320                }
 321                p = (struct IPICMP *)(bp->rp);
 322
 323                ipmove(p->dst, c->raddr);
 324                ipmove(p->src, c->laddr);
 325                hnputs(p->icmpid, c->lport);
 326        }
 327
 328        set_cksum(bp);
 329        p->vcf[0] = 0x06 << 4;
 330        if (p->type <= Maxtype6)
 331                ipriv->out[p->type]++;
 332        ipoput6(c->p->f, bp, 0, c->ttl, c->tos, NULL);
 333}
 334
 335static void icmpctl6(struct conv *c, char **argv, int argc)
 336{
 337        Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
 338
 339        if ((argc == 1) && strcmp(argv[0], "headers") == 0)
 340                icb->headers = 6;
 341        else
 342                error(EINVAL, "unknown command to icmpctl6");
 343}
 344
 345static void goticmpkt6(struct Proto *icmp, struct block *bp, int muxkey)
 346{
 347        struct conv **c, *s;
 348        struct IPICMP *p = (struct IPICMP *)bp->rp;
 349        uint16_t recid;
 350        uint8_t *addr;
 351
 352        if (muxkey == 0) {
 353                recid = nhgets(p->icmpid);
 354                addr = p->src;
 355        } else {
 356                recid = muxkey;
 357                addr = p->dst;
 358        }
 359
 360        for (c = icmp->conv; *c; c++) {
 361                s = *c;
 362                if (s->lport == recid && ipcmp(s->raddr, addr) == 0) {
 363                        bp = concatblock(bp);
 364                        if (bp != NULL)
 365                                qpass(s->rq, bp);
 366                        return;
 367                }
 368        }
 369
 370        freeblist(bp);
 371}
 372
 373static struct block *mkechoreply6(struct block *bp)
 374{
 375        struct IPICMP *p = (struct IPICMP *)(bp->rp);
 376        uint8_t addr[IPaddrlen];
 377
 378        ipmove(addr, p->src);
 379        ipmove(p->src, p->dst);
 380        ipmove(p->dst, addr);
 381        p->type = EchoReplyV6;
 382        set_cksum(bp);
 383        return bp;
 384}
 385
 386/*
 387 * sends out an ICMPv6 neighbor solicitation
 388 *      suni == SRC_UNSPEC or SRC_UNI,
 389 *      tuni == TARG_MULTI => multicast for address resolution,
 390 *      and tuni == TARG_UNI => neighbor reachability.
 391 */
 392
 393void icmpns(struct Fs *f, uint8_t *src, int suni, uint8_t *targ, int tuni,
 394            uint8_t *mac)
 395{
 396        struct block *nbp;
 397        struct Ndpkt *np;
 398        struct Proto *icmp = f->t2p[ICMPv6];
 399        struct Icmppriv6 *ipriv = icmp->priv;
 400
 401        nbp = newIPICMP(sizeof(struct Ndpkt));
 402        np = (struct Ndpkt *)nbp->rp;
 403
 404        if (suni == SRC_UNSPEC)
 405                memmove(np->src, v6Unspecified, IPaddrlen);
 406        else
 407                memmove(np->src, src, IPaddrlen);
 408
 409        if (tuni == TARG_UNI)
 410                memmove(np->dst, targ, IPaddrlen);
 411        else
 412                ipv62smcast(np->dst, targ);
 413
 414        np->type = NbrSolicit;
 415        np->code = 0;
 416        memmove(np->target, targ, IPaddrlen);
 417        if (suni != SRC_UNSPEC) {
 418                np->otype = SRC_LLADDRESS;
 419                np->olen = 1;   /* 1+1+6 = 8 = 1 8-octet */
 420                memmove(np->lnaddr, mac, sizeof(np->lnaddr));
 421        } else {
 422                int r = sizeof(struct Ndpkt) - sizeof(struct NdiscC);
 423                nbp->wp -= r;
 424        }
 425
 426        set_cksum(nbp);
 427        np = (struct Ndpkt *)nbp->rp;
 428        np->ttl = HOP_LIMIT;
 429        np->vcf[0] = 0x06 << 4;
 430        ipriv->out[NbrSolicit]++;
 431        netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
 432        ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
 433}
 434
 435/*
 436 * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
 437 */
 438void icmpna(struct Fs *f, uint8_t * src, uint8_t * dst, uint8_t * targ,
 439            uint8_t * mac, uint8_t flags)
 440{
 441        struct block *nbp;
 442        struct Ndpkt *np;
 443        struct Proto *icmp = f->t2p[ICMPv6];
 444        Icmppriv6 *ipriv = icmp->priv;
 445
 446        nbp = newIPICMP(sizeof(struct Ndpkt));
 447        np = (struct Ndpkt *)nbp->rp;
 448
 449        memmove(np->src, src, IPaddrlen);
 450        memmove(np->dst, dst, IPaddrlen);
 451
 452        np->type = NbrAdvert;
 453        np->code = 0;
 454        np->icmpid[0] = flags;
 455        memmove(np->target, targ, IPaddrlen);
 456
 457        np->otype = TARGET_LLADDRESS;
 458        np->olen = 1;
 459        memmove(np->lnaddr, mac, sizeof(np->lnaddr));
 460
 461        set_cksum(nbp);
 462        np = (struct Ndpkt *)nbp->rp;
 463        np->ttl = HOP_LIMIT;
 464        np->vcf[0] = 0x06 << 4;
 465        ipriv->out[NbrAdvert]++;
 466        netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
 467        ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
 468}
 469
 470void icmphostunr(struct Fs *f, struct Ipifc *ifc,
 471                 struct block *bp, int code, int free)
 472{
 473        struct block *nbp;
 474        struct IPICMP *np;
 475        struct ip6hdr *p;
 476        int osz = BLEN(bp);
 477        int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
 478        struct Proto *icmp = f->t2p[ICMPv6];
 479        Icmppriv6 *ipriv = icmp->priv;
 480
 481        p = (struct ip6hdr *)bp->rp;
 482
 483        if (isv6mcast(p->src))
 484                goto freebl;
 485
 486        nbp = newIPICMP(sz);
 487        np = (struct IPICMP *)nbp->rp;
 488
 489        rlock(&ifc->rwlock);
 490        if (ipv6anylocal(ifc, np->src)) {
 491                netlog(f, Logicmp, "send icmphostunr -> s%I d%I\n",
 492                       p->src, p->dst);
 493        } else {
 494                netlog(f, Logicmp, "icmphostunr fail -> s%I d%I\n",
 495                       p->src, p->dst);
 496                runlock(&ifc->rwlock);
 497                freeblist(nbp);
 498                goto freebl;
 499        }
 500
 501        memmove(np->dst, p->src, IPaddrlen);
 502        np->type = UnreachableV6;
 503        np->code = code;
 504        memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
 505                        sz - sizeof(struct IPICMP));
 506        set_cksum(nbp);
 507        np->ttl = HOP_LIMIT;
 508        np->vcf[0] = 0x06 << 4;
 509        ipriv->out[UnreachableV6]++;
 510
 511        if (free)
 512                ipiput6(f, ifc, nbp);
 513        else
 514                ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
 515        runlock(&ifc->rwlock);
 516freebl:
 517        if (free)
 518                freeblist(bp);
 519}
 520
 521void icmpttlexceeded6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
 522{
 523        struct block *nbp;
 524        struct IPICMP *np;
 525        struct ip6hdr *p;
 526        int osz = BLEN(bp);
 527        int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
 528        struct Proto *icmp = f->t2p[ICMPv6];
 529        Icmppriv6 *ipriv = icmp->priv;
 530
 531        p = (struct ip6hdr *)bp->rp;
 532
 533        if (isv6mcast(p->src))
 534                return;
 535
 536        nbp = newIPICMP(sz);
 537        np = (struct IPICMP *)nbp->rp;
 538
 539        if (ipv6anylocal(ifc, np->src)) {
 540                netlog(f, Logicmp, "send icmpttlexceeded6 -> s%I d%I\n",
 541                       p->src, p->dst);
 542        } else {
 543                netlog(f, Logicmp, "icmpttlexceeded6 fail -> s%I d%I\n",
 544                       p->src, p->dst);
 545                return;
 546        }
 547
 548        memmove(np->dst, p->src, IPaddrlen);
 549        np->type = TimeExceedV6;
 550        np->code = 0;
 551        memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
 552                sz - sizeof(struct IPICMP));
 553        set_cksum(nbp);
 554        np->ttl = HOP_LIMIT;
 555        np->vcf[0] = 0x06 << 4;
 556        ipriv->out[TimeExceedV6]++;
 557        ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
 558}
 559
 560void icmppkttoobig6(struct Fs *f, struct Ipifc *ifc, struct block *bp)
 561{
 562        struct block *nbp;
 563        struct IPICMP *np;
 564        struct ip6hdr *p;
 565        int osz = BLEN(bp);
 566        int sz = MIN(sizeof(struct IPICMP) + osz, v6MINTU);
 567        struct Proto *icmp = f->t2p[ICMPv6];
 568        Icmppriv6 *ipriv = icmp->priv;
 569
 570        p = (struct ip6hdr *)bp->rp;
 571
 572        if (isv6mcast(p->src))
 573                return;
 574
 575        nbp = newIPICMP(sz);
 576        np = (struct IPICMP *)nbp->rp;
 577
 578        if (!ipv6anylocal(ifc, np->src)) {
 579                netlog(f, Logicmp, "icmppkttoobig6 fail -> s%I d%I\n",
 580                       p->src, p->dst);
 581                return;
 582        }
 583        netlog(f, Logicmp, "send icmppkttoobig6 -> s%I d%I\n",
 584               p->src, p->dst);
 585
 586        memmove(np->dst, p->src, IPaddrlen);
 587        np->type = PacketTooBigV6;
 588        np->code = 0;
 589        hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
 590        memmove(nbp->rp + sizeof(struct IPICMP), bp->rp,
 591                        sz - sizeof(struct IPICMP));
 592        set_cksum(nbp);
 593        np->ttl = HOP_LIMIT;
 594        np->vcf[0] = 0x06 << 4;
 595        ipriv->out[PacketTooBigV6]++;
 596        ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, NULL);
 597}
 598
 599/*
 600 * RFC 2461, pages 39-40, pages 57-58.
 601 */
 602static int valid(struct Proto *icmp, struct Ipifc *ifc,
 603                 struct block *bp, Icmppriv6 * ipriv)
 604{
 605        int sz, osz, unsp, n, ttl, iplen;
 606        int pktsz = BLEN(bp);
 607        uint8_t *packet = bp->rp;
 608        struct IPICMP *p = (struct IPICMP *)packet;
 609        struct Ndpkt *np;
 610
 611        n = blocklen(bp);
 612        if (n < sizeof(struct IPICMP)) {
 613                ipriv->stats[HlenErrs6]++;
 614                netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
 615                goto err;
 616        }
 617
 618        iplen = nhgets(p->ploadlen);
 619        if (iplen > n - IPV6HDR_LEN || (iplen % 1)) {
 620                ipriv->stats[LenErrs6]++;
 621                netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
 622                goto err;
 623        }
 624        // Rather than construct explicit pseudoheader, overwrite IPv6 header
 625        if (p->proto != ICMPv6) {
 626                // This code assumes no extension headers!!!
 627                netlog(icmp->f, Logicmp, "icmp error: extension header\n");
 628                goto err;
 629        }
 630        memset(packet, 0, 4);
 631        ttl = p->ttl;
 632        p->ttl = p->proto;
 633        p->proto = 0;
 634        if (ptclcsum(bp, 0, iplen + IPV6HDR_LEN)) {
 635                ipriv->stats[CsumErrs6]++;
 636                netlog(icmp->f, Logicmp, "icmp checksum error\n");
 637                goto err;
 638        }
 639        p->proto = p->ttl;
 640        p->ttl = ttl;
 641
 642        /* additional tests for some pkt types */
 643        if ((p->type == NbrSolicit) ||
 644                (p->type == NbrAdvert) ||
 645                (p->type == RouterAdvert) ||
 646                (p->type == RouterSolicit) || (p->type == RedirectV6)) {
 647
 648                if (p->ttl != HOP_LIMIT) {
 649                        ipriv->stats[HoplimErrs6]++;
 650                        goto err;
 651                }
 652                if (p->code != 0) {
 653                        ipriv->stats[IcmpCodeErrs6]++;
 654                        goto err;
 655                }
 656
 657                switch (p->type) {
 658                case NbrSolicit:
 659                case NbrAdvert:
 660                        np = (struct Ndpkt *)p;
 661                        if (isv6mcast(np->target)) {
 662                                ipriv->stats[TargetErrs6]++;
 663                                goto err;
 664                        }
 665                        if (optexsts(np) && (np->olen == 0)) {
 666                                ipriv->stats[OptlenErrs6]++;
 667                                goto err;
 668                        }
 669
 670                        if (p->type == NbrSolicit) {
 671                                if (ipcmp(np->src, v6Unspecified) == 0) {
 672                                        if (!issmcast(np->dst) || optexsts(np))
 673                                        {
 674                                                ipriv->stats[AddrmxpErrs6]++;
 675                                                goto err;
 676                                        }
 677                                }
 678                        }
 679
 680                        if (p->type == NbrAdvert) {
 681                                if ((isv6mcast(np->dst)) &&
 682                                    (nhgets(np->icmpid) & Sflag)) {
 683                                        ipriv->stats[AddrmxpErrs6]++;
 684                                        goto err;
 685                                }
 686                        }
 687                        break;
 688
 689                case RouterAdvert:
 690                        if (pktsz - sizeof(struct ip6hdr) < 16) {
 691                                ipriv->stats[HlenErrs6]++;
 692                                goto err;
 693                        }
 694                        if (!islinklocal(p->src)) {
 695                                ipriv->stats[RouterAddrErrs6]++;
 696                                goto err;
 697                        }
 698                        sz = sizeof(struct IPICMP) + 8;
 699                        while ((sz + 1) < pktsz) {
 700                                osz = *(packet + sz + 1);
 701                                if (osz <= 0) {
 702                                        ipriv->stats[OptlenErrs6]++;
 703                                        goto err;
 704                                }
 705                                sz += 8 * osz;
 706                        }
 707                        break;
 708
 709                case RouterSolicit:
 710                        if (pktsz - sizeof(struct ip6hdr) < 8) {
 711                                ipriv->stats[HlenErrs6]++;
 712                                goto err;
 713                        }
 714                        unsp = (ipcmp(p->src, v6Unspecified) == 0);
 715                        sz = sizeof(struct IPICMP) + 8;
 716                        while ((sz + 1) < pktsz) {
 717                                osz = *(packet + sz + 1);
 718                                if ((osz <= 0) ||
 719                                    (unsp && (*(packet + sz) == slladd))) {
 720                                        ipriv->stats[OptlenErrs6]++;
 721                                        goto err;
 722                                }
 723                                sz += 8 * osz;
 724                        }
 725                        break;
 726
 727                case RedirectV6:
 728                        //to be filled in
 729                        break;
 730
 731                default:
 732                        goto err;
 733                }
 734        }
 735
 736        return 1;
 737
 738err:
 739        ipriv->stats[InErrors6]++;
 740        return 0;
 741}
 742
 743static int targettype(struct Fs *f, struct Ipifc *ifc, uint8_t * target)
 744{
 745        struct Iplifc *lifc;
 746        int t;
 747
 748        rlock(&ifc->rwlock);
 749        if (ipproxyifc(f, ifc, target)) {
 750                runlock(&ifc->rwlock);
 751                return t_uniproxy;
 752        }
 753
 754        for (lifc = ifc->lifc; lifc; lifc = lifc->next) {
 755                if (ipcmp(lifc->local, target) == 0) {
 756                        t = (lifc->tentative) ? t_unitent : t_unirany;
 757                        runlock(&ifc->rwlock);
 758                        return t;
 759                }
 760        }
 761
 762        runlock(&ifc->rwlock);
 763        return 0;
 764}
 765
 766static void icmpiput6(struct Proto *icmp, struct Ipifc *ipifc, struct block *bp)
 767{
 768        uint8_t *packet = bp->rp;
 769        struct IPICMP *p = (struct IPICMP *)packet;
 770        Icmppriv6 *ipriv = icmp->priv;
 771        struct block *r;
 772        struct Proto *pr;
 773        char *msg, m2[128];
 774        struct Ndpkt *np;
 775        uint8_t pktflags;
 776        uint8_t lsrc[IPaddrlen];
 777        int refresh = 1;
 778        struct Iplifc *lifc;
 779
 780        if (!valid(icmp, ipifc, bp, ipriv))
 781                goto raise;
 782
 783        if (p->type <= Maxtype6)
 784                ipriv->in[p->type]++;
 785        else
 786                goto raise;
 787
 788        switch (p->type) {
 789        case EchoRequestV6:
 790                r = mkechoreply6(bp);
 791                ipriv->out[EchoReply]++;
 792                ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, NULL);
 793                break;
 794
 795        case UnreachableV6:
 796                if (p->code > 4)
 797                        msg = unreachcode[icmp6_unkn_code];
 798                else
 799                        msg = unreachcode[p->code];
 800
 801                bp->rp += sizeof(struct IPICMP);
 802                if (blocklen(bp) < 8) {
 803                        ipriv->stats[LenErrs6]++;
 804                        goto raise;
 805                }
 806                p = (struct IPICMP *)bp->rp;
 807                pr = Fsrcvpcolx(icmp->f, p->proto);
 808                if (pr != NULL && pr->advise != NULL) {
 809                        (*pr->advise) (pr, bp, msg);
 810                        return;
 811                }
 812
 813                bp->rp -= sizeof(struct IPICMP);
 814                goticmpkt6(icmp, bp, 0);
 815                break;
 816
 817        case TimeExceedV6:
 818                if (p->code == 0) {
 819                        snprintf(m2, sizeof(m2), "ttl exceeded at %I", p->src);
 820
 821                        bp->rp += sizeof(struct IPICMP);
 822                        if (blocklen(bp) < 8) {
 823                                ipriv->stats[LenErrs6]++;
 824                                goto raise;
 825                        }
 826                        p = (struct IPICMP *)bp->rp;
 827                        pr = Fsrcvpcolx(icmp->f, p->proto);
 828                        if (pr != NULL && pr->advise != NULL) {
 829                                (*pr->advise) (pr, bp, m2);
 830                                return;
 831                        }
 832                        bp->rp -= sizeof(struct IPICMP);
 833                }
 834
 835                goticmpkt6(icmp, bp, 0);
 836                break;
 837
 838        case RouterAdvert:
 839        case RouterSolicit:
 840                /* using lsrc as a temp, munge hdr for goticmp6
 841                   memmove(lsrc, p->src, IPaddrlen);
 842                   memmove(p->src, p->dst, IPaddrlen);
 843                   memmove(p->dst, lsrc, IPaddrlen); */
 844
 845                goticmpkt6(icmp, bp, p->type);
 846                break;
 847
 848        case NbrSolicit:
 849                np = (struct Ndpkt *)p;
 850                pktflags = 0;
 851                switch (targettype(icmp->f, ipifc, np->target)) {
 852                        case t_unirany:
 853                                pktflags |= Oflag;
 854                                /* fall through */
 855
 856                        case t_uniproxy:
 857                                if (ipcmp(np->src, v6Unspecified) != 0) {
 858                                        arpenter(icmp->f, V6, np->src,
 859                                                 np->lnaddr, 8 * np->olen - 2,
 860                                                 0);
 861                                        pktflags |= Sflag;
 862                                }
 863                                if (ipv6local(ipifc, lsrc)) {
 864                                        icmpna(icmp->f, lsrc,
 865                                               (ipcmp(np->src, v6Unspecified)
 866                                                == 0) ? v6allnodesL : np->src,
 867                                               np->target, ipifc->mac,
 868                                               pktflags);
 869                                } else
 870                                        freeblist(bp);
 871                                break;
 872
 873                        case t_unitent:
 874                                /* not clear what needs to be done. send up an
 875                                 * icmp mesg saying don't use this address? */
 876
 877                        default:
 878                                freeblist(bp);
 879                }
 880
 881                break;
 882
 883        case NbrAdvert:
 884                np = (struct Ndpkt *)p;
 885
 886                /* if the target address matches one of the local interface
 887                 * address and the local interface address has tentative bit
 888                 * set, then insert into ARP table. this is so the duplication
 889                 * address detection part of ipconfig can discover duplication
 890                 * through the arp table
 891                 */
 892                lifc = iplocalonifc(ipifc, np->target);
 893                if (lifc && lifc->tentative)
 894                        refresh = 0;
 895                arpenter(icmp->f, V6, np->target, np->lnaddr, 8 * np->olen - 2,
 896                                 refresh);
 897                freeblist(bp);
 898                break;
 899
 900        case PacketTooBigV6:
 901
 902        default:
 903                goticmpkt6(icmp, bp, 0);
 904                break;
 905        }
 906        return;
 907
 908raise:
 909        freeblist(bp);
 910
 911}
 912
 913int icmpstats6(struct Proto *icmp6, char *buf, int len)
 914{
 915        Icmppriv6 *priv;
 916        char *p, *e;
 917        int i;
 918
 919        priv = icmp6->priv;
 920        p = buf;
 921        e = p + len;
 922        for (i = 0; i < Nstats6; i++)
 923                p = seprintf(p, e, "%s: %u\n", statnames6[i], priv->stats[i]);
 924        for (i = 0; i <= Maxtype6; i++) {
 925                if (icmpnames6[i])
 926                        p = seprintf(p, e, "%s: %u %u\n", icmpnames6[i],
 927                                     priv->in[i], priv->out[i]);
 928                else
 929                        p = seprintf(p, e, "%d: %u %u\n", i, priv->in[i],
 930                                     priv->out[i]);
 931        }
 932        return p - buf;
 933}
 934
 935// need to import from icmp.c
 936extern int icmpstate(struct conv *c, char *state, int n);
 937extern void icmpannounce(struct conv *c, char **argv, int argc);
 938extern void icmpconnect(struct conv *c, char **argv, int argc);
 939extern void icmpclose(struct conv *c);
 940
 941void icmp6init(struct Fs *fs)
 942{
 943        struct Proto *icmp6 = kzmalloc(sizeof(struct Proto), 0);
 944
 945        icmp6->priv = kzmalloc(sizeof(Icmppriv6), 0);
 946        icmp6->name = "icmpv6";
 947        icmp6->connect = icmpconnect;
 948        icmp6->announce = icmpannounce;
 949        icmp6->state = icmpstate;
 950        icmp6->create = icmpcreate6;
 951        icmp6->close = icmpclose;
 952        icmp6->rcv = icmpiput6;
 953        icmp6->stats = icmpstats6;
 954        icmp6->ctl = icmpctl6;
 955        icmp6->advise = icmpadvise6;
 956        icmp6->gc = NULL;
 957        icmp6->ipproto = ICMPv6;
 958        icmp6->nc = 16;
 959        icmp6->ptclsize = sizeof(Icmpcb6);
 960
 961        Fsproto(fs, icmp6);
 962}
 963