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