xref: /plan9/sys/src/9/ip/icmp6.c (revision 58da3067adcdccaaa043d0bfde28ba83b7ced07d)
1ccf6439bSDavid du Colombier /*
2ccf6439bSDavid du Colombier  * Internet Control Message Protocol for IPv6
3ccf6439bSDavid du Colombier  */
43ff48bf5SDavid du Colombier #include "u.h"
53ff48bf5SDavid du Colombier #include "../port/lib.h"
63ff48bf5SDavid du Colombier #include "mem.h"
73ff48bf5SDavid du Colombier #include "dat.h"
83ff48bf5SDavid du Colombier #include "fns.h"
93ff48bf5SDavid du Colombier #include "../port/error.h"
103ff48bf5SDavid du Colombier #include "ip.h"
113ff48bf5SDavid du Colombier #include "ipv6.h"
123ff48bf5SDavid du Colombier 
13ccf6439bSDavid du Colombier enum
14ccf6439bSDavid du Colombier {
15ccf6439bSDavid du Colombier 	InMsgs6,
16ccf6439bSDavid du Colombier 	InErrors6,
17ccf6439bSDavid du Colombier 	OutMsgs6,
18ccf6439bSDavid du Colombier 	CsumErrs6,
19ccf6439bSDavid du Colombier 	LenErrs6,
20ccf6439bSDavid du Colombier 	HlenErrs6,
21ccf6439bSDavid du Colombier 	HoplimErrs6,
22ccf6439bSDavid du Colombier 	IcmpCodeErrs6,
23ccf6439bSDavid du Colombier 	TargetErrs6,
24ccf6439bSDavid du Colombier 	OptlenErrs6,
25ccf6439bSDavid du Colombier 	AddrmxpErrs6,
26ccf6439bSDavid du Colombier 	RouterAddrErrs6,
27ccf6439bSDavid du Colombier 
28ccf6439bSDavid du Colombier 	Nstats6,
29ccf6439bSDavid du Colombier };
30ccf6439bSDavid du Colombier 
31ccf6439bSDavid du Colombier enum {
32ccf6439bSDavid du Colombier 	ICMP_USEAD6	= 40,
33ccf6439bSDavid du Colombier };
34ccf6439bSDavid du Colombier 
35ccf6439bSDavid du Colombier enum {
36ccf6439bSDavid du Colombier 	Oflag	= 1<<5,
37ccf6439bSDavid du Colombier 	Sflag	= 1<<6,
38ccf6439bSDavid du Colombier 	Rflag	= 1<<7,
39ccf6439bSDavid du Colombier };
40ccf6439bSDavid du Colombier 
41ccf6439bSDavid du Colombier enum {
42ccf6439bSDavid du Colombier 	/* ICMPv6 types */
43ccf6439bSDavid du Colombier 	EchoReply	= 0,
44ccf6439bSDavid du Colombier 	UnreachableV6	= 1,
45ccf6439bSDavid du Colombier 	PacketTooBigV6	= 2,
46ccf6439bSDavid du Colombier 	TimeExceedV6	= 3,
47ccf6439bSDavid du Colombier 	SrcQuench	= 4,
48ccf6439bSDavid du Colombier 	ParamProblemV6	= 4,
49ccf6439bSDavid du Colombier 	Redirect	= 5,
50ccf6439bSDavid du Colombier 	EchoRequest	= 8,
51ccf6439bSDavid du Colombier 	TimeExceed	= 11,
52ccf6439bSDavid du Colombier 	InParmProblem	= 12,
53ccf6439bSDavid du Colombier 	Timestamp	= 13,
54ccf6439bSDavid du Colombier 	TimestampReply	= 14,
55ccf6439bSDavid du Colombier 	InfoRequest	= 15,
56ccf6439bSDavid du Colombier 	InfoReply	= 16,
57ccf6439bSDavid du Colombier 	AddrMaskRequest = 17,
58ccf6439bSDavid du Colombier 	AddrMaskReply   = 18,
59ccf6439bSDavid du Colombier 	EchoRequestV6	= 128,
60ccf6439bSDavid du Colombier 	EchoReplyV6	= 129,
61ccf6439bSDavid du Colombier 	RouterSolicit	= 133,
62ccf6439bSDavid du Colombier 	RouterAdvert	= 134,
63ccf6439bSDavid du Colombier 	NbrSolicit	= 135,
64ccf6439bSDavid du Colombier 	NbrAdvert	= 136,
65ccf6439bSDavid du Colombier 	RedirectV6	= 137,
66ccf6439bSDavid du Colombier 
67ccf6439bSDavid du Colombier 	Maxtype6	= 137,
68ccf6439bSDavid du Colombier };
69ccf6439bSDavid du Colombier 
70410ea80bSDavid du Colombier /* on-the-wire packet formats */
713ff48bf5SDavid du Colombier typedef struct IPICMP IPICMP;
723ff48bf5SDavid du Colombier typedef struct Ndpkt Ndpkt;
733ff48bf5SDavid du Colombier typedef struct NdiscC NdiscC;
743ff48bf5SDavid du Colombier 
75410ea80bSDavid du Colombier /* we do this to avoid possible struct padding  */
76410ea80bSDavid du Colombier #define ICMPHDR \
77410ea80bSDavid du Colombier 	IPV6HDR; \
78410ea80bSDavid du Colombier 	uchar	type; \
79410ea80bSDavid du Colombier 	uchar	code; \
80410ea80bSDavid du Colombier 	uchar	cksum[2]; \
81410ea80bSDavid du Colombier 	uchar	icmpid[2]; \
82410ea80bSDavid du Colombier 	uchar	seq[2]
833ff48bf5SDavid du Colombier 
843ff48bf5SDavid du Colombier struct IPICMP {
85410ea80bSDavid du Colombier 	ICMPHDR;
86410ea80bSDavid du Colombier 	uchar	payload[];
873ff48bf5SDavid du Colombier };
883ff48bf5SDavid du Colombier 
89410ea80bSDavid du Colombier #define IPICMPSZ offsetof(IPICMP, payload[0])
90410ea80bSDavid du Colombier 
91410ea80bSDavid du Colombier struct NdiscC {
92410ea80bSDavid du Colombier 	ICMPHDR;
933ff48bf5SDavid du Colombier 	uchar	target[IPaddrlen];
94410ea80bSDavid du Colombier 	uchar	payload[];
953ff48bf5SDavid du Colombier };
963ff48bf5SDavid du Colombier 
97410ea80bSDavid du Colombier #define NDISCSZ offsetof(NdiscC, payload[0])
98410ea80bSDavid du Colombier 
99410ea80bSDavid du Colombier struct Ndpkt {
100410ea80bSDavid du Colombier 	ICMPHDR;
101410ea80bSDavid du Colombier 	uchar	target[IPaddrlen];
1023ff48bf5SDavid du Colombier 	uchar	otype;
103ccf6439bSDavid du Colombier 	uchar	olen;		/* length in units of 8 octets(incl type, code),
104ccf6439bSDavid du Colombier 				 * 1 for IEEE 802 addresses */
105ccf6439bSDavid du Colombier 	uchar	lnaddr[6];	/* link-layer address */
106410ea80bSDavid du Colombier 	uchar	payload[];
1073ff48bf5SDavid du Colombier };
1083ff48bf5SDavid du Colombier 
109410ea80bSDavid du Colombier #define NDPKTSZ offsetof(Ndpkt, payload[0])
110410ea80bSDavid du Colombier 
111ccf6439bSDavid du Colombier typedef struct Icmppriv6
112ccf6439bSDavid du Colombier {
113ccf6439bSDavid du Colombier 	ulong	stats[Nstats6];
1143ff48bf5SDavid du Colombier 
115ccf6439bSDavid du Colombier 	/* message counts */
116ccf6439bSDavid du Colombier 	ulong	in[Maxtype6+1];
117ccf6439bSDavid du Colombier 	ulong	out[Maxtype6+1];
118ccf6439bSDavid du Colombier } Icmppriv6;
119ccf6439bSDavid du Colombier 
120ccf6439bSDavid du Colombier typedef struct Icmpcb6
121ccf6439bSDavid du Colombier {
122ccf6439bSDavid du Colombier 	QLock;
123ccf6439bSDavid du Colombier 	uchar	headers;
124ccf6439bSDavid du Colombier } Icmpcb6;
1253ff48bf5SDavid du Colombier 
1263ff48bf5SDavid du Colombier char *icmpnames6[Maxtype6+1] =
1273ff48bf5SDavid du Colombier {
1283ff48bf5SDavid du Colombier [EchoReply]		"EchoReply",
1293ff48bf5SDavid du Colombier [UnreachableV6]		"UnreachableV6",
1303ff48bf5SDavid du Colombier [PacketTooBigV6]	"PacketTooBigV6",
1313ff48bf5SDavid du Colombier [TimeExceedV6]		"TimeExceedV6",
1323ff48bf5SDavid du Colombier [SrcQuench]		"SrcQuench",
1333ff48bf5SDavid du Colombier [Redirect]		"Redirect",
1343ff48bf5SDavid du Colombier [EchoRequest]		"EchoRequest",
1353ff48bf5SDavid du Colombier [TimeExceed]		"TimeExceed",
1363ff48bf5SDavid du Colombier [InParmProblem]		"InParmProblem",
1373ff48bf5SDavid du Colombier [Timestamp]		"Timestamp",
1383ff48bf5SDavid du Colombier [TimestampReply]	"TimestampReply",
1393ff48bf5SDavid du Colombier [InfoRequest]		"InfoRequest",
1403ff48bf5SDavid du Colombier [InfoReply]		"InfoReply",
1413ff48bf5SDavid du Colombier [AddrMaskRequest]	"AddrMaskRequest",
1423ff48bf5SDavid du Colombier [AddrMaskReply]		"AddrMaskReply",
1433ff48bf5SDavid du Colombier [EchoRequestV6]		"EchoRequestV6",
1443ff48bf5SDavid du Colombier [EchoReplyV6]		"EchoReplyV6",
1453ff48bf5SDavid du Colombier [RouterSolicit]		"RouterSolicit",
1463ff48bf5SDavid du Colombier [RouterAdvert]		"RouterAdvert",
1473ff48bf5SDavid du Colombier [NbrSolicit]		"NbrSolicit",
1483ff48bf5SDavid du Colombier [NbrAdvert]		"NbrAdvert",
1493ff48bf5SDavid du Colombier [RedirectV6]		"RedirectV6",
1503ff48bf5SDavid du Colombier };
1513ff48bf5SDavid du Colombier 
1523ff48bf5SDavid du Colombier static char *statnames6[Nstats6] =
1533ff48bf5SDavid du Colombier {
1543ff48bf5SDavid du Colombier [InMsgs6]	"InMsgs",
1553ff48bf5SDavid du Colombier [InErrors6]	"InErrors",
1563ff48bf5SDavid du Colombier [OutMsgs6]	"OutMsgs",
1573ff48bf5SDavid du Colombier [CsumErrs6]	"CsumErrs",
1583ff48bf5SDavid du Colombier [LenErrs6]	"LenErrs",
1593ff48bf5SDavid du Colombier [HlenErrs6]	"HlenErrs",
1603ff48bf5SDavid du Colombier [HoplimErrs6]	"HoplimErrs",
1613ff48bf5SDavid du Colombier [IcmpCodeErrs6]	"IcmpCodeErrs",
1623ff48bf5SDavid du Colombier [TargetErrs6]	"TargetErrs",
1633ff48bf5SDavid du Colombier [OptlenErrs6]	"OptlenErrs",
1643ff48bf5SDavid du Colombier [AddrmxpErrs6]	"AddrmxpErrs",
1653ff48bf5SDavid du Colombier [RouterAddrErrs6]	"RouterAddrErrs",
1663ff48bf5SDavid du Colombier };
1673ff48bf5SDavid du Colombier 
1683ff48bf5SDavid du Colombier static char *unreachcode[] =
1693ff48bf5SDavid du Colombier {
170f2c197d9SDavid du Colombier [Icmp6_no_route]	"no route to destination",
171f2c197d9SDavid du Colombier [Icmp6_ad_prohib]	"comm with destination administratively prohibited",
172f2c197d9SDavid du Colombier [Icmp6_out_src_scope]	"beyond scope of source address",
173f2c197d9SDavid du Colombier [Icmp6_adr_unreach]	"address unreachable",
174f2c197d9SDavid du Colombier [Icmp6_port_unreach]	"port unreachable",
175f2c197d9SDavid du Colombier [Icmp6_gress_src_fail]	"source address failed ingress/egress policy",
176f2c197d9SDavid du Colombier [Icmp6_rej_route]	"reject route to destination",
177f2c197d9SDavid du Colombier [Icmp6_unknown]		"icmp unreachable: unknown code",
1783ff48bf5SDavid du Colombier };
1793ff48bf5SDavid du Colombier 
180e6c6b7f8SDavid du Colombier static void icmpkick6(void *x, Block *bp);
1813ff48bf5SDavid du Colombier 
1823ff48bf5SDavid du Colombier static void
icmpcreate6(Conv * c)1833ff48bf5SDavid du Colombier icmpcreate6(Conv *c)
1843ff48bf5SDavid du Colombier {
1853ff48bf5SDavid du Colombier 	c->rq = qopen(64*1024, Qmsg, 0, c);
186e6c6b7f8SDavid du Colombier 	c->wq = qbypass(icmpkick6, c);
1873ff48bf5SDavid du Colombier }
1883ff48bf5SDavid du Colombier 
1893ff48bf5SDavid du Colombier static void
set_cksum(Block * bp)1903ff48bf5SDavid du Colombier set_cksum(Block *bp)
1913ff48bf5SDavid du Colombier {
1923ff48bf5SDavid du Colombier 	IPICMP *p = (IPICMP *)(bp->rp);
1933ff48bf5SDavid du Colombier 
194ccf6439bSDavid du Colombier 	hnputl(p->vcf, 0);  	/* borrow IP header as pseudoheader */
1950774058cSDavid du Colombier 	hnputs(p->ploadlen, blocklen(bp) - IP6HDR);
1963ff48bf5SDavid du Colombier 	p->proto = 0;
197ccf6439bSDavid du Colombier 	p->ttl = ICMPv6;	/* ttl gets set later */
1983ff48bf5SDavid du Colombier 	hnputs(p->cksum, 0);
1993ff48bf5SDavid du Colombier 	hnputs(p->cksum, ptclcsum(bp, 0, blocklen(bp)));
2003ff48bf5SDavid du Colombier 	p->proto = ICMPv6;
2013ff48bf5SDavid du Colombier }
2023ff48bf5SDavid du Colombier 
2033ff48bf5SDavid du Colombier static Block *
newIPICMP(int packetlen)2043ff48bf5SDavid du Colombier newIPICMP(int packetlen)
2053ff48bf5SDavid du Colombier {
2063ff48bf5SDavid du Colombier 	Block *nbp;
207ccf6439bSDavid du Colombier 
2083ff48bf5SDavid du Colombier 	nbp = allocb(packetlen);
2093ff48bf5SDavid du Colombier 	nbp->wp += packetlen;
2103ff48bf5SDavid du Colombier 	memset(nbp->rp, 0, packetlen);
2113ff48bf5SDavid du Colombier 	return nbp;
2123ff48bf5SDavid du Colombier }
2133ff48bf5SDavid du Colombier 
2143ff48bf5SDavid du Colombier void
icmpadvise6(Proto * icmp,Block * bp,char * msg)2153ff48bf5SDavid du Colombier icmpadvise6(Proto *icmp, Block *bp, char *msg)
2163ff48bf5SDavid du Colombier {
217ccf6439bSDavid du Colombier 	ushort recid;
2183ff48bf5SDavid du Colombier 	Conv **c, *s;
2193ff48bf5SDavid du Colombier 	IPICMP *p;
2203ff48bf5SDavid du Colombier 
2213ff48bf5SDavid du Colombier 	p = (IPICMP *)bp->rp;
2223ff48bf5SDavid du Colombier 	recid = nhgets(p->icmpid);
2233ff48bf5SDavid du Colombier 
2243ff48bf5SDavid du Colombier 	for(c = icmp->conv; *c; c++) {
2253ff48bf5SDavid du Colombier 		s = *c;
226ccf6439bSDavid du Colombier 		if(s->lport == recid && ipcmp(s->raddr, p->dst) == 0){
2273ff48bf5SDavid du Colombier 			qhangup(s->rq, msg);
2283ff48bf5SDavid du Colombier 			qhangup(s->wq, msg);
2293ff48bf5SDavid du Colombier 			break;
2303ff48bf5SDavid du Colombier 		}
2313ff48bf5SDavid du Colombier 	}
2323ff48bf5SDavid du Colombier 	freeblist(bp);
2333ff48bf5SDavid du Colombier }
2343ff48bf5SDavid du Colombier 
2353ff48bf5SDavid du Colombier static void
icmpkick6(void * x,Block * bp)236e6c6b7f8SDavid du Colombier icmpkick6(void *x, Block *bp)
2373ff48bf5SDavid du Colombier {
238ccf6439bSDavid du Colombier 	uchar laddr[IPaddrlen], raddr[IPaddrlen];
2393ff48bf5SDavid du Colombier 	Conv *c = x;
2403ff48bf5SDavid du Colombier 	IPICMP *p;
2413ff48bf5SDavid du Colombier 	Icmppriv6 *ipriv = c->p->priv;
2423ff48bf5SDavid du Colombier 	Icmpcb6 *icb = (Icmpcb6*)c->ptcl;
2433ff48bf5SDavid du Colombier 
2443ff48bf5SDavid du Colombier 	if(bp == nil)
2453ff48bf5SDavid du Colombier 		return;
2463ff48bf5SDavid du Colombier 
2473ff48bf5SDavid du Colombier 	if(icb->headers==6) {
2483ff48bf5SDavid du Colombier 		/* get user specified addresses */
2493ff48bf5SDavid du Colombier 		bp = pullupblock(bp, ICMP_USEAD6);
2503ff48bf5SDavid du Colombier 		if(bp == nil)
2513ff48bf5SDavid du Colombier 			return;
2523ff48bf5SDavid du Colombier 		bp->rp += 8;
2533ff48bf5SDavid du Colombier 		ipmove(laddr, bp->rp);
2543ff48bf5SDavid du Colombier 		bp->rp += IPaddrlen;
2553ff48bf5SDavid du Colombier 		ipmove(raddr, bp->rp);
2563ff48bf5SDavid du Colombier 		bp->rp += IPaddrlen;
257410ea80bSDavid du Colombier 		bp = padblock(bp, IP6HDR);
2583ff48bf5SDavid du Colombier 	}
2593ff48bf5SDavid du Colombier 
260410ea80bSDavid du Colombier 	if(blocklen(bp) < IPICMPSZ){
2613ff48bf5SDavid du Colombier 		freeblist(bp);
2623ff48bf5SDavid du Colombier 		return;
2633ff48bf5SDavid du Colombier 	}
2643ff48bf5SDavid du Colombier 	p = (IPICMP *)(bp->rp);
2653ff48bf5SDavid du Colombier 	if(icb->headers == 6) {
2663ff48bf5SDavid du Colombier 		ipmove(p->dst, raddr);
2673ff48bf5SDavid du Colombier 		ipmove(p->src, laddr);
2683ff48bf5SDavid du Colombier 	} else {
2693ff48bf5SDavid du Colombier 		ipmove(p->dst, c->raddr);
2703ff48bf5SDavid du Colombier 		ipmove(p->src, c->laddr);
2713ff48bf5SDavid du Colombier 		hnputs(p->icmpid, c->lport);
2723ff48bf5SDavid du Colombier 	}
2733ff48bf5SDavid du Colombier 
2743ff48bf5SDavid du Colombier 	set_cksum(bp);
2753ff48bf5SDavid du Colombier 	p->vcf[0] = 0x06 << 4;
2763ff48bf5SDavid du Colombier 	if(p->type <= Maxtype6)
2773ff48bf5SDavid du Colombier 		ipriv->out[p->type]++;
278a6a9e072SDavid du Colombier 	ipoput6(c->p->f, bp, 0, c->ttl, c->tos, nil);
2793ff48bf5SDavid du Colombier }
2803ff48bf5SDavid du Colombier 
2813ff48bf5SDavid du Colombier char*
icmpctl6(Conv * c,char ** argv,int argc)2823ff48bf5SDavid du Colombier icmpctl6(Conv *c, char **argv, int argc)
2833ff48bf5SDavid du Colombier {
2843ff48bf5SDavid du Colombier 	Icmpcb6 *icb;
2853ff48bf5SDavid du Colombier 
2863ff48bf5SDavid du Colombier 	icb = (Icmpcb6*) c->ptcl;
287ccf6439bSDavid du Colombier 	if(argc==1 && strcmp(argv[0], "headers")==0) {
2883ff48bf5SDavid du Colombier 		icb->headers = 6;
2893ff48bf5SDavid du Colombier 		return nil;
2903ff48bf5SDavid du Colombier 	}
2913ff48bf5SDavid du Colombier 	return "unknown control request";
2923ff48bf5SDavid du Colombier }
2933ff48bf5SDavid du Colombier 
2943ff48bf5SDavid du Colombier static void
goticmpkt6(Proto * icmp,Block * bp,int muxkey)2953ff48bf5SDavid du Colombier goticmpkt6(Proto *icmp, Block *bp, int muxkey)
2963ff48bf5SDavid du Colombier {
2973ff48bf5SDavid du Colombier 	ushort recid;
2983ff48bf5SDavid du Colombier 	uchar *addr;
299ccf6439bSDavid du Colombier 	Conv **c, *s;
300ccf6439bSDavid du Colombier 	IPICMP *p = (IPICMP *)bp->rp;
3013ff48bf5SDavid du Colombier 
3023ff48bf5SDavid du Colombier 	if(muxkey == 0) {
3033ff48bf5SDavid du Colombier 		recid = nhgets(p->icmpid);
3043ff48bf5SDavid du Colombier 		addr = p->src;
305ccf6439bSDavid du Colombier 	} else {
3063ff48bf5SDavid du Colombier 		recid = muxkey;
3073ff48bf5SDavid du Colombier 		addr = p->dst;
3083ff48bf5SDavid du Colombier 	}
3093ff48bf5SDavid du Colombier 
3103ff48bf5SDavid du Colombier 	for(c = icmp->conv; *c; c++){
3113ff48bf5SDavid du Colombier 		s = *c;
3123ff48bf5SDavid du Colombier 		if(s->lport == recid && ipcmp(s->raddr, addr) == 0){
3133ff48bf5SDavid du Colombier 			bp = concatblock(bp);
3143ff48bf5SDavid du Colombier 			if(bp != nil)
3153ff48bf5SDavid du Colombier 				qpass(s->rq, bp);
3163ff48bf5SDavid du Colombier 			return;
3173ff48bf5SDavid du Colombier 		}
3183ff48bf5SDavid du Colombier 	}
3193ff48bf5SDavid du Colombier 
3203ff48bf5SDavid du Colombier 	freeblist(bp);
3213ff48bf5SDavid du Colombier }
3223ff48bf5SDavid du Colombier 
3233ff48bf5SDavid du Colombier static Block *
mkechoreply6(Block * bp,Ipifc * ifc)3247366567fSDavid du Colombier mkechoreply6(Block *bp, Ipifc *ifc)
3253ff48bf5SDavid du Colombier {
3263ff48bf5SDavid du Colombier 	uchar addr[IPaddrlen];
327ccf6439bSDavid du Colombier 	IPICMP *p = (IPICMP *)(bp->rp);
3283ff48bf5SDavid du Colombier 
3293ff48bf5SDavid du Colombier 	ipmove(addr, p->src);
3307366567fSDavid du Colombier 	if(!isv6mcast(p->dst))
3313ff48bf5SDavid du Colombier 		ipmove(p->src, p->dst);
3327366567fSDavid du Colombier 	else if (!ipv6anylocal(ifc, p->src))
3337366567fSDavid du Colombier 		return nil;
3343ff48bf5SDavid du Colombier 	ipmove(p->dst, addr);
3353ff48bf5SDavid du Colombier 	p->type = EchoReplyV6;
3363ff48bf5SDavid du Colombier 	set_cksum(bp);
3373ff48bf5SDavid du Colombier 	return bp;
3383ff48bf5SDavid du Colombier }
3393ff48bf5SDavid du Colombier 
3403ff48bf5SDavid du Colombier /*
3413ff48bf5SDavid du Colombier  * sends out an ICMPv6 neighbor solicitation
3423ff48bf5SDavid du Colombier  * 	suni == SRC_UNSPEC or SRC_UNI,
3433ff48bf5SDavid du Colombier  *	tuni == TARG_MULTI => multicast for address resolution,
344f0ed0fb6SDavid du Colombier  * 	and tuni == TARG_UNI => neighbor reachability.
3453ff48bf5SDavid du Colombier  */
3463ff48bf5SDavid du Colombier extern void
icmpns(Fs * f,uchar * src,int suni,uchar * targ,int tuni,uchar * mac)3473ff48bf5SDavid du Colombier icmpns(Fs *f, uchar* src, int suni, uchar* targ, int tuni, uchar* mac)
3483ff48bf5SDavid du Colombier {
3493ff48bf5SDavid du Colombier 	Block *nbp;
3503ff48bf5SDavid du Colombier 	Ndpkt *np;
3513ff48bf5SDavid du Colombier 	Proto *icmp = f->t2p[ICMPv6];
3523ff48bf5SDavid du Colombier 	Icmppriv6 *ipriv = icmp->priv;
3533ff48bf5SDavid du Colombier 
354410ea80bSDavid du Colombier 	nbp = newIPICMP(NDPKTSZ);
3553ff48bf5SDavid du Colombier 	np = (Ndpkt*) nbp->rp;
3563ff48bf5SDavid du Colombier 
3573ff48bf5SDavid du Colombier 	if(suni == SRC_UNSPEC)
3583ff48bf5SDavid du Colombier 		memmove(np->src, v6Unspecified, IPaddrlen);
3593ff48bf5SDavid du Colombier 	else
3603ff48bf5SDavid du Colombier 		memmove(np->src, src, IPaddrlen);
3613ff48bf5SDavid du Colombier 
3623ff48bf5SDavid du Colombier 	if(tuni == TARG_UNI)
3633ff48bf5SDavid du Colombier 		memmove(np->dst, targ, IPaddrlen);
3643ff48bf5SDavid du Colombier 	else
3653ff48bf5SDavid du Colombier 		ipv62smcast(np->dst, targ);
3663ff48bf5SDavid du Colombier 
3673ff48bf5SDavid du Colombier 	np->type = NbrSolicit;
3683ff48bf5SDavid du Colombier 	np->code = 0;
3693ff48bf5SDavid du Colombier 	memmove(np->target, targ, IPaddrlen);
3703ff48bf5SDavid du Colombier 	if(suni != SRC_UNSPEC) {
371ccf6439bSDavid du Colombier 		np->otype = SRC_LLADDR;
3723ff48bf5SDavid du Colombier 		np->olen = 1;		/* 1+1+6 = 8 = 1 8-octet */
3733ff48bf5SDavid du Colombier 		memmove(np->lnaddr, mac, sizeof(np->lnaddr));
374ccf6439bSDavid du Colombier 	} else
375410ea80bSDavid du Colombier 		nbp->wp -= NDPKTSZ - NDISCSZ;
3763ff48bf5SDavid du Colombier 
3773ff48bf5SDavid du Colombier 	set_cksum(nbp);
3783ff48bf5SDavid du Colombier 	np = (Ndpkt*)nbp->rp;
3793ff48bf5SDavid du Colombier 	np->ttl = HOP_LIMIT;
3803ff48bf5SDavid du Colombier 	np->vcf[0] = 0x06 << 4;
3813ff48bf5SDavid du Colombier 	ipriv->out[NbrSolicit]++;
3823ff48bf5SDavid du Colombier 	netlog(f, Logicmp, "sending neighbor solicitation %I\n", targ);
383a6a9e072SDavid du Colombier 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
3843ff48bf5SDavid du Colombier }
3853ff48bf5SDavid du Colombier 
3863ff48bf5SDavid du Colombier /*
3873ff48bf5SDavid du Colombier  * sends out an ICMPv6 neighbor advertisement. pktflags == RSO flags.
3883ff48bf5SDavid du Colombier  */
3893ff48bf5SDavid du Colombier extern void
icmpna(Fs * f,uchar * src,uchar * dst,uchar * targ,uchar * mac,uchar flags)3903ff48bf5SDavid du Colombier icmpna(Fs *f, uchar* src, uchar* dst, uchar* targ, uchar* mac, uchar flags)
3913ff48bf5SDavid du Colombier {
3923ff48bf5SDavid du Colombier 	Block *nbp;
3933ff48bf5SDavid du Colombier 	Ndpkt *np;
3943ff48bf5SDavid du Colombier 	Proto *icmp = f->t2p[ICMPv6];
3953ff48bf5SDavid du Colombier 	Icmppriv6 *ipriv = icmp->priv;
3963ff48bf5SDavid du Colombier 
397410ea80bSDavid du Colombier 	nbp = newIPICMP(NDPKTSZ);
3983ff48bf5SDavid du Colombier 	np = (Ndpkt*)nbp->rp;
3993ff48bf5SDavid du Colombier 
4003ff48bf5SDavid du Colombier 	memmove(np->src, src, IPaddrlen);
4013ff48bf5SDavid du Colombier 	memmove(np->dst, dst, IPaddrlen);
4023ff48bf5SDavid du Colombier 
4033ff48bf5SDavid du Colombier 	np->type = NbrAdvert;
4043ff48bf5SDavid du Colombier 	np->code = 0;
4053ff48bf5SDavid du Colombier 	np->icmpid[0] = flags;
4063ff48bf5SDavid du Colombier 	memmove(np->target, targ, IPaddrlen);
4073ff48bf5SDavid du Colombier 
408ccf6439bSDavid du Colombier 	np->otype = TARGET_LLADDR;
4093ff48bf5SDavid du Colombier 	np->olen = 1;
4103ff48bf5SDavid du Colombier 	memmove(np->lnaddr, mac, sizeof(np->lnaddr));
4113ff48bf5SDavid du Colombier 
4123ff48bf5SDavid du Colombier 	set_cksum(nbp);
4133ff48bf5SDavid du Colombier 	np = (Ndpkt*) nbp->rp;
4143ff48bf5SDavid du Colombier 	np->ttl = HOP_LIMIT;
4153ff48bf5SDavid du Colombier 	np->vcf[0] = 0x06 << 4;
4163ff48bf5SDavid du Colombier 	ipriv->out[NbrAdvert]++;
4173ff48bf5SDavid du Colombier 	netlog(f, Logicmp, "sending neighbor advertisement %I\n", src);
418a6a9e072SDavid du Colombier 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
4193ff48bf5SDavid du Colombier }
4203ff48bf5SDavid du Colombier 
4216083aa43SDavid du Colombier /* if free is true, freeblist(bp) before return. */
4223ff48bf5SDavid du Colombier extern void
icmphostunr(Fs * f,Ipifc * ifc,Block * bp,int code,int free)4233ff48bf5SDavid du Colombier icmphostunr(Fs *f, Ipifc *ifc, Block *bp, int code, int free)
4243ff48bf5SDavid du Colombier {
4256083aa43SDavid du Colombier 	int osz, sz;
4263ff48bf5SDavid du Colombier 	Block *nbp;
4273ff48bf5SDavid du Colombier 	IPICMP *np;
4286083aa43SDavid du Colombier 	Icmppriv6 *ipriv;
4293ff48bf5SDavid du Colombier 	Ip6hdr *p;
4306083aa43SDavid du Colombier 	Proto *icmp;
4313ff48bf5SDavid du Colombier 
4326083aa43SDavid du Colombier 	osz = BLEN(bp);
4336083aa43SDavid du Colombier 	sz = MIN(IPICMPSZ + osz, v6MINTU);
4346083aa43SDavid du Colombier 	icmp = f->t2p[ICMPv6];
4356083aa43SDavid du Colombier 	ipriv = icmp->priv;
4363ff48bf5SDavid du Colombier 	p = (Ip6hdr *)bp->rp;
4373ff48bf5SDavid du Colombier 	if(isv6mcast(p->src))
4386083aa43SDavid du Colombier 		goto freebl;
4393ff48bf5SDavid du Colombier 	nbp = newIPICMP(sz);
4403ff48bf5SDavid du Colombier 	np = (IPICMP *)nbp->rp;
4413ff48bf5SDavid du Colombier 
4423ff48bf5SDavid du Colombier 	rlock(ifc);
4436083aa43SDavid du Colombier 	if(!ipv6anylocal(ifc, np->src)){
4447133e0eeSDavid du Colombier 		netlog(f, Logicmp, "icmphostunr fail -> src %I dst %I\n",
445ccf6439bSDavid du Colombier 			p->src, p->dst);
4466083aa43SDavid du Colombier 		runlock(ifc);
4473ff48bf5SDavid du Colombier 		freeblist(nbp);
4486083aa43SDavid du Colombier 		goto freebl;
4493ff48bf5SDavid du Colombier 	}
4503ff48bf5SDavid du Colombier 
4516083aa43SDavid du Colombier 	netlog(f, Logicmp, "send icmphostunr -> src %I dst %I\n", p->src, p->dst);
4523ff48bf5SDavid du Colombier 	memmove(np->dst, p->src, IPaddrlen);
4533ff48bf5SDavid du Colombier 	np->type = UnreachableV6;
4543ff48bf5SDavid du Colombier 	np->code = code;
455410ea80bSDavid du Colombier 	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
4563ff48bf5SDavid du Colombier 	set_cksum(nbp);
4573ff48bf5SDavid du Colombier 	np->ttl = HOP_LIMIT;
4583ff48bf5SDavid du Colombier 	np->vcf[0] = 0x06 << 4;
4593ff48bf5SDavid du Colombier 	ipriv->out[UnreachableV6]++;
4603ff48bf5SDavid du Colombier 
4613ff48bf5SDavid du Colombier 	if(free)
4623ff48bf5SDavid du Colombier 		ipiput6(f, ifc, nbp);
4636083aa43SDavid du Colombier 	else
464a6a9e072SDavid du Colombier 		ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
4653ff48bf5SDavid du Colombier 	runlock(ifc);
4666083aa43SDavid du Colombier freebl:
4676083aa43SDavid du Colombier 	if(free)
4683ff48bf5SDavid du Colombier 		freeblist(bp);
4693ff48bf5SDavid du Colombier }
4703ff48bf5SDavid du Colombier 
4713ff48bf5SDavid du Colombier extern void
icmpttlexceeded6(Fs * f,Ipifc * ifc,Block * bp)4723ff48bf5SDavid du Colombier icmpttlexceeded6(Fs *f, Ipifc *ifc, Block *bp)
4733ff48bf5SDavid du Colombier {
474ccf6439bSDavid du Colombier 	int osz = BLEN(bp);
475410ea80bSDavid du Colombier 	int sz = MIN(IPICMPSZ + osz, v6MINTU);
4763ff48bf5SDavid du Colombier 	Block *nbp;
4773ff48bf5SDavid du Colombier 	IPICMP *np;
4783ff48bf5SDavid du Colombier 	Ip6hdr *p;
4793ff48bf5SDavid du Colombier 	Proto *icmp = f->t2p[ICMPv6];
4803ff48bf5SDavid du Colombier 	Icmppriv6 *ipriv = icmp->priv;
4813ff48bf5SDavid du Colombier 
4823ff48bf5SDavid du Colombier 	p = (Ip6hdr *)bp->rp;
4833ff48bf5SDavid du Colombier 	if(isv6mcast(p->src))
4843ff48bf5SDavid du Colombier 		return;
4853ff48bf5SDavid du Colombier 
4863ff48bf5SDavid du Colombier 	nbp = newIPICMP(sz);
4873ff48bf5SDavid du Colombier 	np = (IPICMP *) nbp->rp;
488ccf6439bSDavid du Colombier 	if(ipv6anylocal(ifc, np->src))
4897133e0eeSDavid du Colombier 		netlog(f, Logicmp, "send icmpttlexceeded6 -> src %I dst %I\n",
490ccf6439bSDavid du Colombier 			p->src, p->dst);
4913ff48bf5SDavid du Colombier 	else {
4927133e0eeSDavid du Colombier 		netlog(f, Logicmp, "icmpttlexceeded6 fail -> src %I dst %I\n",
493ccf6439bSDavid du Colombier 			p->src, p->dst);
4943ff48bf5SDavid du Colombier 		return;
4953ff48bf5SDavid du Colombier 	}
4963ff48bf5SDavid du Colombier 
4973ff48bf5SDavid du Colombier 	memmove(np->dst, p->src, IPaddrlen);
4983ff48bf5SDavid du Colombier 	np->type = TimeExceedV6;
4993ff48bf5SDavid du Colombier 	np->code = 0;
500410ea80bSDavid du Colombier 	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
5013ff48bf5SDavid du Colombier 	set_cksum(nbp);
5023ff48bf5SDavid du Colombier 	np->ttl = HOP_LIMIT;
5033ff48bf5SDavid du Colombier 	np->vcf[0] = 0x06 << 4;
5043ff48bf5SDavid du Colombier 	ipriv->out[TimeExceedV6]++;
505a6a9e072SDavid du Colombier 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
5063ff48bf5SDavid du Colombier }
5073ff48bf5SDavid du Colombier 
5083ff48bf5SDavid du Colombier extern void
icmppkttoobig6(Fs * f,Ipifc * ifc,Block * bp)5093ff48bf5SDavid du Colombier icmppkttoobig6(Fs *f, Ipifc *ifc, Block *bp)
5103ff48bf5SDavid du Colombier {
511ccf6439bSDavid du Colombier 	int osz = BLEN(bp);
512410ea80bSDavid du Colombier 	int sz = MIN(IPICMPSZ + osz, v6MINTU);
5133ff48bf5SDavid du Colombier 	Block *nbp;
5143ff48bf5SDavid du Colombier 	IPICMP *np;
5153ff48bf5SDavid du Colombier 	Ip6hdr *p;
5163ff48bf5SDavid du Colombier 	Proto *icmp = f->t2p[ICMPv6];
5173ff48bf5SDavid du Colombier 	Icmppriv6 *ipriv = icmp->priv;
5183ff48bf5SDavid du Colombier 
5193ff48bf5SDavid du Colombier 	p = (Ip6hdr *)bp->rp;
5203ff48bf5SDavid du Colombier 	if(isv6mcast(p->src))
5213ff48bf5SDavid du Colombier 		return;
5223ff48bf5SDavid du Colombier 
5233ff48bf5SDavid du Colombier 	nbp = newIPICMP(sz);
5243ff48bf5SDavid du Colombier 	np = (IPICMP *)nbp->rp;
525ccf6439bSDavid du Colombier 	if(ipv6anylocal(ifc, np->src))
5267133e0eeSDavid du Colombier 		netlog(f, Logicmp, "send icmppkttoobig6 -> src %I dst %I\n",
527ccf6439bSDavid du Colombier 			p->src, p->dst);
5283ff48bf5SDavid du Colombier 	else {
5297133e0eeSDavid du Colombier 		netlog(f, Logicmp, "icmppkttoobig6 fail -> src %I dst %I\n",
530ccf6439bSDavid du Colombier 			p->src, p->dst);
5313ff48bf5SDavid du Colombier 		return;
5323ff48bf5SDavid du Colombier 	}
5333ff48bf5SDavid du Colombier 
5343ff48bf5SDavid du Colombier 	memmove(np->dst, p->src, IPaddrlen);
5353ff48bf5SDavid du Colombier 	np->type = PacketTooBigV6;
5363ff48bf5SDavid du Colombier 	np->code = 0;
5373f695129SDavid du Colombier 	hnputl(np->icmpid, ifc->maxtu - ifc->m->hsize);
538410ea80bSDavid du Colombier 	memmove(nbp->rp + IPICMPSZ, bp->rp, sz - IPICMPSZ);
5393ff48bf5SDavid du Colombier 	set_cksum(nbp);
5403ff48bf5SDavid du Colombier 	np->ttl = HOP_LIMIT;
5413ff48bf5SDavid du Colombier 	np->vcf[0] = 0x06 << 4;
5423ff48bf5SDavid du Colombier 	ipriv->out[PacketTooBigV6]++;
543a6a9e072SDavid du Colombier 	ipoput6(f, nbp, 0, MAXTTL, DFLTTOS, nil);
5443ff48bf5SDavid du Colombier }
5453ff48bf5SDavid du Colombier 
5463ff48bf5SDavid du Colombier /*
5473ff48bf5SDavid du Colombier  * RFC 2461, pages 39-40, pages 57-58.
5483ff48bf5SDavid du Colombier  */
5493ff48bf5SDavid du Colombier static int
valid(Proto * icmp,Ipifc *,Block * bp,Icmppriv6 * ipriv)5506083aa43SDavid du Colombier valid(Proto *icmp, Ipifc *, Block *bp, Icmppriv6 *ipriv)
551ccf6439bSDavid du Colombier {
5526083aa43SDavid du Colombier 	int sz, osz, unsp, n, ttl, iplen, pktsz;
5536083aa43SDavid du Colombier 	uchar *packet;
5546083aa43SDavid du Colombier 	IPICMP *p;
5553ff48bf5SDavid du Colombier 	Ndpkt *np;
5563ff48bf5SDavid du Colombier 
5573ff48bf5SDavid du Colombier 	n = blocklen(bp);
558410ea80bSDavid du Colombier 	if(n < IPICMPSZ) {
5593ff48bf5SDavid du Colombier 		ipriv->stats[HlenErrs6]++;
5603ff48bf5SDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp hlen %d\n", n);
5613ff48bf5SDavid du Colombier 		goto err;
5623ff48bf5SDavid du Colombier 	}
5633ff48bf5SDavid du Colombier 
5646083aa43SDavid du Colombier 	packet = bp->rp;
5656083aa43SDavid du Colombier 	p = (IPICMP *)packet;
5666083aa43SDavid du Colombier 	pktsz = BLEN(bp);
5673ff48bf5SDavid du Colombier 	iplen = nhgets(p->ploadlen);
5685acbe002SDavid du Colombier 	if(iplen > n - IP6HDR) {
5693ff48bf5SDavid du Colombier 		ipriv->stats[LenErrs6]++;
5703ff48bf5SDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp length %d\n", iplen);
5713ff48bf5SDavid du Colombier 		goto err;
5723ff48bf5SDavid du Colombier 	}
5733ff48bf5SDavid du Colombier 
574ccf6439bSDavid du Colombier 	/* Rather than construct explicit pseudoheader, overwrite IPv6 header */
5753ff48bf5SDavid du Colombier 	if(p->proto != ICMPv6) {
576ccf6439bSDavid du Colombier 		/* This code assumes no extension headers!!! */
5773ff48bf5SDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp error: extension header\n");
5783ff48bf5SDavid du Colombier 		goto err;
5793ff48bf5SDavid du Colombier 	}
5803ff48bf5SDavid du Colombier 	memset(packet, 0, 4);
5813ff48bf5SDavid du Colombier 	ttl = p->ttl;
5823ff48bf5SDavid du Colombier 	p->ttl = p->proto;
5833ff48bf5SDavid du Colombier 	p->proto = 0;
5840774058cSDavid du Colombier 	if(ptclcsum(bp, 0, iplen + IP6HDR)) {
5853ff48bf5SDavid du Colombier 		ipriv->stats[CsumErrs6]++;
5863ff48bf5SDavid du Colombier 		netlog(icmp->f, Logicmp, "icmp checksum error\n");
5873ff48bf5SDavid du Colombier 		goto err;
5883ff48bf5SDavid du Colombier 	}
5893ff48bf5SDavid du Colombier 	p->proto = p->ttl;
5903ff48bf5SDavid du Colombier 	p->ttl = ttl;
5913ff48bf5SDavid du Colombier 
5923ff48bf5SDavid du Colombier 	/* additional tests for some pkt types */
5936083aa43SDavid du Colombier 	if (p->type != NbrSolicit   && p->type != NbrAdvert &&
5946083aa43SDavid du Colombier 	    p->type != RouterAdvert && p->type != RouterSolicit &&
5956083aa43SDavid du Colombier 	    p->type != RedirectV6)
5966083aa43SDavid du Colombier 		return 1;	/* TODO: unknown, presumed valid; why? */
5973ff48bf5SDavid du Colombier 	if(p->ttl != HOP_LIMIT) {
5983ff48bf5SDavid du Colombier 		ipriv->stats[HoplimErrs6]++;
5993ff48bf5SDavid du Colombier 		goto err;
6003ff48bf5SDavid du Colombier 	}
6013ff48bf5SDavid du Colombier 	if(p->code != 0) {
6023ff48bf5SDavid du Colombier 		ipriv->stats[IcmpCodeErrs6]++;
6033ff48bf5SDavid du Colombier 		goto err;
6043ff48bf5SDavid du Colombier 	}
6053ff48bf5SDavid du Colombier 
6063ff48bf5SDavid du Colombier 	switch (p->type) {
6073ff48bf5SDavid du Colombier 	case NbrSolicit:
6083ff48bf5SDavid du Colombier 	case NbrAdvert:
6093ff48bf5SDavid du Colombier 		np = (Ndpkt*) p;
6103ff48bf5SDavid du Colombier 		if(isv6mcast(np->target)) {
6113ff48bf5SDavid du Colombier 			ipriv->stats[TargetErrs6]++;
6123ff48bf5SDavid du Colombier 			goto err;
6133ff48bf5SDavid du Colombier 		}
614ccf6439bSDavid du Colombier 		if(optexsts(np) && np->olen == 0) {
6153ff48bf5SDavid du Colombier 			ipriv->stats[OptlenErrs6]++;
6163ff48bf5SDavid du Colombier 			goto err;
6173ff48bf5SDavid du Colombier 		}
6186083aa43SDavid du Colombier 		if (p->type == NbrSolicit && ipcmp(np->src, v6Unspecified) == 0)
6193ff48bf5SDavid du Colombier 			if(!issmcast(np->dst) || optexsts(np)) {
6203ff48bf5SDavid du Colombier 				ipriv->stats[AddrmxpErrs6]++;
6213ff48bf5SDavid du Colombier 				goto err;
6223ff48bf5SDavid du Colombier 			}
6236083aa43SDavid du Colombier 		if(p->type == NbrAdvert && isv6mcast(np->dst) &&
6246083aa43SDavid du Colombier 		    nhgets(np->icmpid) & Sflag){
6253ff48bf5SDavid du Colombier 			ipriv->stats[AddrmxpErrs6]++;
6263ff48bf5SDavid du Colombier 			goto err;
6273ff48bf5SDavid du Colombier 		}
6283ff48bf5SDavid du Colombier 		break;
6293ff48bf5SDavid du Colombier 	case RouterAdvert:
630410ea80bSDavid du Colombier 		if(pktsz - IP6HDR < 16) {
6313ff48bf5SDavid du Colombier 			ipriv->stats[HlenErrs6]++;
6323ff48bf5SDavid du Colombier 			goto err;
6333ff48bf5SDavid du Colombier 		}
6343ff48bf5SDavid du Colombier 		if(!islinklocal(p->src)) {
6353ff48bf5SDavid du Colombier 			ipriv->stats[RouterAddrErrs6]++;
6363ff48bf5SDavid du Colombier 			goto err;
6373ff48bf5SDavid du Colombier 		}
6386083aa43SDavid du Colombier 		for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
639ccf6439bSDavid du Colombier 			osz = packet[sz+1];
6403ff48bf5SDavid du Colombier 			if(osz <= 0) {
6413ff48bf5SDavid du Colombier 				ipriv->stats[OptlenErrs6]++;
6423ff48bf5SDavid du Colombier 				goto err;
6433ff48bf5SDavid du Colombier 			}
6443ff48bf5SDavid du Colombier 		}
6453ff48bf5SDavid du Colombier 		break;
6463ff48bf5SDavid du Colombier 	case RouterSolicit:
647410ea80bSDavid du Colombier 		if(pktsz - IP6HDR < 8) {
6483ff48bf5SDavid du Colombier 			ipriv->stats[HlenErrs6]++;
6493ff48bf5SDavid du Colombier 			goto err;
6503ff48bf5SDavid du Colombier 		}
6513ff48bf5SDavid du Colombier 		unsp = (ipcmp(p->src, v6Unspecified) == 0);
6526083aa43SDavid du Colombier 		for (sz = IPICMPSZ + 8; sz+1 < pktsz; sz += 8*osz) {
653ccf6439bSDavid du Colombier 			osz = packet[sz+1];
6546083aa43SDavid du Colombier 			if(osz <= 0 || (unsp && packet[sz] == SRC_LLADDR)) {
6553ff48bf5SDavid du Colombier 				ipriv->stats[OptlenErrs6]++;
6563ff48bf5SDavid du Colombier 				goto err;
6573ff48bf5SDavid du Colombier 			}
6583ff48bf5SDavid du Colombier 		}
6593ff48bf5SDavid du Colombier 		break;
6603ff48bf5SDavid du Colombier 	case RedirectV6:
6616083aa43SDavid du Colombier 		/* TODO: fill in */
6623ff48bf5SDavid du Colombier 		break;
6633ff48bf5SDavid du Colombier 	default:
6643ff48bf5SDavid du Colombier 		goto err;
6653ff48bf5SDavid du Colombier 	}
6663ff48bf5SDavid du Colombier 	return 1;
6673ff48bf5SDavid du Colombier err:
6683ff48bf5SDavid du Colombier 	ipriv->stats[InErrors6]++;
6693ff48bf5SDavid du Colombier 	return 0;
6703ff48bf5SDavid du Colombier }
6713ff48bf5SDavid du Colombier 
6723ff48bf5SDavid du Colombier static int
targettype(Fs * f,Ipifc * ifc,uchar * target)6733ff48bf5SDavid du Colombier targettype(Fs *f, Ipifc *ifc, uchar *target)
6743ff48bf5SDavid du Colombier {
6753ff48bf5SDavid du Colombier 	Iplifc *lifc;
6763ff48bf5SDavid du Colombier 	int t;
6773ff48bf5SDavid du Colombier 
6783ff48bf5SDavid du Colombier 	rlock(ifc);
6793ff48bf5SDavid du Colombier 	if(ipproxyifc(f, ifc, target)) {
6803ff48bf5SDavid du Colombier 		runlock(ifc);
681f2c197d9SDavid du Colombier 		return Tuniproxy;
6823ff48bf5SDavid du Colombier 	}
6833ff48bf5SDavid du Colombier 
684ccf6439bSDavid du Colombier 	for(lifc = ifc->lifc; lifc; lifc = lifc->next)
6853ff48bf5SDavid du Colombier 		if(ipcmp(lifc->local, target) == 0) {
686f2c197d9SDavid du Colombier 			t = (lifc->tentative)? Tunitent: Tunirany;
6873ff48bf5SDavid du Colombier 			runlock(ifc);
6883ff48bf5SDavid du Colombier 			return t;
6893ff48bf5SDavid du Colombier 		}
6903ff48bf5SDavid du Colombier 
6913ff48bf5SDavid du Colombier 	runlock(ifc);
6923ff48bf5SDavid du Colombier 	return 0;
6933ff48bf5SDavid du Colombier }
6943ff48bf5SDavid du Colombier 
6956083aa43SDavid du Colombier /* bp needs to be freed with freeblist or passed on. */
6963ff48bf5SDavid du Colombier static void
icmpiput6(Proto * icmp,Ipifc * ipifc,Block * bp)6973ff48bf5SDavid du Colombier icmpiput6(Proto *icmp, Ipifc *ipifc, Block *bp)
6983ff48bf5SDavid du Colombier {
6996083aa43SDavid du Colombier 	int type;
700ccf6439bSDavid du Colombier 	char *msg, m2[128];
701ccf6439bSDavid du Colombier 	uchar pktflags;
7026083aa43SDavid du Colombier 	uchar *packet, *src;
703ccf6439bSDavid du Colombier 	uchar lsrc[IPaddrlen];
704ccf6439bSDavid du Colombier 	Block *r;
7056083aa43SDavid du Colombier 	IPICMP *p;
7066083aa43SDavid du Colombier 	Icmppriv6 *ipriv;
7073ff48bf5SDavid du Colombier 	Iplifc *lifc;
708ccf6439bSDavid du Colombier 	Ndpkt* np;
709ccf6439bSDavid du Colombier 	Proto *pr;
7103ff48bf5SDavid du Colombier 
7116083aa43SDavid du Colombier 	packet = bp->rp;
7126083aa43SDavid du Colombier 	p = (IPICMP *)packet;
7136083aa43SDavid du Colombier 	type = p->type;
7146083aa43SDavid du Colombier 	ipriv = icmp->priv;
7156083aa43SDavid du Colombier 	if(!valid(icmp, ipifc, bp, ipriv) || type > Maxtype6)
7163ff48bf5SDavid du Colombier 		goto raise;
7173ff48bf5SDavid du Colombier 
7186083aa43SDavid du Colombier 	ipriv->in[type]++;
7196083aa43SDavid du Colombier 	switch(type) {
7203ff48bf5SDavid du Colombier 	case EchoRequestV6:
721*58da3067SDavid du Colombier 		bp = concatblock(bp);
7227366567fSDavid du Colombier 		r = mkechoreply6(bp, ipifc);
7237366567fSDavid du Colombier 		if(r == nil)
7247366567fSDavid du Colombier 			goto raise;
7253ff48bf5SDavid du Colombier 		ipriv->out[EchoReply]++;
726a6a9e072SDavid du Colombier 		ipoput6(icmp->f, r, 0, MAXTTL, DFLTTOS, nil);
7273ff48bf5SDavid du Colombier 		break;
7283ff48bf5SDavid du Colombier 	case UnreachableV6:
729f2c197d9SDavid du Colombier 		if(p->code >= nelem(unreachcode))
730f2c197d9SDavid du Colombier 			msg = unreachcode[Icmp6_unknown];
7313ff48bf5SDavid du Colombier 		else
7323ff48bf5SDavid du Colombier 			msg = unreachcode[p->code];
7333ff48bf5SDavid du Colombier 
734410ea80bSDavid du Colombier 		bp->rp += IPICMPSZ;
7353ff48bf5SDavid du Colombier 		if(blocklen(bp) < 8){
7363ff48bf5SDavid du Colombier 			ipriv->stats[LenErrs6]++;
7373ff48bf5SDavid du Colombier 			goto raise;
7383ff48bf5SDavid du Colombier 		}
7393ff48bf5SDavid du Colombier 		p = (IPICMP *)bp->rp;
7403ff48bf5SDavid du Colombier 		pr = Fsrcvpcolx(icmp->f, p->proto);
7413ff48bf5SDavid du Colombier 		if(pr != nil && pr->advise != nil) {
7423ff48bf5SDavid du Colombier 			(*pr->advise)(pr, bp, msg);
7433ff48bf5SDavid du Colombier 			return;
7443ff48bf5SDavid du Colombier 		}
7453ff48bf5SDavid du Colombier 
746410ea80bSDavid du Colombier 		bp->rp -= IPICMPSZ;
7473ff48bf5SDavid du Colombier 		goticmpkt6(icmp, bp, 0);
7483ff48bf5SDavid du Colombier 		break;
7493ff48bf5SDavid du Colombier 	case TimeExceedV6:
7503ff48bf5SDavid du Colombier 		if(p->code == 0){
7514e3613abSDavid du Colombier 			snprint(m2, sizeof m2, "ttl exceeded at %I", p->src);
752410ea80bSDavid du Colombier 			bp->rp += IPICMPSZ;
7533ff48bf5SDavid du Colombier 			if(blocklen(bp) < 8){
7543ff48bf5SDavid du Colombier 				ipriv->stats[LenErrs6]++;
7553ff48bf5SDavid du Colombier 				goto raise;
7563ff48bf5SDavid du Colombier 			}
7573ff48bf5SDavid du Colombier 			p = (IPICMP *)bp->rp;
7583ff48bf5SDavid du Colombier 			pr = Fsrcvpcolx(icmp->f, p->proto);
759107aedb4SDavid du Colombier 			if(pr && pr->advise) {
7603ff48bf5SDavid du Colombier 				(*pr->advise)(pr, bp, m2);
7613ff48bf5SDavid du Colombier 				return;
7623ff48bf5SDavid du Colombier 			}
763410ea80bSDavid du Colombier 			bp->rp -= IPICMPSZ;
7643ff48bf5SDavid du Colombier 		}
7653ff48bf5SDavid du Colombier 		goticmpkt6(icmp, bp, 0);
7663ff48bf5SDavid du Colombier 		break;
7673ff48bf5SDavid du Colombier 	case RouterAdvert:
7683ff48bf5SDavid du Colombier 	case RouterSolicit:
769ccf6439bSDavid du Colombier 		/* using lsrc as a temp, munge hdr for goticmp6 */
770ccf6439bSDavid du Colombier 		if (0) {
7713ff48bf5SDavid du Colombier 			memmove(lsrc, p->src, IPaddrlen);
7723ff48bf5SDavid du Colombier 			memmove(p->src, p->dst, IPaddrlen);
773ccf6439bSDavid du Colombier 			memmove(p->dst, lsrc, IPaddrlen);
774ccf6439bSDavid du Colombier 		}
7756083aa43SDavid du Colombier 		goticmpkt6(icmp, bp, type);
7763ff48bf5SDavid du Colombier 		break;
7773ff48bf5SDavid du Colombier 	case NbrSolicit:
7786083aa43SDavid du Colombier 		np = (Ndpkt*)p;			/* within bp */
7793ff48bf5SDavid du Colombier 		pktflags = 0;
7803ff48bf5SDavid du Colombier 		switch (targettype(icmp->f, ipifc, np->target)) {
781f2c197d9SDavid du Colombier 		case Tunirany:
7823ff48bf5SDavid du Colombier 			pktflags |= Oflag;
7833ff48bf5SDavid du Colombier 			/* fall through */
784f2c197d9SDavid du Colombier 		case Tuniproxy:
7853ff48bf5SDavid du Colombier 			if(ipcmp(np->src, v6Unspecified) != 0) {
786ccf6439bSDavid du Colombier 				arpenter(icmp->f, V6, np->src, np->lnaddr,
787ccf6439bSDavid du Colombier 					8*np->olen-2, 0);
7883ff48bf5SDavid du Colombier 				pktflags |= Sflag;
7893ff48bf5SDavid du Colombier 			}
7906083aa43SDavid du Colombier 			if(ipv6local(ipifc, lsrc)) {
7916083aa43SDavid du Colombier 				src = np->src;
7926083aa43SDavid du Colombier 				if(ipcmp(src, v6Unspecified) == 0)
7936083aa43SDavid du Colombier 					src = v6allnodesL;
7946083aa43SDavid du Colombier 				icmpna(icmp->f, lsrc, src, np->target,
7956083aa43SDavid du Colombier 					ipifc->mac, pktflags);
7963ff48bf5SDavid du Colombier 			}
7973ff48bf5SDavid du Colombier 			break;
7986083aa43SDavid du Colombier 		case Tunitent:
7996083aa43SDavid du Colombier 			/*
8006083aa43SDavid du Colombier 			 * not clear what needs to be done.  send up
8016083aa43SDavid du Colombier 			 * an icmp mesg saying `don't use this address'?
8026083aa43SDavid du Colombier 			 */
8036083aa43SDavid du Colombier 			break;
8046083aa43SDavid du Colombier 		}
8056083aa43SDavid du Colombier 		freeblist(bp);
8066083aa43SDavid du Colombier 		break;
8073ff48bf5SDavid du Colombier 	case NbrAdvert:
808ccf6439bSDavid du Colombier 		/*
809ccf6439bSDavid du Colombier 		 * if the target address matches one of the local interface
810ccf6439bSDavid du Colombier 		 * addresses and the local interface address has tentative bit
811ccf6439bSDavid du Colombier 		 * set, insert into ARP table. this is so the duplicate address
8123ff48bf5SDavid du Colombier 		 * detection part of ipconfig can discover duplication through
813ccf6439bSDavid du Colombier 		 * the arp table.
8143ff48bf5SDavid du Colombier 		 */
8156083aa43SDavid du Colombier 		np = (Ndpkt*)p;			/* within bp */
8163ff48bf5SDavid du Colombier 		lifc = iplocalonifc(ipifc, np->target);
817ccf6439bSDavid du Colombier 		arpenter(icmp->f, V6, np->target, np->lnaddr, 8*np->olen-2,
8186083aa43SDavid du Colombier 			lifc && lifc->tentative);
8193ff48bf5SDavid du Colombier 		freeblist(bp);
8203ff48bf5SDavid du Colombier 		break;
8213ff48bf5SDavid du Colombier 	case PacketTooBigV6:
8223ff48bf5SDavid du Colombier 	default:
8233ff48bf5SDavid du Colombier 		goticmpkt6(icmp, bp, 0);
8243ff48bf5SDavid du Colombier 		break;
8253ff48bf5SDavid du Colombier 	}
8263ff48bf5SDavid du Colombier 	return;
8276083aa43SDavid du Colombier 
8283ff48bf5SDavid du Colombier raise:
8293ff48bf5SDavid du Colombier 	freeblist(bp);
8303ff48bf5SDavid du Colombier }
8313ff48bf5SDavid du Colombier 
8323ff48bf5SDavid du Colombier int
icmpstats6(Proto * icmp6,char * buf,int len)8333ff48bf5SDavid du Colombier icmpstats6(Proto *icmp6, char *buf, int len)
8343ff48bf5SDavid du Colombier {
8353ff48bf5SDavid du Colombier 	Icmppriv6 *priv;
8363ff48bf5SDavid du Colombier 	char *p, *e;
8373ff48bf5SDavid du Colombier 	int i;
8383ff48bf5SDavid du Colombier 
8393ff48bf5SDavid du Colombier 	priv = icmp6->priv;
8403ff48bf5SDavid du Colombier 	p = buf;
8413ff48bf5SDavid du Colombier 	e = p+len;
8423ff48bf5SDavid du Colombier 	for(i = 0; i < Nstats6; i++)
8433ff48bf5SDavid du Colombier 		p = seprint(p, e, "%s: %lud\n", statnames6[i], priv->stats[i]);
844ccf6439bSDavid du Colombier 	for(i = 0; i <= Maxtype6; i++)
8453ff48bf5SDavid du Colombier 		if(icmpnames6[i])
846ccf6439bSDavid du Colombier 			p = seprint(p, e, "%s: %lud %lud\n", icmpnames6[i],
847ccf6439bSDavid du Colombier 				priv->in[i], priv->out[i]);
8486083aa43SDavid du Colombier 		else if (0)
849ccf6439bSDavid du Colombier 			p = seprint(p, e, "%d: %lud %lud\n", i, priv->in[i],
850ccf6439bSDavid du Colombier 				priv->out[i]);
8513ff48bf5SDavid du Colombier 	return p - buf;
8523ff48bf5SDavid du Colombier }
8533ff48bf5SDavid du Colombier 
854ccf6439bSDavid du Colombier /* import from icmp.c */
8553ff48bf5SDavid du Colombier extern int	icmpstate(Conv *c, char *state, int n);
8563ff48bf5SDavid du Colombier extern char*	icmpannounce(Conv *c, char **argv, int argc);
8573ff48bf5SDavid du Colombier extern char*	icmpconnect(Conv *c, char **argv, int argc);
8583ff48bf5SDavid du Colombier extern void	icmpclose(Conv *c);
8593ff48bf5SDavid du Colombier 
8603ff48bf5SDavid du Colombier void
icmp6init(Fs * fs)8613ff48bf5SDavid du Colombier icmp6init(Fs *fs)
8623ff48bf5SDavid du Colombier {
8633ff48bf5SDavid du Colombier 	Proto *icmp6 = smalloc(sizeof(Proto));
8643ff48bf5SDavid du Colombier 
8653ff48bf5SDavid du Colombier 	icmp6->priv = smalloc(sizeof(Icmppriv6));
8663ff48bf5SDavid du Colombier 	icmp6->name = "icmpv6";
8673ff48bf5SDavid du Colombier 	icmp6->connect = icmpconnect;
8683ff48bf5SDavid du Colombier 	icmp6->announce = icmpannounce;
8693ff48bf5SDavid du Colombier 	icmp6->state = icmpstate;
8703ff48bf5SDavid du Colombier 	icmp6->create = icmpcreate6;
8713ff48bf5SDavid du Colombier 	icmp6->close = icmpclose;
8723ff48bf5SDavid du Colombier 	icmp6->rcv = icmpiput6;
8733ff48bf5SDavid du Colombier 	icmp6->stats = icmpstats6;
8743ff48bf5SDavid du Colombier 	icmp6->ctl = icmpctl6;
8753ff48bf5SDavid du Colombier 	icmp6->advise = icmpadvise6;
8763ff48bf5SDavid du Colombier 	icmp6->gc = nil;
8773ff48bf5SDavid du Colombier 	icmp6->ipproto = ICMPv6;
8783ff48bf5SDavid du Colombier 	icmp6->nc = 16;
8793ff48bf5SDavid du Colombier 	icmp6->ptclsize = sizeof(Icmpcb6);
8803ff48bf5SDavid du Colombier 
8813ff48bf5SDavid du Colombier 	Fsproto(fs, icmp6);
8823ff48bf5SDavid du Colombier }
883