xref: /plan9-contrib/sys/src/9k/ip/icmp6.c (revision a3323688dee1e9a0fcc1d7d933e3ce101170952d)
19ef1f84bSDavid du Colombier /*
29ef1f84bSDavid du Colombier  * Internet Control Message Protocol for IPv6
39ef1f84bSDavid du Colombier  */
49ef1f84bSDavid du Colombier #include "u.h"
59ef1f84bSDavid du Colombier #include "../port/lib.h"
69ef1f84bSDavid du Colombier #include "mem.h"
79ef1f84bSDavid du Colombier #include "dat.h"
89ef1f84bSDavid du Colombier #include "fns.h"
99ef1f84bSDavid du Colombier #include "../port/error.h"
109ef1f84bSDavid du Colombier #include "ip.h"
119ef1f84bSDavid du Colombier #include "ipv6.h"
129ef1f84bSDavid du Colombier 
139ef1f84bSDavid du Colombier enum
149ef1f84bSDavid du Colombier {
159ef1f84bSDavid du Colombier 	InMsgs6,
169ef1f84bSDavid du Colombier 	InErrors6,
179ef1f84bSDavid du Colombier 	OutMsgs6,
189ef1f84bSDavid du Colombier 	CsumErrs6,
199ef1f84bSDavid du Colombier 	LenErrs6,
209ef1f84bSDavid du Colombier 	HlenErrs6,
219ef1f84bSDavid du Colombier 	HoplimErrs6,
229ef1f84bSDavid du Colombier 	IcmpCodeErrs6,
239ef1f84bSDavid du Colombier 	TargetErrs6,
249ef1f84bSDavid du Colombier 	OptlenErrs6,
259ef1f84bSDavid du Colombier 	AddrmxpErrs6,
269ef1f84bSDavid du Colombier 	RouterAddrErrs6,
279ef1f84bSDavid du Colombier 
289ef1f84bSDavid du Colombier 	Nstats6,
299ef1f84bSDavid du Colombier };
309ef1f84bSDavid du Colombier 
319ef1f84bSDavid du Colombier enum {
329ef1f84bSDavid du Colombier 	ICMP_USEAD6	= 40,
339ef1f84bSDavid du Colombier };
349ef1f84bSDavid du Colombier 
359ef1f84bSDavid du Colombier enum {
369ef1f84bSDavid du Colombier 	Oflag	= 1<<5,
379ef1f84bSDavid du Colombier 	Sflag	= 1<<6,
389ef1f84bSDavid du Colombier 	Rflag	= 1<<7,
399ef1f84bSDavid du Colombier };
409ef1f84bSDavid du Colombier 
419ef1f84bSDavid du Colombier enum {
429ef1f84bSDavid du Colombier 	/* ICMPv6 types */
439ef1f84bSDavid du Colombier 	EchoReply	= 0,
449ef1f84bSDavid du Colombier 	UnreachableV6	= 1,
459ef1f84bSDavid du Colombier 	PacketTooBigV6	= 2,
469ef1f84bSDavid du Colombier 	TimeExceedV6	= 3,
479ef1f84bSDavid du Colombier 	SrcQuench	= 4,
489ef1f84bSDavid du Colombier 	ParamProblemV6	= 4,
499ef1f84bSDavid du Colombier 	Redirect	= 5,
509ef1f84bSDavid du Colombier 	EchoRequest	= 8,
519ef1f84bSDavid du Colombier 	TimeExceed	= 11,
529ef1f84bSDavid du Colombier 	InParmProblem	= 12,
539ef1f84bSDavid du Colombier 	Timestamp	= 13,
549ef1f84bSDavid du Colombier 	TimestampReply	= 14,
559ef1f84bSDavid du Colombier 	InfoRequest	= 15,
569ef1f84bSDavid du Colombier 	InfoReply	= 16,
579ef1f84bSDavid du Colombier 	AddrMaskRequest = 17,
589ef1f84bSDavid du Colombier 	AddrMaskReply	= 18,
599ef1f84bSDavid du Colombier 	EchoRequestV6	= 128,
609ef1f84bSDavid du Colombier 	EchoReplyV6	= 129,
619ef1f84bSDavid du Colombier 	RouterSolicit	= 133,
629ef1f84bSDavid du Colombier 	RouterAdvert	= 134,
639ef1f84bSDavid du Colombier 	NbrSolicit	= 135,
649ef1f84bSDavid du Colombier 	NbrAdvert	= 136,
659ef1f84bSDavid du Colombier 	RedirectV6	= 137,
669ef1f84bSDavid du Colombier 
679ef1f84bSDavid du Colombier 	Maxtype6	= 137,
689ef1f84bSDavid du Colombier };
699ef1f84bSDavid du Colombier 
709ef1f84bSDavid du Colombier /* on-the-wire packet formats */
719ef1f84bSDavid du Colombier typedef struct IPICMP IPICMP;
729ef1f84bSDavid du Colombier typedef struct Ndpkt Ndpkt;
739ef1f84bSDavid du Colombier typedef struct NdiscC NdiscC;
749ef1f84bSDavid du Colombier 
759ef1f84bSDavid du Colombier /* we do this to avoid possible struct padding  */
769ef1f84bSDavid du Colombier #define ICMPHDR \
779ef1f84bSDavid du Colombier 	IPV6HDR; \
789ef1f84bSDavid du Colombier 	uchar	type; \
799ef1f84bSDavid du Colombier 	uchar	code; \
809ef1f84bSDavid du Colombier 	uchar	cksum[2]; \
819ef1f84bSDavid du Colombier 	uchar	icmpid[2]; \
829ef1f84bSDavid du Colombier 	uchar	seq[2]
839ef1f84bSDavid du Colombier 
849ef1f84bSDavid du Colombier struct IPICMP {
859ef1f84bSDavid du Colombier 	ICMPHDR;
869ef1f84bSDavid du Colombier 	uchar	payload[];
879ef1f84bSDavid du Colombier };
889ef1f84bSDavid du Colombier 
899ef1f84bSDavid du Colombier #define IPICMPSZ offsetof(IPICMP, payload[0])
909ef1f84bSDavid du Colombier 
919ef1f84bSDavid du Colombier struct NdiscC {
929ef1f84bSDavid du Colombier 	ICMPHDR;
939ef1f84bSDavid du Colombier 	uchar	target[IPaddrlen];
949ef1f84bSDavid du Colombier 	uchar	payload[];
959ef1f84bSDavid du Colombier };
969ef1f84bSDavid du Colombier 
979ef1f84bSDavid du Colombier #define NDISCSZ offsetof(NdiscC, payload[0])
989ef1f84bSDavid du Colombier 
999ef1f84bSDavid du Colombier struct Ndpkt {
1009ef1f84bSDavid du Colombier 	ICMPHDR;
1019ef1f84bSDavid du Colombier 	uchar	target[IPaddrlen];
1029ef1f84bSDavid du Colombier 	uchar	otype;
1039ef1f84bSDavid du Colombier 	uchar	olen;		/* length in units of 8 octets(incl type, code),
1049ef1f84bSDavid du Colombier 				 * 1 for IEEE 802 addresses */
1059ef1f84bSDavid du Colombier 	uchar	lnaddr[6];	/* link-layer address */
1069ef1f84bSDavid du Colombier 	uchar	payload[];
1079ef1f84bSDavid du Colombier };
1089ef1f84bSDavid du Colombier 
1099ef1f84bSDavid du Colombier #define NDPKTSZ offsetof(Ndpkt, payload[0])
1109ef1f84bSDavid du Colombier 
1119ef1f84bSDavid du Colombier typedef struct Icmppriv6
1129ef1f84bSDavid du Colombier {
1139ef1f84bSDavid du Colombier 	ulong	stats[Nstats6];
1149ef1f84bSDavid du Colombier 
1159ef1f84bSDavid du Colombier 	/* message counts */
1169ef1f84bSDavid du Colombier 	ulong	in[Maxtype6+1];
1179ef1f84bSDavid du Colombier 	ulong	out[Maxtype6+1];
1189ef1f84bSDavid du Colombier } Icmppriv6;
1199ef1f84bSDavid du Colombier 
1209ef1f84bSDavid du Colombier typedef struct Icmpcb6
1219ef1f84bSDavid du Colombier {
1229ef1f84bSDavid du Colombier 	QLock;
1239ef1f84bSDavid du Colombier 	uchar	headers;
1249ef1f84bSDavid du Colombier } Icmpcb6;
1259ef1f84bSDavid du Colombier 
1269ef1f84bSDavid du Colombier char *icmpnames6[Maxtype6+1] =
1279ef1f84bSDavid du Colombier {
1289ef1f84bSDavid du Colombier [EchoReply]		"EchoReply",
1299ef1f84bSDavid du Colombier [UnreachableV6]		"UnreachableV6",
1309ef1f84bSDavid du Colombier [PacketTooBigV6]	"PacketTooBigV6",
1319ef1f84bSDavid du Colombier [TimeExceedV6]		"TimeExceedV6",
1329ef1f84bSDavid du Colombier [SrcQuench]		"SrcQuench",
1339ef1f84bSDavid du Colombier [Redirect]		"Redirect",
1349ef1f84bSDavid du Colombier [EchoRequest]		"EchoRequest",
1359ef1f84bSDavid du Colombier [TimeExceed]		"TimeExceed",
1369ef1f84bSDavid du Colombier [InParmProblem]		"InParmProblem",
1379ef1f84bSDavid du Colombier [Timestamp]		"Timestamp",
1389ef1f84bSDavid du Colombier [TimestampReply]	"TimestampReply",
1399ef1f84bSDavid du Colombier [InfoRequest]		"InfoRequest",
1409ef1f84bSDavid du Colombier [InfoReply]		"InfoReply",
1419ef1f84bSDavid du Colombier [AddrMaskRequest]	"AddrMaskRequest",
1429ef1f84bSDavid du Colombier [AddrMaskReply]		"AddrMaskReply",
1439ef1f84bSDavid du Colombier [EchoRequestV6]		"EchoRequestV6",
1449ef1f84bSDavid du Colombier [EchoReplyV6]		"EchoReplyV6",
1459ef1f84bSDavid du Colombier [RouterSolicit]		"RouterSolicit",
1469ef1f84bSDavid du Colombier [RouterAdvert]		"RouterAdvert",
1479ef1f84bSDavid du Colombier [NbrSolicit]		"NbrSolicit",
1489ef1f84bSDavid du Colombier [NbrAdvert]		"NbrAdvert",
1499ef1f84bSDavid du Colombier [RedirectV6]		"RedirectV6",
1509ef1f84bSDavid du Colombier };
1519ef1f84bSDavid du Colombier 
1529ef1f84bSDavid du Colombier static char *statnames6[Nstats6] =
1539ef1f84bSDavid du Colombier {
1549ef1f84bSDavid du Colombier [InMsgs6]	"InMsgs",
1559ef1f84bSDavid du Colombier [InErrors6]	"InErrors",
1569ef1f84bSDavid du Colombier [OutMsgs6]	"OutMsgs",
1579ef1f84bSDavid du Colombier [CsumErrs6]	"CsumErrs",
1589ef1f84bSDavid du Colombier [LenErrs6]	"LenErrs",
1599ef1f84bSDavid du Colombier [HlenErrs6]	"HlenErrs",
1609ef1f84bSDavid du Colombier [HoplimErrs6]	"HoplimErrs",
1619ef1f84bSDavid du Colombier [IcmpCodeErrs6]	"IcmpCodeErrs",
1629ef1f84bSDavid du Colombier [TargetErrs6]	"TargetErrs",
1639ef1f84bSDavid du Colombier [OptlenErrs6]	"OptlenErrs",
1649ef1f84bSDavid du Colombier [AddrmxpErrs6]	"AddrmxpErrs",
1659ef1f84bSDavid du Colombier [RouterAddrErrs6]	"RouterAddrErrs",
1669ef1f84bSDavid du Colombier };
1679ef1f84bSDavid du Colombier 
1689ef1f84bSDavid du Colombier static char *unreachcode[] =
1699ef1f84bSDavid du Colombier {
1709ef1f84bSDavid du Colombier [Icmp6_no_route]	"no route to destination",
1719ef1f84bSDavid du Colombier [Icmp6_ad_prohib]	"comm with destination administratively prohibited",
1729ef1f84bSDavid du Colombier [Icmp6_out_src_scope]	"beyond scope of source address",
1739ef1f84bSDavid du Colombier [Icmp6_adr_unreach]	"address unreachable",
1749ef1f84bSDavid du Colombier [Icmp6_port_unreach]	"port unreachable",
1759ef1f84bSDavid du Colombier [Icmp6_gress_src_fail]	"source address failed ingress/egress policy",
1769ef1f84bSDavid du Colombier [Icmp6_rej_route]	"reject route to destination",
1779ef1f84bSDavid du Colombier [Icmp6_unknown]		"icmp unreachable: unknown code",
1789ef1f84bSDavid du Colombier };
1799ef1f84bSDavid du Colombier 
1809ef1f84bSDavid du Colombier static void icmpkick6(void *x, Block *bp);
1819ef1f84bSDavid du Colombier 
1829ef1f84bSDavid du Colombier static void
icmpcreate6(Conv * c)1839ef1f84bSDavid du Colombier icmpcreate6(Conv *c)
1849ef1f84bSDavid du Colombier {
1859ef1f84bSDavid du Colombier 	c->rq = qopen(64*1024, Qmsg, 0, c);
1869ef1f84bSDavid du Colombier 	c->wq = qbypass(icmpkick6, c);
1879ef1f84bSDavid du Colombier }
1889ef1f84bSDavid du Colombier 
1899ef1f84bSDavid du Colombier static void
set_cksum(Block * bp)1909ef1f84bSDavid du Colombier set_cksum(Block *bp)
1919ef1f84bSDavid du Colombier {
1929ef1f84bSDavid du Colombier 	IPICMP *p = (IPICMP *)(bp->rp);
1939ef1f84bSDavid du Colombier 
1949ef1f84bSDavid du Colombier 	hnputl(p->vcf, 0);	/* borrow IP header as pseudoheader */
1959ef1f84bSDavid du Colombier 	hnputs(p->ploadlen, blocklen(bp) - IP6HDR);
1969ef1f84bSDavid du Colombier 	p->proto = 0;
1979ef1f84bSDavid du Colombier 	p->ttl = ICMPv6;	/* ttl gets set later */
1989ef1f84bSDavid du Colombier 	hnputs(p->cksum, 0);
1999ef1f84bSDavid du Colombier 	hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
2009ef1f84bSDavid du Colombier 	p->proto = ICMPv6;
2019ef1f84bSDavid du Colombier }
2029ef1f84bSDavid du Colombier 
2039ef1f84bSDavid du Colombier static Block *
newIPICMP(int packetlen)2049ef1f84bSDavid du Colombier newIPICMP(int packetlen)
2059ef1f84bSDavid du Colombier {
2069ef1f84bSDavid du Colombier 	Block *nbp;
2079ef1f84bSDavid du Colombier 
2089ef1f84bSDavid du Colombier 	nbp = allocb(packetlen);
2099ef1f84bSDavid du Colombier 	nbp->wp += packetlen;
2109ef1f84bSDavid du Colombier 	memset(nbp->rp, 0, packetlen);
2119ef1f84bSDavid du Colombier 	return nbp;
2129ef1f84bSDavid du Colombier }
2139ef1f84bSDavid du Colombier 
2149ef1f84bSDavid du Colombier void
icmpadvise6(Proto * icmp,Block * bp,char * msg)2159ef1f84bSDavid du Colombier icmpadvise6(Proto *icmp, Block *bp, char *msg)
2169ef1f84bSDavid du Colombier {
2179ef1f84bSDavid du Colombier 	ushort recid;
2189ef1f84bSDavid du Colombier 	Conv **c, *s;
2199ef1f84bSDavid du Colombier 	IPICMP *p;
2209ef1f84bSDavid du Colombier 
2219ef1f84bSDavid du Colombier 	p = (IPICMP *)bp->rp;
2229ef1f84bSDavid du Colombier 	recid = nhgets(p->icmpid);
2239ef1f84bSDavid du Colombier 
2249ef1f84bSDavid du Colombier 	for(c = icmp->conv; *c; c++) {
2259ef1f84bSDavid du Colombier 		s = *c;
2269ef1f84bSDavid du Colombier 		if(s->lport == recid && ipcmp(s->raddr, p->dst) == 0){
2279ef1f84bSDavid du Colombier 			qhangup(s->rq, msg);
2289ef1f84bSDavid du Colombier 			qhangup(s->wq, msg);
2299ef1f84bSDavid du Colombier 			break;
2309ef1f84bSDavid du Colombier 		}
2319ef1f84bSDavid du Colombier 	}
2329ef1f84bSDavid du Colombier 	freeblist(bp);
2339ef1f84bSDavid du Colombier }
2349ef1f84bSDavid du Colombier 
2359ef1f84bSDavid du Colombier static void
icmpkick6(void * x,Block * bp)2369ef1f84bSDavid du Colombier icmpkick6(void *x, Block *bp)
2379ef1f84bSDavid du Colombier {
2389ef1f84bSDavid du Colombier 	uchar laddr[IPaddrlen], raddr[IPaddrlen];
2399ef1f84bSDavid du Colombier 	Conv *c = x;
2409ef1f84bSDavid du Colombier 	IPICMP *p;
2419ef1f84bSDavid du Colombier 	Icmppriv6 *ipriv = c->p->priv;
2429ef1f84bSDavid du Colombier 	Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
2439ef1f84bSDavid du Colombier 
2449ef1f84bSDavid du Colombier 	if(bp == nil)
2459ef1f84bSDavid du Colombier 		return;
2469ef1f84bSDavid du Colombier 
2479ef1f84bSDavid du Colombier 	if(icb->headers==6) {
2489ef1f84bSDavid du Colombier 		/* get user specified addresses */
2499ef1f84bSDavid du Colombier 		bp = pullupblock(bp, ICMP_USEAD6);
2509ef1f84bSDavid du Colombier 		if(bp == nil)
2519ef1f84bSDavid du Colombier 			return;
2529ef1f84bSDavid du Colombier 		bp->rp += 8;
2539ef1f84bSDavid du Colombier 		ipmove(laddr, bp->rp);
2549ef1f84bSDavid du Colombier 		bp->rp += IPaddrlen;
2559ef1f84bSDavid du Colombier 		ipmove(raddr, bp->rp);
2569ef1f84bSDavid du Colombier 		bp->rp += IPaddrlen;
2579ef1f84bSDavid du Colombier 		bp = padblock(bp, IP6HDR);
2589ef1f84bSDavid du Colombier 	}
2599ef1f84bSDavid du Colombier 
2609ef1f84bSDavid du Colombier 	if(blocklen(bp) < IPICMPSZ){
2619ef1f84bSDavid du Colombier 		freeblist(bp);
2629ef1f84bSDavid du Colombier 		return;
2639ef1f84bSDavid du Colombier 	}
2649ef1f84bSDavid du Colombier 	p = (IPICMP *)(bp->rp);
2659ef1f84bSDavid du Colombier 	if(icb->headers == 6) {
2669ef1f84bSDavid du Colombier 		ipmove(p->dst, raddr);
2679ef1f84bSDavid du Colombier 		ipmove(p->src, laddr);
2689ef1f84bSDavid du Colombier 	} else {
2699ef1f84bSDavid du Colombier 		ipmove(p->dst, c->raddr);
2709ef1f84bSDavid du Colombier 		ipmove(p->src, c->laddr);
2719ef1f84bSDavid du Colombier 		hnputs(p->icmpid, c->lport);
2729ef1f84bSDavid du Colombier 	}
2739ef1f84bSDavid du Colombier 
2749ef1f84bSDavid du Colombier 	set_cksum(bp);
2759ef1f84bSDavid du Colombier 	p->vcf[0] = 0x06 << 4;
2769ef1f84bSDavid du Colombier 	if(p->type <= Maxtype6)
2779ef1f84bSDavid du Colombier 		ipriv->out[p->type]++;
2789ef1f84bSDavid du Colombier 	ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil);
2799ef1f84bSDavid du Colombier }
2809ef1f84bSDavid du Colombier 
2819ef1f84bSDavid du Colombier char*
icmpctl6(Conv * c,char ** argv,int argc)2829ef1f84bSDavid du Colombier icmpctl6(Conv *c, char **argv, int argc)
2839ef1f84bSDavid du Colombier {
2849ef1f84bSDavid du Colombier 	Icmpcb6 *icb;
2859ef1f84bSDavid du Colombier 
2869ef1f84bSDavid du Colombier 	icb = (Icmpcb6*) c->ptcl;
2879ef1f84bSDavid du Colombier 	if(argc==1 && strcmp(argv[0], "headers")==0) {
2889ef1f84bSDavid du Colombier 		icb->headers = 6;
2899ef1f84bSDavid du Colombier 		return nil;
2909ef1f84bSDavid du Colombier 	}
2919ef1f84bSDavid du Colombier 	return "unknown control request";
2929ef1f84bSDavid du Colombier }
2939ef1f84bSDavid du Colombier 
2949ef1f84bSDavid du Colombier static void
goticmpkt6(Proto * icmp,Block * bp,int muxkey)2959ef1f84bSDavid du Colombier goticmpkt6(Proto *icmp, Block *bp, int muxkey)
2969ef1f84bSDavid du Colombier {
2979ef1f84bSDavid du Colombier 	ushort recid;
2989ef1f84bSDavid du Colombier 	uchar *addr;
2999ef1f84bSDavid du Colombier 	Conv **c, *s;
3009ef1f84bSDavid du Colombier 	IPICMP *p = (IPICMP *)bp->rp;
3019ef1f84bSDavid du Colombier 
3029ef1f84bSDavid du Colombier 	if(muxkey == 0) {
3039ef1f84bSDavid du Colombier 		recid = nhgets(p->icmpid);
3049ef1f84bSDavid du Colombier 		addr = p->src;
3059ef1f84bSDavid du Colombier 	} else {
3069ef1f84bSDavid du Colombier 		recid = muxkey;
3079ef1f84bSDavid du Colombier 		addr = p->dst;
3089ef1f84bSDavid du Colombier 	}
3099ef1f84bSDavid du Colombier 
3109ef1f84bSDavid du Colombier 	for(c = icmp->conv; *c; c++){
3119ef1f84bSDavid du Colombier 		s = *c;
3129ef1f84bSDavid du Colombier 		if(s->lport == recid && ipcmp(s->raddr, addr) == 0){
3139ef1f84bSDavid du Colombier 			bp = concatblock(bp);
3149ef1f84bSDavid du Colombier 			if(bp != nil)
3159ef1f84bSDavid du Colombier 				qpass(s->rq, bp);
3169ef1f84bSDavid du Colombier 			return;
3179ef1f84bSDavid du Colombier 		}
3189ef1f84bSDavid du Colombier 	}
3199ef1f84bSDavid du Colombier 
3209ef1f84bSDavid du Colombier 	freeblist(bp);
3219ef1f84bSDavid du Colombier }
3229ef1f84bSDavid du Colombier 
3239ef1f84bSDavid du Colombier static Block *
mkechoreply6(Block * bp,Ipifc * ifc)3249ef1f84bSDavid du Colombier mkechoreply6(Block *bp, Ipifc *ifc)
3259ef1f84bSDavid du Colombier {
3269ef1f84bSDavid du Colombier 	uchar addr[IPaddrlen];
3279ef1f84bSDavid du Colombier 	IPICMP *p = (IPICMP *)(bp->rp);
3289ef1f84bSDavid du Colombier 
3299ef1f84bSDavid du Colombier 	ipmove(addr, p->src);
3309ef1f84bSDavid du Colombier 	if(!isv6mcast(p->dst))
3319ef1f84bSDavid du Colombier 		ipmove(p->src, p->dst);
3329ef1f84bSDavid du Colombier 	else if (!ipv6anylocal(ifc, p->src))
3339ef1f84bSDavid du Colombier 		return nil;
3349ef1f84bSDavid du Colombier 	ipmove(p->dst, addr);
3359ef1f84bSDavid du Colombier 	p->type = EchoReplyV6;
3369ef1f84bSDavid du Colombier 	set_cksum(bp);
3379ef1f84bSDavid du Colombier 	return bp;
3389ef1f84bSDavid du Colombier }
3399ef1f84bSDavid du Colombier 
3409ef1f84bSDavid du Colombier /*
3419ef1f84bSDavid du Colombier  * sends out an ICMPv6 neighbor solicitation
3429ef1f84bSDavid du Colombier  *	suni == SRC_UNSPEC or SRC_UNI,
3439ef1f84bSDavid du Colombier  *	tuni == TARG_MULTI => multicast for address resolution,
3449ef1f84bSDavid du Colombier  *	and tuni == TARG_UNI => neighbor reachability.
3459ef1f84bSDavid du Colombier  */
3469ef1f84bSDavid du Colombier extern void
icmpns(Fs * f,uchar * src,int suni,uchar * targ,int tuni,uchar * mac)3479ef1f84bSDavid du Colombier icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
3489ef1f84bSDavid du Colombier {
3499ef1f84bSDavid du Colombier 	Block *nbp;
3509ef1f84bSDavid du Colombier 	Ndpkt *np;
3519ef1f84bSDavid du Colombier 	Proto *icmp = f->t2p[ICMPv6];
3529ef1f84bSDavid du Colombier 	Icmppriv6 *ipriv = icmp->priv;
3539ef1f84bSDavid du Colombier 
3549ef1f84bSDavid du Colombier 	nbp = newIPICMP(NDPKTSZ);
3559ef1f84bSDavid du Colombier 	np = (Ndpkt*) nbp->rp;
3569ef1f84bSDavid du Colombier 
3579ef1f84bSDavid du Colombier 	if(suni == SRC_UNSPEC)
3589ef1f84bSDavid du Colombier 		memmove(np->src, v6Unspecified, IPaddrlen);
3599ef1f84bSDavid du Colombier 	else
3609ef1f84bSDavid du Colombier 		memmove(np->src, src, IPaddrlen);
3619ef1f84bSDavid du Colombier 
3629ef1f84bSDavid du Colombier 	if(tuni == TARG_UNI)
3639ef1f84bSDavid du Colombier 		memmove(np->dst, targ, IPaddrlen);
3649ef1f84bSDavid du Colombier 	else
3659ef1f84bSDavid du Colombier 		ipv62smcast(np->dst, targ);
3669ef1f84bSDavid du Colombier 
3679ef1f84bSDavid du Colombier 	np->type = NbrSolicit;
3689ef1f84bSDavid du Colombier 	np->code = 0;
3699ef1f84bSDavid du Colombier 	memmove(np->target, targ, IPaddrlen);
3709ef1f84bSDavid du Colombier 	if(suni != SRC_UNSPEC) {
3719ef1f84bSDavid du Colombier 		np->otype = SRC_LLADDR;
3729ef1f84bSDavid du Colombier 		np->olen = 1;		/* 1+1+6 = 8 = 1 8-octet */
3739ef1f84bSDavid du Colombier 		memmove(np->lnaddr, mac, sizeof(np->lnaddr));
3749ef1f84bSDavid du Colombier 	} else
3759ef1f84bSDavid du Colombier 		nbp->wp -= NDPKTSZ - NDISCSZ;
3769ef1f84bSDavid du Colombier 
3779ef1f84bSDavid du Colombier 	set_cksum(nbp);
3789ef1f84bSDavid du Colombier 	np = (Ndpkt*)nbp->rp;
3799ef1f84bSDavid du Colombier 	np->ttl = HOP_LIMIT;
3809ef1f84bSDavid du Colombier 	np->vcf[0] = 0x06 << 4;
3819ef1f84bSDavid du Colombier 	ipriv->out[NbrSolicit]++;
3829ef1f84bSDavid du Colombier 	netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
3839ef1f84bSDavid du Colombier 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
3849ef1f84bSDavid du Colombier }
3859ef1f84bSDavid du Colombier 
3869ef1f84bSDavid du Colombier /*
3879ef1f84bSDavid du Colombier  * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
3889ef1f84bSDavid du Colombier  */
3899ef1f84bSDavid du Colombier extern void
icmpna(Fs * f,uchar * src,uchar * dst,uchar * targ,uchar * mac,uchar flags)3909ef1f84bSDavid du Colombier icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
3919ef1f84bSDavid du Colombier {
3929ef1f84bSDavid du Colombier 	Block *nbp;
3939ef1f84bSDavid du Colombier 	Ndpkt *np;
3949ef1f84bSDavid du Colombier 	Proto *icmp = f->t2p[ICMPv6];
3959ef1f84bSDavid du Colombier 	Icmppriv6 *ipriv = icmp->priv;
3969ef1f84bSDavid du Colombier 
3979ef1f84bSDavid du Colombier 	nbp = newIPICMP(NDPKTSZ);
3989ef1f84bSDavid du Colombier 	np = (Ndpkt*)nbp->rp;
3999ef1f84bSDavid du Colombier 
4009ef1f84bSDavid du Colombier 	memmove(np->src, src, IPaddrlen);
4019ef1f84bSDavid du Colombier 	memmove(np->dst, dst, IPaddrlen);
4029ef1f84bSDavid du Colombier 
4039ef1f84bSDavid du Colombier 	np->type = NbrAdvert;
4049ef1f84bSDavid du Colombier 	np->code = 0;
4059ef1f84bSDavid du Colombier 	np->icmpid[0] = flags;
4069ef1f84bSDavid du Colombier 	memmove(np->target, targ, IPaddrlen);
4079ef1f84bSDavid du Colombier 
4089ef1f84bSDavid du Colombier 	np->otype = TARGET_LLADDR;
4099ef1f84bSDavid du Colombier 	np->olen = 1;
4109ef1f84bSDavid du Colombier 	memmove(np->lnaddr, mac, sizeof(np->lnaddr));
4119ef1f84bSDavid du Colombier 
4129ef1f84bSDavid du Colombier 	set_cksum(nbp);
4139ef1f84bSDavid du Colombier 	np = (Ndpkt*) nbp->rp;
4149ef1f84bSDavid du Colombier 	np->ttl = HOP_LIMIT;
4159ef1f84bSDavid du Colombier 	np->vcf[0] = 0x06 << 4;
4169ef1f84bSDavid du Colombier 	ipriv->out[NbrAdvert]++;
4179ef1f84bSDavid du Colombier 	netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
4189ef1f84bSDavid du Colombier 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
4199ef1f84bSDavid du Colombier }
4209ef1f84bSDavid du Colombier 
421*a3323688SDavid du Colombier /* if free is true, freeblist(bp) before return. */
4229ef1f84bSDavid du Colombier extern void
icmphostunr(Fs * f,Ipifc * ifc,Block * bp,int code,int free)4239ef1f84bSDavid du Colombier icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free)
4249ef1f84bSDavid du Colombier {
425*a3323688SDavid du Colombier 	int osz, sz;
4269ef1f84bSDavid du Colombier 	Block *nbp;
4279ef1f84bSDavid du Colombier 	IPICMP *np;
428*a3323688SDavid du Colombier 	Icmppriv6 *ipriv;
4299ef1f84bSDavid du Colombier 	Ip6hdr *p;
430*a3323688SDavid du Colombier 	Proto *icmp;
4319ef1f84bSDavid du Colombier 
432*a3323688SDavid du Colombier 	osz = BLEN(bp);
433*a3323688SDavid du Colombier 	sz = MIN(IPICMPSZ + osz, v6MINTU);
434*a3323688SDavid du Colombier 	icmp = f->t2p[ICMPv6];
435*a3323688SDavid du Colombier 	ipriv = icmp->priv;
4369ef1f84bSDavid du Colombier 	p = (Ip6hdr *)bp->rp;
4379ef1f84bSDavid du Colombier 	if(isv6mcast(p->src))
438*a3323688SDavid du Colombier 		goto freebl;
4399ef1f84bSDavid du Colombier 	nbp = newIPICMP(sz);
4409ef1f84bSDavid du Colombier 	np = (IPICMP *)nbp->rp;
4419ef1f84bSDavid du Colombier 
4429ef1f84bSDavid du Colombier 	rlock(ifc);
443*a3323688SDavid du Colombier 	if(!ipv6anylocal(ifc, np->src)){
4449ef1f84bSDavid du Colombier 		netlog(f, Logicmp, "icmphostunr fail -> src %I dst %I\n",
4459ef1f84bSDavid du Colombier 			p->src, p->dst);
446*a3323688SDavid du Colombier 		runlock(ifc);
4479ef1f84bSDavid du Colombier 		freeblist(nbp);
448*a3323688SDavid du Colombier 		goto freebl;
4499ef1f84bSDavid du Colombier 	}
4509ef1f84bSDavid du Colombier 
451*a3323688SDavid du Colombier 	netlog(f, Logicmp, "send icmphostunr -> src %I dst %I\n", p->src, p->dst);
4529ef1f84bSDavid du Colombier 	memmove(np->dst, p->src, IPaddrlen);
4539ef1f84bSDavid du Colombier 	np->type = UnreachableV6;
4549ef1f84bSDavid du Colombier 	np->code = code;
4559ef1f84bSDavid du Colombier 	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
4569ef1f84bSDavid du Colombier 	set_cksum(nbp);
4579ef1f84bSDavid du Colombier 	np->ttl = HOP_LIMIT;
4589ef1f84bSDavid du Colombier 	np->vcf[0] = 0x06 << 4;
4599ef1f84bSDavid du Colombier 	ipriv->out[UnreachableV6]++;
4609ef1f84bSDavid du Colombier 
4619ef1f84bSDavid du Colombier 	if(free)
4629ef1f84bSDavid du Colombier 		ipiput6(f, ifc, nbp);
463*a3323688SDavid du Colombier 	else
4649ef1f84bSDavid du Colombier 		ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
4659ef1f84bSDavid du Colombier 	runlock(ifc);
466*a3323688SDavid du Colombier freebl:
467*a3323688SDavid du Colombier 	if(free)
4689ef1f84bSDavid du Colombier 		freeblist(bp);
4699ef1f84bSDavid du Colombier }
4709ef1f84bSDavid du Colombier 
4719ef1f84bSDavid du Colombier extern void
icmpttlexceeded6(Fs * f,Ipifc * ifc,Block * bp)4729ef1f84bSDavid du Colombier icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
4739ef1f84bSDavid du Colombier {
4749ef1f84bSDavid du Colombier 	int osz = BLEN(bp);
4759ef1f84bSDavid du Colombier 	int sz = MIN(IPICMPSZ + osz, v6MINTU);
4769ef1f84bSDavid du Colombier 	Block *nbp;
4779ef1f84bSDavid du Colombier 	IPICMP *np;
4789ef1f84bSDavid du Colombier 	Ip6hdr *p;
4799ef1f84bSDavid du Colombier 	Proto *icmp = f->t2p[ICMPv6];
4809ef1f84bSDavid du Colombier 	Icmppriv6 *ipriv = icmp->priv;
4819ef1f84bSDavid du Colombier 
4829ef1f84bSDavid du Colombier 	p = (Ip6hdr *)bp->rp;
4839ef1f84bSDavid du Colombier 	if(isv6mcast(p->src))
4849ef1f84bSDavid du Colombier 		return;
4859ef1f84bSDavid du Colombier 
4869ef1f84bSDavid du Colombier 	nbp = newIPICMP(sz);
4879ef1f84bSDavid du Colombier 	np = (IPICMP *) nbp->rp;
4889ef1f84bSDavid du Colombier 	if(ipv6anylocal(ifc, np->src))
4899ef1f84bSDavid du Colombier 		netlog(f, Logicmp, "send icmpttlexceeded6 -> src %I dst %I\n",
4909ef1f84bSDavid du Colombier 			p->src, p->dst);
4919ef1f84bSDavid du Colombier 	else {
4929ef1f84bSDavid du Colombier 		netlog(f, Logicmp, "icmpttlexceeded6 fail -> src %I dst %I\n",
4939ef1f84bSDavid du Colombier 			p->src, p->dst);
4949ef1f84bSDavid du Colombier 		return;
4959ef1f84bSDavid du Colombier 	}
4969ef1f84bSDavid du Colombier 
4979ef1f84bSDavid du Colombier 	memmove(np->dst, p->src, IPaddrlen);
4989ef1f84bSDavid du Colombier 	np->type = TimeExceedV6;
4999ef1f84bSDavid du Colombier 	np->code = 0;
5009ef1f84bSDavid du Colombier 	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
5019ef1f84bSDavid du Colombier 	set_cksum(nbp);
5029ef1f84bSDavid du Colombier 	np->ttl = HOP_LIMIT;
5039ef1f84bSDavid du Colombier 	np->vcf[0] = 0x06 << 4;
5049ef1f84bSDavid du Colombier 	ipriv->out[TimeExceedV6]++;
5059ef1f84bSDavid du Colombier 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
5069ef1f84bSDavid du Colombier }
5079ef1f84bSDavid du Colombier 
5089ef1f84bSDavid du Colombier extern void
icmppkttoobig6(Fs * f,Ipifc * ifc,Block * bp)5099ef1f84bSDavid du Colombier icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
5109ef1f84bSDavid du Colombier {
5119ef1f84bSDavid du Colombier 	int osz = BLEN(bp);
5129ef1f84bSDavid du Colombier 	int sz = MIN(IPICMPSZ + osz, v6MINTU);
5139ef1f84bSDavid du Colombier 	Block *nbp;
5149ef1f84bSDavid du Colombier 	IPICMP *np;
5159ef1f84bSDavid du Colombier 	Ip6hdr *p;
5169ef1f84bSDavid du Colombier 	Proto *icmp = f->t2p[ICMPv6];
5179ef1f84bSDavid du Colombier 	Icmppriv6 *ipriv = icmp->priv;
5189ef1f84bSDavid du Colombier 
5199ef1f84bSDavid du Colombier 	p = (Ip6hdr *)bp->rp;
5209ef1f84bSDavid du Colombier 	if(isv6mcast(p->src))
5219ef1f84bSDavid du Colombier 		return;
5229ef1f84bSDavid du Colombier 
5239ef1f84bSDavid du Colombier 	nbp = newIPICMP(sz);
5249ef1f84bSDavid du Colombier 	np = (IPICMP *)nbp->rp;
5259ef1f84bSDavid du Colombier 	if(ipv6anylocal(ifc, np->src))
5269ef1f84bSDavid du Colombier 		netlog(f, Logicmp, "send icmppkttoobig6 -> src %I dst %I\n",
5279ef1f84bSDavid du Colombier 			p->src, p->dst);
5289ef1f84bSDavid du Colombier 	else {
5299ef1f84bSDavid du Colombier 		netlog(f, Logicmp, "icmppkttoobig6 fail -> src %I dst %I\n",
5309ef1f84bSDavid du Colombier 			p->src, p->dst);
5319ef1f84bSDavid du Colombier 		return;
5329ef1f84bSDavid du Colombier 	}
5339ef1f84bSDavid du Colombier 
5349ef1f84bSDavid du Colombier 	memmove(np->dst, p->src, IPaddrlen);
5359ef1f84bSDavid du Colombier 	np->type = PacketTooBigV6;
5369ef1f84bSDavid du Colombier 	np->code = 0;
5379ef1f84bSDavid du Colombier 	hnputl(np->icmpid, ifc->maxtu - ifc->medium->hsize);
5389ef1f84bSDavid du Colombier 	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
5399ef1f84bSDavid du Colombier 	set_cksum(nbp);
5409ef1f84bSDavid du Colombier 	np->ttl = HOP_LIMIT;
5419ef1f84bSDavid du Colombier 	np->vcf[0] = 0x06 << 4;
5429ef1f84bSDavid du Colombier 	ipriv->out[PacketTooBigV6]++;
5439ef1f84bSDavid du Colombier 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
5449ef1f84bSDavid du Colombier }
5459ef1f84bSDavid du Colombier 
5469ef1f84bSDavid du Colombier /*
5479ef1f84bSDavid du Colombier  * RFC 2461, pages 39-40, pages 57-58.
5489ef1f84bSDavid du Colombier  */
5499ef1f84bSDavid du Colombier static int
valid(Proto * icmp,Ipifc *,Block * bp,Icmppriv6 * ipriv)550*a3323688SDavid du Colombier valid(Proto *icmp, Ipifc *, Block *bp, Icmppriv6 *ipriv)
5519ef1f84bSDavid du Colombier {
552*a3323688SDavid du Colombier 	int sz, osz, unsp, n, ttl, iplen, pktsz;
553*a3323688SDavid du Colombier 	uchar *packet;
554*a3323688SDavid du Colombier 	IPICMP *p;
5559ef1f84bSDavid du Colombier 	Ndpkt *np;
5569ef1f84bSDavid du Colombier 
5579ef1f84bSDavid du Colombier 	n = blocklen(bp);
5589ef1f84bSDavid du Colombier 	if(n < IPICMPSZ) {
5599ef1f84bSDavid du Colombier 		ipriv->stats[HlenErrs6]++;
5609ef1f84bSDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
5619ef1f84bSDavid du Colombier 		goto err;
5629ef1f84bSDavid du Colombier 	}
5639ef1f84bSDavid du Colombier 
564*a3323688SDavid du Colombier 	packet = bp->rp;
565*a3323688SDavid du Colombier 	p = (IPICMP *)packet;
566*a3323688SDavid du Colombier 	pktsz = BLEN(bp);
5679ef1f84bSDavid du Colombier 	iplen = nhgets(p->ploadlen);
5689ef1f84bSDavid du Colombier 	if(iplen > n - IP6HDR) {
5699ef1f84bSDavid du Colombier 		ipriv->stats[LenErrs6]++;
5709ef1f84bSDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
5719ef1f84bSDavid du Colombier 		goto err;
5729ef1f84bSDavid du Colombier 	}
5739ef1f84bSDavid du Colombier 
5749ef1f84bSDavid du Colombier 	/* Rather than construct explicit pseudoheader, overwrite IPv6 header */
5759ef1f84bSDavid du Colombier 	if(p->proto != ICMPv6) {
5769ef1f84bSDavid du Colombier 		/* This code assumes no extension headers!!! */
5779ef1f84bSDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp error: extension header\n");
5789ef1f84bSDavid du Colombier 		goto err;
5799ef1f84bSDavid du Colombier 	}
5809ef1f84bSDavid du Colombier 	memset(packet, 0, 4);
5819ef1f84bSDavid du Colombier 	ttl = p->ttl;
5829ef1f84bSDavid du Colombier 	p->ttl = p->proto;
5839ef1f84bSDavid du Colombier 	p->proto = 0;
5849ef1f84bSDavid du Colombier 	if(ptclcsum(bp, 0, iplen + IP6HDR)) {
5859ef1f84bSDavid du Colombier 		ipriv->stats[CsumErrs6]++;
5869ef1f84bSDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp checksum error\n");
5879ef1f84bSDavid du Colombier 		goto err;
5889ef1f84bSDavid du Colombier 	}
5899ef1f84bSDavid du Colombier 	p->proto = p->ttl;
5909ef1f84bSDavid du Colombier 	p->ttl = ttl;
5919ef1f84bSDavid du Colombier 
5929ef1f84bSDavid du Colombier 	/* additional tests for some pkt types */
593*a3323688SDavid du Colombier 	if (p->type != NbrSolicit   && p->type != NbrAdvert &&
594*a3323688SDavid du Colombier 	    p->type != RouterAdvert && p->type != RouterSolicit &&
595*a3323688SDavid du Colombier 	    p->type != RedirectV6)
596*a3323688SDavid du Colombier 		return 1;	/* TODO: unknown, presumed valid; why? */
5979ef1f84bSDavid du Colombier 	if(p->ttl != HOP_LIMIT) {
5989ef1f84bSDavid du Colombier 		ipriv->stats[HoplimErrs6]++;
5999ef1f84bSDavid du Colombier 		goto err;
6009ef1f84bSDavid du Colombier 	}
6019ef1f84bSDavid du Colombier 	if(p->code != 0) {
6029ef1f84bSDavid du Colombier 		ipriv->stats[IcmpCodeErrs6]++;
6039ef1f84bSDavid du Colombier 		goto err;
6049ef1f84bSDavid du Colombier 	}
6059ef1f84bSDavid du Colombier 
6069ef1f84bSDavid du Colombier 	switch (p->type) {
6079ef1f84bSDavid du Colombier 	case NbrSolicit:
6089ef1f84bSDavid du Colombier 	case NbrAdvert:
6099ef1f84bSDavid du Colombier 		np = (Ndpkt*) p;
6109ef1f84bSDavid du Colombier 		if(isv6mcast(np->target)) {
6119ef1f84bSDavid du Colombier 			ipriv->stats[TargetErrs6]++;
6129ef1f84bSDavid du Colombier 			goto err;
6139ef1f84bSDavid du Colombier 		}
6149ef1f84bSDavid du Colombier 		if(optexsts(np) && np->olen == 0) {
6159ef1f84bSDavid du Colombier 			ipriv->stats[OptlenErrs6]++;
6169ef1f84bSDavid du Colombier 			goto err;
6179ef1f84bSDavid du Colombier 		}
618*a3323688SDavid du Colombier 		if (p->type == NbrSolicit && ipcmp(np->src, v6Unspecified) == 0)
6199ef1f84bSDavid du Colombier 			if(!issmcast(np->dst) || optexsts(np)) {
6209ef1f84bSDavid du Colombier 				ipriv->stats[AddrmxpErrs6]++;
6219ef1f84bSDavid du Colombier 				goto err;
6229ef1f84bSDavid du Colombier 			}
623*a3323688SDavid du Colombier 		if(p->type == NbrAdvert && isv6mcast(np->dst) &&
624*a3323688SDavid du Colombier 		    nhgets(np->icmpid) & Sflag){
6259ef1f84bSDavid du Colombier 			ipriv->stats[AddrmxpErrs6]++;
6269ef1f84bSDavid du Colombier 			goto err;
6279ef1f84bSDavid du Colombier 		}
6289ef1f84bSDavid du Colombier 		break;
6299ef1f84bSDavid du Colombier 	case RouterAdvert:
6309ef1f84bSDavid du Colombier 		if(pktsz - IP6HDR < 16) {
6319ef1f84bSDavid du Colombier 			ipriv->stats[HlenErrs6]++;
6329ef1f84bSDavid du Colombier 			goto err;
6339ef1f84bSDavid du Colombier 		}
6349ef1f84bSDavid du Colombier 		if(!islinklocal(p->src)) {
6359ef1f84bSDavid du Colombier 			ipriv->stats[RouterAddrErrs6]++;
6369ef1f84bSDavid du Colombier 			goto err;
6379ef1f84bSDavid du Colombier 		}
638*a3323688SDavid du Colombier 		for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
6399ef1f84bSDavid du Colombier 			osz = packet[sz+1];
6409ef1f84bSDavid du Colombier 			if(osz <= 0) {
6419ef1f84bSDavid du Colombier 				ipriv->stats[OptlenErrs6]++;
6429ef1f84bSDavid du Colombier 				goto err;
6439ef1f84bSDavid du Colombier 			}
6449ef1f84bSDavid du Colombier 		}
6459ef1f84bSDavid du Colombier 		break;
6469ef1f84bSDavid du Colombier 	case RouterSolicit:
6479ef1f84bSDavid du Colombier 		if(pktsz - IP6HDR < 8) {
6489ef1f84bSDavid du Colombier 			ipriv->stats[HlenErrs6]++;
6499ef1f84bSDavid du Colombier 			goto err;
6509ef1f84bSDavid du Colombier 		}
6519ef1f84bSDavid du Colombier 		unsp = (ipcmp(p->src, v6Unspecified) == 0);
652*a3323688SDavid du Colombier 		for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
6539ef1f84bSDavid du Colombier 			osz = packet[sz+1];
654*a3323688SDavid du Colombier 			if(osz <= 0 || (unsp && packet[sz] == SRC_LLADDR)) {
6559ef1f84bSDavid du Colombier 				ipriv->stats[OptlenErrs6]++;
6569ef1f84bSDavid du Colombier 				goto err;
6579ef1f84bSDavid du Colombier 			}
6589ef1f84bSDavid du Colombier 		}
6599ef1f84bSDavid du Colombier 		break;
6609ef1f84bSDavid du Colombier 	case RedirectV6:
661*a3323688SDavid du Colombier 		/* TODO: fill in */
6629ef1f84bSDavid du Colombier 		break;
6639ef1f84bSDavid du Colombier 	default:
6649ef1f84bSDavid du Colombier 		goto err;
6659ef1f84bSDavid du Colombier 	}
6669ef1f84bSDavid du Colombier 	return 1;
6679ef1f84bSDavid du Colombier err:
6689ef1f84bSDavid du Colombier 	ipriv->stats[InErrors6]++;
6699ef1f84bSDavid du Colombier 	return 0;
6709ef1f84bSDavid du Colombier }
6719ef1f84bSDavid du Colombier 
6729ef1f84bSDavid du Colombier static int
targettype(Fs * f,Ipifc * ifc,uchar * target)6739ef1f84bSDavid du Colombier targettype(Fs *f, Ipifc *ifc, uchar *target)
6749ef1f84bSDavid du Colombier {
6759ef1f84bSDavid du Colombier 	Iplifc *lifc;
6769ef1f84bSDavid du Colombier 	int t;
6779ef1f84bSDavid du Colombier 
6789ef1f84bSDavid du Colombier 	rlock(ifc);
6799ef1f84bSDavid du Colombier 	if(ipproxyifc(f, ifc, target)) {
6809ef1f84bSDavid du Colombier 		runlock(ifc);
6819ef1f84bSDavid du Colombier 		return Tuniproxy;
6829ef1f84bSDavid du Colombier 	}
6839ef1f84bSDavid du Colombier 
6849ef1f84bSDavid du Colombier 	for(lifc = ifc->lifc; lifc; lifc = lifc->next)
6859ef1f84bSDavid du Colombier 		if(ipcmp(lifc->local, target) == 0) {
6869ef1f84bSDavid du Colombier 			t = (lifc->tentative)? Tunitent: Tunirany;
6879ef1f84bSDavid du Colombier 			runlock(ifc);
6889ef1f84bSDavid du Colombier 			return t;
6899ef1f84bSDavid du Colombier 		}
6909ef1f84bSDavid du Colombier 
6919ef1f84bSDavid du Colombier 	runlock(ifc);
6929ef1f84bSDavid du Colombier 	return 0;
6939ef1f84bSDavid du Colombier }
6949ef1f84bSDavid du Colombier 
695*a3323688SDavid du Colombier /* bp needs to be freed with freeblist or passed on. */
6969ef1f84bSDavid du Colombier static void
icmpiput6(Proto * icmp,Ipifc * ipifc,Block * bp)6979ef1f84bSDavid du Colombier icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
6989ef1f84bSDavid du Colombier {
699*a3323688SDavid du Colombier 	int type;
7009ef1f84bSDavid du Colombier 	char *msg, m2[128];
7019ef1f84bSDavid du Colombier 	uchar pktflags;
702*a3323688SDavid du Colombier 	uchar *packet, *src;
7039ef1f84bSDavid du Colombier 	uchar lsrc[IPaddrlen];
7049ef1f84bSDavid du Colombier 	Block *r;
705*a3323688SDavid du Colombier 	IPICMP *p;
706*a3323688SDavid du Colombier 	Icmppriv6 *ipriv;
7079ef1f84bSDavid du Colombier 	Iplifc *lifc;
7089ef1f84bSDavid du Colombier 	Ndpkt* np;
7099ef1f84bSDavid du Colombier 	Proto *pr;
7109ef1f84bSDavid du Colombier 
711*a3323688SDavid du Colombier 	packet = bp->rp;
712*a3323688SDavid du Colombier 	p = (IPICMP *)packet;
713*a3323688SDavid du Colombier 	type = p->type;
714*a3323688SDavid du Colombier 	ipriv = icmp->priv;
715*a3323688SDavid du Colombier 	if(!valid(icmp, ipifc, bp, ipriv) || type > Maxtype6)
7169ef1f84bSDavid du Colombier 		goto raise;
7179ef1f84bSDavid du Colombier 
718*a3323688SDavid du Colombier 	ipriv->in[type]++;
719*a3323688SDavid du Colombier 	switch(type) {
7209ef1f84bSDavid du Colombier 	case EchoRequestV6:
721*a3323688SDavid du Colombier 		bp = concatblock(bp);
7229ef1f84bSDavid du Colombier 		r = mkechoreply6(bp, ipifc);
7239ef1f84bSDavid du Colombier 		if(r == nil)
7249ef1f84bSDavid du Colombier 			goto raise;
7259ef1f84bSDavid du Colombier 		ipriv->out[EchoReply]++;
7269ef1f84bSDavid du Colombier 		ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
7279ef1f84bSDavid du Colombier 		break;
7289ef1f84bSDavid du Colombier 	case UnreachableV6:
7299ef1f84bSDavid du Colombier 		if(p->code >= nelem(unreachcode))
7309ef1f84bSDavid du Colombier 			msg = unreachcode[Icmp6_unknown];
7319ef1f84bSDavid du Colombier 		else
7329ef1f84bSDavid du Colombier 			msg = unreachcode[p->code];
7339ef1f84bSDavid du Colombier 
7349ef1f84bSDavid du Colombier 		bp->rp += IPICMPSZ;
7359ef1f84bSDavid du Colombier 		if(blocklen(bp) < 8){
7369ef1f84bSDavid du Colombier 			ipriv->stats[LenErrs6]++;
7379ef1f84bSDavid du Colombier 			goto raise;
7389ef1f84bSDavid du Colombier 		}
7399ef1f84bSDavid du Colombier 		p = (IPICMP *)bp->rp;
7409ef1f84bSDavid du Colombier 		pr = Fsrcvpcolx(icmp->f, p->proto);
7419ef1f84bSDavid du Colombier 		if(pr != nil && pr->advise != nil) {
7429ef1f84bSDavid du Colombier 			(*pr->advise)(pr, bp, msg);
7439ef1f84bSDavid du Colombier 			return;
7449ef1f84bSDavid du Colombier 		}
7459ef1f84bSDavid du Colombier 
7469ef1f84bSDavid du Colombier 		bp->rp -= IPICMPSZ;
7479ef1f84bSDavid du Colombier 		goticmpkt6(icmp, bp, 0);
7489ef1f84bSDavid du Colombier 		break;
7499ef1f84bSDavid du Colombier 	case TimeExceedV6:
7509ef1f84bSDavid du Colombier 		if(p->code == 0){
751*a3323688SDavid du Colombier 			snprint(m2, sizeof m2, "ttl exceeded at %I", p->src);
7529ef1f84bSDavid du Colombier 			bp->rp += IPICMPSZ;
7539ef1f84bSDavid du Colombier 			if(blocklen(bp) < 8){
7549ef1f84bSDavid du Colombier 				ipriv->stats[LenErrs6]++;
7559ef1f84bSDavid du Colombier 				goto raise;
7569ef1f84bSDavid du Colombier 			}
7579ef1f84bSDavid du Colombier 			p = (IPICMP *)bp->rp;
7589ef1f84bSDavid du Colombier 			pr = Fsrcvpcolx(icmp->f, p->proto);
7599ef1f84bSDavid du Colombier 			if(pr && pr->advise) {
7609ef1f84bSDavid du Colombier 				(*pr->advise)(pr, bp, m2);
7619ef1f84bSDavid du Colombier 				return;
7629ef1f84bSDavid du Colombier 			}
7639ef1f84bSDavid du Colombier 			bp->rp -= IPICMPSZ;
7649ef1f84bSDavid du Colombier 		}
7659ef1f84bSDavid du Colombier 		goticmpkt6(icmp, bp, 0);
7669ef1f84bSDavid du Colombier 		break;
7679ef1f84bSDavid du Colombier 	case RouterAdvert:
7689ef1f84bSDavid du Colombier 	case RouterSolicit:
7699ef1f84bSDavid du Colombier 		/* using lsrc as a temp, munge hdr for goticmp6 */
7709ef1f84bSDavid du Colombier 		if (0) {
7719ef1f84bSDavid du Colombier 			memmove(lsrc, p->src, IPaddrlen);
7729ef1f84bSDavid du Colombier 			memmove(p->src, p->dst, IPaddrlen);
7739ef1f84bSDavid du Colombier 			memmove(p->dst, lsrc, IPaddrlen);
7749ef1f84bSDavid du Colombier 		}
775*a3323688SDavid du Colombier 		goticmpkt6(icmp, bp, type);
7769ef1f84bSDavid du Colombier 		break;
7779ef1f84bSDavid du Colombier 	case NbrSolicit:
778*a3323688SDavid du Colombier 		np = (Ndpkt*)p;			/* within bp */
7799ef1f84bSDavid du Colombier 		pktflags = 0;
7809ef1f84bSDavid du Colombier 		switch (targettype(icmp->f, ipifc, np->target)) {
7819ef1f84bSDavid du Colombier 		case Tunirany:
7829ef1f84bSDavid du Colombier 			pktflags |= Oflag;
7839ef1f84bSDavid du Colombier 			/* fall through */
7849ef1f84bSDavid du Colombier 		case Tuniproxy:
7859ef1f84bSDavid du Colombier 			if(ipcmp(np->src, v6Unspecified) != 0) {
7869ef1f84bSDavid du Colombier 				arpenter(icmp->f, V6, np->src, np->lnaddr,
7879ef1f84bSDavid du Colombier 					8*np->olen-2, 0);
7889ef1f84bSDavid du Colombier 				pktflags |= Sflag;
7899ef1f84bSDavid du Colombier 			}
790*a3323688SDavid du Colombier 			if(ipv6local(ipifc, lsrc)) {
791*a3323688SDavid du Colombier 				src = np->src;
792*a3323688SDavid du Colombier 				if(ipcmp(src, v6Unspecified) == 0)
793*a3323688SDavid du Colombier 					src = v6allnodesL;
794*a3323688SDavid du Colombier 				icmpna(icmp->f, lsrc, src, np->target,
795*a3323688SDavid du Colombier 					ipifc->mac, pktflags);
7969ef1f84bSDavid du Colombier 			}
7979ef1f84bSDavid du Colombier 			break;
798*a3323688SDavid du Colombier 		case Tunitent:
799*a3323688SDavid du Colombier 			/*
800*a3323688SDavid du Colombier 			 * not clear what needs to be done.  send up
801*a3323688SDavid du Colombier 			 * an icmp mesg saying `don't use this address'?
802*a3323688SDavid du Colombier 			 */
803*a3323688SDavid du Colombier 			break;
804*a3323688SDavid du Colombier 		}
805*a3323688SDavid du Colombier 		freeblist(bp);
806*a3323688SDavid du Colombier 		break;
8079ef1f84bSDavid du Colombier 	case NbrAdvert:
8089ef1f84bSDavid du Colombier 		/*
8099ef1f84bSDavid du Colombier 		 * if the target address matches one of the local interface
8109ef1f84bSDavid du Colombier 		 * addresses and the local interface address has tentative bit
8119ef1f84bSDavid du Colombier 		 * set, insert into ARP table. this is so the duplicate address
8129ef1f84bSDavid du Colombier 		 * detection part of ipconfig can discover duplication through
8139ef1f84bSDavid du Colombier 		 * the arp table.
8149ef1f84bSDavid du Colombier 		 */
815*a3323688SDavid du Colombier 		np = (Ndpkt*)p;			/* within bp */
8169ef1f84bSDavid du Colombier 		lifc = iplocalonifc(ipifc, np->target);
8179ef1f84bSDavid du Colombier 		arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2,
818*a3323688SDavid du Colombier 			lifc && lifc->tentative);
8199ef1f84bSDavid du Colombier 		freeblist(bp);
8209ef1f84bSDavid du Colombier 		break;
8219ef1f84bSDavid du Colombier 	case PacketTooBigV6:
8229ef1f84bSDavid du Colombier 	default:
8239ef1f84bSDavid du Colombier 		goticmpkt6(icmp, bp, 0);
8249ef1f84bSDavid du Colombier 		break;
8259ef1f84bSDavid du Colombier 	}
8269ef1f84bSDavid du Colombier 	return;
827*a3323688SDavid du Colombier 
8289ef1f84bSDavid du Colombier raise:
8299ef1f84bSDavid du Colombier 	freeblist(bp);
8309ef1f84bSDavid du Colombier }
8319ef1f84bSDavid du Colombier 
8329ef1f84bSDavid du Colombier int
icmpstats6(Proto * icmp6,char * buf,int len)8339ef1f84bSDavid du Colombier icmpstats6(Proto *icmp6, char *buf, int len)
8349ef1f84bSDavid du Colombier {
8359ef1f84bSDavid du Colombier 	Icmppriv6 *priv;
8369ef1f84bSDavid du Colombier 	char *p, *e;
8379ef1f84bSDavid du Colombier 	int i;
8389ef1f84bSDavid du Colombier 
8399ef1f84bSDavid du Colombier 	priv = icmp6->priv;
8409ef1f84bSDavid du Colombier 	p = buf;
8419ef1f84bSDavid du Colombier 	e = p+len;
8429ef1f84bSDavid du Colombier 	for(i = 0; i < Nstats6; i++)
8439ef1f84bSDavid du Colombier 		p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]);
8449ef1f84bSDavid du Colombier 	for(i = 0; i <= Maxtype6; i++)
8459ef1f84bSDavid du Colombier 		if(icmpnames6[i])
8469ef1f84bSDavid du Colombier 			p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i],
8479ef1f84bSDavid du Colombier 				priv->in[i], priv->out[i]);
848*a3323688SDavid du Colombier 		else if (0)
8499ef1f84bSDavid du Colombier 			p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i],
8509ef1f84bSDavid du Colombier 				priv->out[i]);
8519ef1f84bSDavid du Colombier 	return p - buf;
8529ef1f84bSDavid du Colombier }
8539ef1f84bSDavid du Colombier 
8549ef1f84bSDavid du Colombier /* import from icmp.c */
8559ef1f84bSDavid du Colombier extern int	icmpstate(Conv *c, char *state, int n);
8569ef1f84bSDavid du Colombier extern char*	icmpannounce(Conv *c, char **argv, int argc);
8579ef1f84bSDavid du Colombier extern char*	icmpconnect(Conv *c, char **argv, int argc);
8589ef1f84bSDavid du Colombier extern void	icmpclose(Conv *c);
8599ef1f84bSDavid du Colombier 
8609ef1f84bSDavid du Colombier void
icmp6init(Fs * fs)8619ef1f84bSDavid du Colombier icmp6init(Fs *fs)
8629ef1f84bSDavid du Colombier {
8639ef1f84bSDavid du Colombier 	Proto *icmp6 = smalloc(sizeof(Proto));
8649ef1f84bSDavid du Colombier 
8659ef1f84bSDavid du Colombier 	icmp6->priv = smalloc(sizeof(Icmppriv6));
8669ef1f84bSDavid du Colombier 	icmp6->name = "icmpv6";
8679ef1f84bSDavid du Colombier 	icmp6->connect = icmpconnect;
8689ef1f84bSDavid du Colombier 	icmp6->announce = icmpannounce;
8699ef1f84bSDavid du Colombier 	icmp6->state = icmpstate;
8709ef1f84bSDavid du Colombier 	icmp6->create = icmpcreate6;
8719ef1f84bSDavid du Colombier 	icmp6->close = icmpclose;
8729ef1f84bSDavid du Colombier 	icmp6->rcv = icmpiput6;
8739ef1f84bSDavid du Colombier 	icmp6->stats = icmpstats6;
8749ef1f84bSDavid du Colombier 	icmp6->ctl = icmpctl6;
8759ef1f84bSDavid du Colombier 	icmp6->advise = icmpadvise6;
8769ef1f84bSDavid du Colombier 	icmp6->gc = nil;
8779ef1f84bSDavid du Colombier 	icmp6->ipproto = ICMPv6;
8789ef1f84bSDavid du Colombier 	icmp6->nc = 16;
8799ef1f84bSDavid du Colombier 	icmp6->ptclsize = sizeof(Icmpcb6);
8809ef1f84bSDavid du Colombier 
8819ef1f84bSDavid du Colombier 	Fsproto(fs, icmp6);
8829ef1f84bSDavid du Colombier }
883