vx32

Local 9vx git repository for patches.
git clone git://r-36.net/vx32
Log | Files | 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 }