xref: /plan9-contrib/sys/src/cmd/ip/ipconfig/ipv6.c (revision fafe75d552bbcea7be972cac0a0a7720a586a83f)
1e47528f3SDavid du Colombier /*
2e47528f3SDavid du Colombier  * ipconfig for IPv6
38c6ab946SDavid du Colombier  *	RS means Router Solicitation
48c6ab946SDavid du Colombier  *	RA means Router Advertisement
5e47528f3SDavid du Colombier  */
6e47528f3SDavid du Colombier 
7e47528f3SDavid du Colombier #include <u.h>
8e47528f3SDavid du Colombier #include <libc.h>
9e47528f3SDavid du Colombier #include <bio.h>
10e47528f3SDavid du Colombier #include <ip.h>
11e47528f3SDavid du Colombier #include "ipconfig.h"
12e47528f3SDavid du Colombier #include "../icmp.h"
13e47528f3SDavid du Colombier 
14e47528f3SDavid du Colombier #pragma varargck argpos ralog 1
15e47528f3SDavid du Colombier 
16e47528f3SDavid du Colombier #define RALOG "v6routeradv"
17e47528f3SDavid du Colombier 
18e47528f3SDavid du Colombier #define NetS(x) (((uchar*)x)[0]<< 8 | ((uchar*)x)[1])
19e47528f3SDavid du Colombier #define NetL(x) (((uchar*)x)[0]<<24 | ((uchar*)x)[1]<<16 | \
20e47528f3SDavid du Colombier 		 ((uchar*)x)[2]<< 8 | ((uchar*)x)[3])
21e47528f3SDavid du Colombier 
22e47528f3SDavid du Colombier enum {
23e47528f3SDavid du Colombier 	ICMP6LEN=	4,
24e47528f3SDavid du Colombier };
25e47528f3SDavid du Colombier 
26e47528f3SDavid du Colombier typedef struct Hdr Hdr;
27e47528f3SDavid du Colombier struct Hdr			/* ICMP v4 & v6 header */
28e47528f3SDavid du Colombier {
29e47528f3SDavid du Colombier 	uchar	type;
30e47528f3SDavid du Colombier 	uchar	code;
31e47528f3SDavid du Colombier 	uchar	cksum[2];	/* Checksum */
3294aa1c4cSDavid du Colombier 	uchar	data[];
33e47528f3SDavid du Colombier };
34e47528f3SDavid du Colombier 
35e47528f3SDavid du Colombier char *icmpmsg6[Maxtype6+1] =
36e47528f3SDavid du Colombier {
37e47528f3SDavid du Colombier [EchoReply]		"EchoReply",
38e47528f3SDavid du Colombier [UnreachableV6]		"UnreachableV6",
39e47528f3SDavid du Colombier [PacketTooBigV6]	"PacketTooBigV6",
40e47528f3SDavid du Colombier [TimeExceedV6]		"TimeExceedV6",
41e47528f3SDavid du Colombier [Redirect]		"Redirect",
42e47528f3SDavid du Colombier [EchoRequest]		"EchoRequest",
43e47528f3SDavid du Colombier [TimeExceed]		"TimeExceed",
44e47528f3SDavid du Colombier [InParmProblem]		"InParmProblem",
45e47528f3SDavid du Colombier [Timestamp]		"Timestamp",
46e47528f3SDavid du Colombier [TimestampReply]	"TimestampReply",
47e47528f3SDavid du Colombier [InfoRequest]		"InfoRequest",
48e47528f3SDavid du Colombier [InfoReply]		"InfoReply",
49e47528f3SDavid du Colombier [AddrMaskRequest]	"AddrMaskRequest",
50e47528f3SDavid du Colombier [AddrMaskReply]		"AddrMaskReply",
51e47528f3SDavid du Colombier [EchoRequestV6]		"EchoRequestV6",
52e47528f3SDavid du Colombier [EchoReplyV6]		"EchoReplyV6",
53e47528f3SDavid du Colombier [RouterSolicit]		"RouterSolicit",
54e47528f3SDavid du Colombier [RouterAdvert]		"RouterAdvert",
55e47528f3SDavid du Colombier [NbrSolicit]		"NbrSolicit",
56e47528f3SDavid du Colombier [NbrAdvert]		"NbrAdvert",
57e47528f3SDavid du Colombier [RedirectV6]		"RedirectV6",
58e47528f3SDavid du Colombier };
59e47528f3SDavid du Colombier 
60e47528f3SDavid du Colombier static char *icmp6opts[] =
61e47528f3SDavid du Colombier {
62de1e7cd0SDavid du Colombier [0]			"unknown option",
630a84db5eSDavid du Colombier [V6nd_srclladdr]	"srcll_addr",
640a84db5eSDavid du Colombier [V6nd_targlladdr]	"targll_addr",
650a84db5eSDavid du Colombier [V6nd_pfxinfo]		"prefix",
668c6ab946SDavid du Colombier [V6nd_redirhdr]		"redirect",
670a84db5eSDavid du Colombier [V6nd_mtu]		"mtu",
680a84db5eSDavid du Colombier [V6nd_home]		"home",
690a84db5eSDavid du Colombier [V6nd_srcaddrs]		"src_addrs",
700a84db5eSDavid du Colombier [V6nd_ip]		"ip",
710a84db5eSDavid du Colombier [V6nd_rdns]		"rdns",
720a84db5eSDavid du Colombier [V6nd_9fs]		"9fs",
730a84db5eSDavid du Colombier [V6nd_9auth]		"9auth",
74e47528f3SDavid du Colombier };
75e47528f3SDavid du Colombier 
76e47528f3SDavid du Colombier uchar v6allroutersL[IPaddrlen] = {
77e47528f3SDavid du Colombier 	0xff, 0x02, 0, 0,
78e47528f3SDavid du Colombier 	0, 0, 0, 0,
79e47528f3SDavid du Colombier 	0, 0, 0, 0,
80e47528f3SDavid du Colombier 	0, 0, 0, 0x02
81e47528f3SDavid du Colombier };
82e47528f3SDavid du Colombier 
83e47528f3SDavid du Colombier uchar v6allnodesL[IPaddrlen] = {
84e47528f3SDavid du Colombier 	0xff, 0x02, 0, 0,
85e47528f3SDavid du Colombier 	0, 0, 0, 0,
86e47528f3SDavid du Colombier 	0, 0, 0, 0,
87e47528f3SDavid du Colombier 	0, 0, 0, 0x01
88e47528f3SDavid du Colombier };
89e47528f3SDavid du Colombier 
90e47528f3SDavid du Colombier uchar v6Unspecified[IPaddrlen] = {
91e47528f3SDavid du Colombier 	0, 0, 0, 0,
92e47528f3SDavid du Colombier 	0, 0, 0, 0,
93e47528f3SDavid du Colombier 	0, 0, 0, 0,
94e47528f3SDavid du Colombier 	0, 0, 0, 0
95e47528f3SDavid du Colombier };
96e47528f3SDavid du Colombier 
975e1edbcaSDavid du Colombier uchar v6loopback[IPaddrlen] = {
985e1edbcaSDavid du Colombier 	0, 0, 0, 0,
995e1edbcaSDavid du Colombier 	0, 0, 0, 0,
1005e1edbcaSDavid du Colombier 	0, 0, 0, 0,
1015e1edbcaSDavid du Colombier 	0, 0, 0, 1
1025e1edbcaSDavid du Colombier };
1035e1edbcaSDavid du Colombier 
104e47528f3SDavid du Colombier uchar v6glunicast[IPaddrlen] = {
105e47528f3SDavid du Colombier 	0x08, 0, 0, 0,
106e47528f3SDavid du Colombier 	0, 0, 0, 0,
107e47528f3SDavid du Colombier 	0, 0, 0, 0,
108e47528f3SDavid du Colombier 	0, 0, 0, 0
109e47528f3SDavid du Colombier };
110e47528f3SDavid du Colombier 
111e47528f3SDavid du Colombier uchar v6linklocal[IPaddrlen] = {
112e47528f3SDavid du Colombier 	0xfe, 0x80, 0, 0,
113e47528f3SDavid du Colombier 	0, 0, 0, 0,
114e47528f3SDavid du Colombier 	0, 0, 0, 0,
115e47528f3SDavid du Colombier 	0, 0, 0, 0
116e47528f3SDavid du Colombier };
117e47528f3SDavid du Colombier 
118e47528f3SDavid du Colombier uchar v6solpfx[IPaddrlen] = {
119e47528f3SDavid du Colombier 	0xff, 0x02, 0, 0,
120e47528f3SDavid du Colombier 	0, 0, 0, 0,
121e47528f3SDavid du Colombier 	0, 0, 0, 1,
122e47528f3SDavid du Colombier 	/* last 3 bytes filled with low-order bytes of addr being solicited */
123e47528f3SDavid du Colombier 	0xff, 0, 0, 0,
124e47528f3SDavid du Colombier };
125e47528f3SDavid du Colombier 
126e47528f3SDavid du Colombier uchar v6defmask[IPaddrlen] = {
127e47528f3SDavid du Colombier 	0xff, 0xff, 0xff, 0xff,
128e47528f3SDavid du Colombier 	0xff, 0xff, 0xff, 0xff,
129e47528f3SDavid du Colombier 	0, 0, 0, 0,
130e47528f3SDavid du Colombier 	0, 0, 0, 0
131e47528f3SDavid du Colombier };
132e47528f3SDavid du Colombier 
133e47528f3SDavid du Colombier enum
134e47528f3SDavid du Colombier {
135e47528f3SDavid du Colombier 	Vadd,
136e47528f3SDavid du Colombier 	Vremove,
137e47528f3SDavid du Colombier 	Vunbind,
138e47528f3SDavid du Colombier 	Vaddpref6,
139e47528f3SDavid du Colombier 	Vra6,
140e47528f3SDavid du Colombier };
141e47528f3SDavid du Colombier 
142e47528f3SDavid du Colombier static void
ralog(char * fmt,...)143e47528f3SDavid du Colombier ralog(char *fmt, ...)
144e47528f3SDavid du Colombier {
145e47528f3SDavid du Colombier 	char msg[512];
146e47528f3SDavid du Colombier 	va_list arg;
147e47528f3SDavid du Colombier 
148e47528f3SDavid du Colombier 	va_start(arg, fmt);
149e47528f3SDavid du Colombier 	vseprint(msg, msg+sizeof msg, fmt, arg);
150e47528f3SDavid du Colombier 	va_end(arg);
151e47528f3SDavid du Colombier 	syslog(debug, RALOG, msg);
152e47528f3SDavid du Colombier }
153e47528f3SDavid du Colombier 
154e47528f3SDavid du Colombier extern void
ea2lla(uchar * lla,uchar * ea)155e47528f3SDavid du Colombier ea2lla(uchar *lla, uchar *ea)
156e47528f3SDavid du Colombier {
157e47528f3SDavid du Colombier 	assert(IPaddrlen == 16);
158e47528f3SDavid du Colombier 	memset(lla, 0, IPaddrlen);
159e47528f3SDavid du Colombier 	lla[0]  = 0xFE;
160e47528f3SDavid du Colombier 	lla[1]  = 0x80;
161e47528f3SDavid du Colombier 	lla[8]  = ea[0] | 0x2;
162e47528f3SDavid du Colombier 	lla[9]  = ea[1];
163e47528f3SDavid du Colombier 	lla[10] = ea[2];
164e47528f3SDavid du Colombier 	lla[11] = 0xFF;
165e47528f3SDavid du Colombier 	lla[12] = 0xFE;
166e47528f3SDavid du Colombier 	lla[13] = ea[3];
167e47528f3SDavid du Colombier 	lla[14] = ea[4];
168e47528f3SDavid du Colombier 	lla[15] = ea[5];
169e47528f3SDavid du Colombier }
170e47528f3SDavid du Colombier 
171e47528f3SDavid du Colombier extern void
ipv62smcast(uchar * smcast,uchar * a)172e47528f3SDavid du Colombier ipv62smcast(uchar *smcast, uchar *a)
173e47528f3SDavid du Colombier {
174e47528f3SDavid du Colombier 	assert(IPaddrlen == 16);
175e47528f3SDavid du Colombier 	memset(smcast, 0, IPaddrlen);
176e47528f3SDavid du Colombier 	smcast[0]  = 0xFF;
177e47528f3SDavid du Colombier 	smcast[1]  = 0x02;
178e47528f3SDavid du Colombier 	smcast[11] = 0x1;
179e47528f3SDavid du Colombier 	smcast[12] = 0xFF;
180e47528f3SDavid du Colombier 	smcast[13] = a[13];
181e47528f3SDavid du Colombier 	smcast[14] = a[14];
182e47528f3SDavid du Colombier 	smcast[15] = a[15];
183e47528f3SDavid du Colombier }
184e47528f3SDavid du Colombier 
185e47528f3SDavid du Colombier void
v6paraminit(Conf * cf)186ccaac148SDavid du Colombier v6paraminit(Conf *cf)
187e47528f3SDavid du Colombier {
188ccaac148SDavid du Colombier 	cf->sendra = cf->recvra = 0;
189ccaac148SDavid du Colombier 	cf->mflag = 0;
190ccaac148SDavid du Colombier 	cf->oflag = 0;
1915e1edbcaSDavid du Colombier 	cf->maxraint = Maxv6initraintvl;
1925e1edbcaSDavid du Colombier 	cf->minraint = Maxv6initraintvl / 4;
193ccaac148SDavid du Colombier 	cf->linkmtu = 1500;
1945e1edbcaSDavid du Colombier 	cf->reachtime = V6reachabletime;
1955e1edbcaSDavid du Colombier 	cf->rxmitra = V6retranstimer;
196ccaac148SDavid du Colombier 	cf->ttl = MAXTTL;
197e47528f3SDavid du Colombier 
198ccaac148SDavid du Colombier 	cf->routerlt = 0;
199e47528f3SDavid du Colombier 
200ccaac148SDavid du Colombier 	cf->prefixlen = 64;
201ccaac148SDavid du Colombier 	cf->onlink = 0;
202ccaac148SDavid du Colombier 	cf->autoflag = 0;
203ccaac148SDavid du Colombier 	cf->validlt = cf->preflt = ~0L;
204e47528f3SDavid du Colombier }
205e47528f3SDavid du Colombier 
206e47528f3SDavid du Colombier static char *
optname(unsigned opt)2070a84db5eSDavid du Colombier optname(unsigned opt)
2080a84db5eSDavid du Colombier {
2090a84db5eSDavid du Colombier 	static char buf[32];
2100a84db5eSDavid du Colombier 
2110a84db5eSDavid du Colombier 	if (opt >= nelem(icmp6opts) || icmp6opts[opt] == nil) {
2120a84db5eSDavid du Colombier 		snprint(buf, sizeof buf, "unknown option %d", opt);
2130a84db5eSDavid du Colombier 		return buf;
2140a84db5eSDavid du Colombier 	} else
2150a84db5eSDavid du Colombier 		return icmp6opts[opt];
2160a84db5eSDavid du Colombier }
2170a84db5eSDavid du Colombier 
2180a84db5eSDavid du Colombier static char*
opt_seprint(uchar * ps,uchar * pe,char * sps,char * spe)219e47528f3SDavid du Colombier opt_seprint(uchar *ps, uchar *pe, char *sps, char *spe)
220e47528f3SDavid du Colombier {
221e47528f3SDavid du Colombier 	int otype, osz, pktsz;
222e47528f3SDavid du Colombier 	uchar *a;
223e47528f3SDavid du Colombier 	char *p = sps, *e = spe;
224e47528f3SDavid du Colombier 
225e47528f3SDavid du Colombier 	a = ps;
226e47528f3SDavid du Colombier 	for (pktsz = pe - ps; pktsz > 0; pktsz -= osz) {
227e47528f3SDavid du Colombier 		otype = a[0];
228e47528f3SDavid du Colombier 		osz = a[1] * 8;
229e47528f3SDavid du Colombier 
230e47528f3SDavid du Colombier 		switch (otype) {
231e47528f3SDavid du Colombier 		default:
2320a84db5eSDavid du Colombier 			return seprint(p, e, " option=%s ", optname(otype));
2338c6ab946SDavid du Colombier 		case V6nd_srclladdr:
2348c6ab946SDavid du Colombier 		case V6nd_targlladdr:
235e47528f3SDavid du Colombier 			if (pktsz < osz || osz != 8)
236e47528f3SDavid du Colombier 				return seprint(p, e, " option=%s bad size=%d",
2370a84db5eSDavid du Colombier 					optname(otype), osz);
2380a84db5eSDavid du Colombier 			p = seprint(p, e, " option=%s maddr=%E", optname(otype),
2390a84db5eSDavid du Colombier 				a+2);
240e47528f3SDavid du Colombier 			break;
2418c6ab946SDavid du Colombier 		case V6nd_pfxinfo:
242e47528f3SDavid du Colombier 			if (pktsz < osz || osz != 32)
243e47528f3SDavid du Colombier 				return seprint(p, e, " option=%s: bad size=%d",
2440a84db5eSDavid du Colombier 					optname(otype), osz);
245e47528f3SDavid du Colombier 
246e47528f3SDavid du Colombier 			p = seprint(p, e, " option=%s pref=%I preflen=%3.3d"
247e47528f3SDavid du Colombier 				" lflag=%1.1d aflag=%1.1d unused1=%1.1d"
248e47528f3SDavid du Colombier 				" validlt=%ud preflt=%ud unused2=%1.1d",
2490a84db5eSDavid du Colombier 				optname(otype), a+16, (int)(*(a+2)),
250e47528f3SDavid du Colombier 				(*(a+3) & (1 << 7)) != 0,
251e47528f3SDavid du Colombier 				(*(a+3) & (1 << 6)) != 0,
252e47528f3SDavid du Colombier 				(*(a+3) & 63) != 0,
253e47528f3SDavid du Colombier 				NetL(a+4), NetL(a+8), NetL(a+12)!=0);
254e47528f3SDavid du Colombier 			break;
255e47528f3SDavid du Colombier 		}
256e47528f3SDavid du Colombier 		a += osz;
257e47528f3SDavid du Colombier 	}
258e47528f3SDavid du Colombier 	return p;
259e47528f3SDavid du Colombier }
260e47528f3SDavid du Colombier 
261e47528f3SDavid du Colombier static void
pkt2str(uchar * ps,uchar * pe,char * sps,char * spe)262e47528f3SDavid du Colombier pkt2str(uchar *ps, uchar *pe, char *sps, char *spe)
263e47528f3SDavid du Colombier {
264e47528f3SDavid du Colombier 	int pktlen;
265e47528f3SDavid du Colombier 	char *tn, *p, *e;
266e47528f3SDavid du Colombier 	uchar *a;
267e47528f3SDavid du Colombier 	Hdr *h;
268e47528f3SDavid du Colombier 
269e47528f3SDavid du Colombier 	h = (Hdr*)ps;
270e47528f3SDavid du Colombier 	a = ps + 4;
271e47528f3SDavid du Colombier 	p = sps;
272e47528f3SDavid du Colombier 	e = spe;
273e47528f3SDavid du Colombier 
274e47528f3SDavid du Colombier 	pktlen = pe - ps;
275e47528f3SDavid du Colombier 	if(pktlen < ICMP6LEN) {
276e47528f3SDavid du Colombier 		seprint(sps, spe, "short pkt");
277e47528f3SDavid du Colombier 		return;
278e47528f3SDavid du Colombier 	}
279e47528f3SDavid du Colombier 
280e47528f3SDavid du Colombier 	tn = icmpmsg6[h->type];
281e47528f3SDavid du Colombier 	if(tn == nil)
282e47528f3SDavid du Colombier 		p = seprint(p, e, "t=%ud c=%d ck=%4.4ux", h->type,
283e47528f3SDavid du Colombier 			h->code, (ushort)NetS(h->cksum));
284e47528f3SDavid du Colombier 	else
285e47528f3SDavid du Colombier 		p = seprint(p, e, "t=%s c=%d ck=%4.4ux", tn,
286e47528f3SDavid du Colombier 			h->code, (ushort)NetS(h->cksum));
287e47528f3SDavid du Colombier 
288e47528f3SDavid du Colombier 	switch(h->type){
289e47528f3SDavid du Colombier 	case RouterSolicit:
290e47528f3SDavid du Colombier 		ps += 8;
291e47528f3SDavid du Colombier 		p = seprint(p, e, " unused=%1.1d ", NetL(a)!=0);
292e47528f3SDavid du Colombier 		opt_seprint(ps, pe, p, e);
293e47528f3SDavid du Colombier 		break;
294e47528f3SDavid du Colombier 	case RouterAdvert:
295e47528f3SDavid du Colombier 		ps += 16;
296e47528f3SDavid du Colombier 		p = seprint(p, e, " hoplim=%3.3d mflag=%1.1d oflag=%1.1d"
297e47528f3SDavid du Colombier 			" unused=%1.1d routerlt=%d reachtime=%d rxmtimer=%d",
298e47528f3SDavid du Colombier 			a[0],
299e47528f3SDavid du Colombier 			(*(a+1) & (1 << 7)) != 0,
300e47528f3SDavid du Colombier 			(*(a+1) & (1 << 6)) != 0,
301e47528f3SDavid du Colombier 			(*(a+1) & 63) != 0,
302e47528f3SDavid du Colombier 			NetS(a+2), NetL(a+4), NetL(a+8));
303e47528f3SDavid du Colombier 		opt_seprint(ps, pe, p, e);
304e47528f3SDavid du Colombier 		break;
305e47528f3SDavid du Colombier 	default:
306e47528f3SDavid du Colombier 		seprint(p, e, " unexpected icmp6 pkt type");
307e47528f3SDavid du Colombier 		break;
308e47528f3SDavid du Colombier 	}
309e47528f3SDavid du Colombier }
310e47528f3SDavid du Colombier 
311e47528f3SDavid du Colombier static void
catch(void * a,char * msg)312e47528f3SDavid du Colombier catch(void *a, char *msg)
313e47528f3SDavid du Colombier {
314e47528f3SDavid du Colombier 	USED(a);
315e47528f3SDavid du Colombier 	if(strstr(msg, "alarm"))
316e47528f3SDavid du Colombier 		noted(NCONT);
317e47528f3SDavid du Colombier 	else
318e47528f3SDavid du Colombier 		noted(NDFLT);
319e47528f3SDavid du Colombier }
320e47528f3SDavid du Colombier 
321e47528f3SDavid du Colombier /*
322e47528f3SDavid du Colombier  * based on libthread's threadsetname, but drags in less library code.
323e47528f3SDavid du Colombier  * actually just sets the arguments displayed.
324e47528f3SDavid du Colombier  */
325e47528f3SDavid du Colombier void
procsetname(char * fmt,...)326e47528f3SDavid du Colombier procsetname(char *fmt, ...)
327e47528f3SDavid du Colombier {
328e47528f3SDavid du Colombier 	int fd;
329e47528f3SDavid du Colombier 	char *cmdname;
330e47528f3SDavid du Colombier 	char buf[128];
331e47528f3SDavid du Colombier 	va_list arg;
332e47528f3SDavid du Colombier 
333e47528f3SDavid du Colombier 	va_start(arg, fmt);
334e47528f3SDavid du Colombier 	cmdname = vsmprint(fmt, arg);
335e47528f3SDavid du Colombier 	va_end(arg);
336e47528f3SDavid du Colombier 	if (cmdname == nil)
337e47528f3SDavid du Colombier 		return;
338e47528f3SDavid du Colombier 	snprint(buf, sizeof buf, "#p/%d/args", getpid());
339e47528f3SDavid du Colombier 	if((fd = open(buf, OWRITE)) >= 0){
340e47528f3SDavid du Colombier 		write(fd, cmdname, strlen(cmdname)+1);
341e47528f3SDavid du Colombier 		close(fd);
342e47528f3SDavid du Colombier 	}
343e47528f3SDavid du Colombier 	free(cmdname);
344e47528f3SDavid du Colombier }
345e47528f3SDavid du Colombier 
346e47528f3SDavid du Colombier int
dialicmp(uchar * dst,int dport,int * ctlfd)347e47528f3SDavid du Colombier dialicmp(uchar *dst, int dport, int *ctlfd)
348e47528f3SDavid du Colombier {
349e47528f3SDavid du Colombier 	int fd, cfd, n, m;
350e47528f3SDavid du Colombier 	char cmsg[100], name[128], connind[40];
351e47528f3SDavid du Colombier 	char hdrs[] = "headers";
352e47528f3SDavid du Colombier 
353e47528f3SDavid du Colombier 	snprint(name, sizeof name, "%s/icmpv6/clone", conf.mpoint);
354e47528f3SDavid du Colombier 	cfd = open(name, ORDWR);
355e47528f3SDavid du Colombier 	if(cfd < 0)
356e47528f3SDavid du Colombier 		sysfatal("dialicmp: can't open %s: %r", name);
357e47528f3SDavid du Colombier 
358e47528f3SDavid du Colombier 	n = snprint(cmsg, sizeof cmsg, "connect %I!%d!r %d", dst, dport, dport);
359e47528f3SDavid du Colombier 	m = write(cfd, cmsg, n);
360e47528f3SDavid du Colombier 	if (m < n)
361e47528f3SDavid du Colombier 		sysfatal("dialicmp: can't write %s to %s: %r", cmsg, name);
362e47528f3SDavid du Colombier 
363e47528f3SDavid du Colombier 	seek(cfd, 0, 0);
364e47528f3SDavid du Colombier 	n = read(cfd, connind, sizeof connind);
365e47528f3SDavid du Colombier 	if (n < 0)
366e47528f3SDavid du Colombier 		connind[0] = 0;
367e47528f3SDavid du Colombier 	else if (n < sizeof connind)
368e47528f3SDavid du Colombier 		connind[n] = 0;
369e47528f3SDavid du Colombier 	else
370e47528f3SDavid du Colombier 		connind[sizeof connind - 1] = 0;
371e47528f3SDavid du Colombier 
372e47528f3SDavid du Colombier 	snprint(name, sizeof name, "%s/icmpv6/%s/data", conf.mpoint, connind);
373e47528f3SDavid du Colombier 	fd = open(name, ORDWR);
374e47528f3SDavid du Colombier 	if(fd < 0)
375e47528f3SDavid du Colombier 		sysfatal("dialicmp: can't open %s: %r", name);
376e47528f3SDavid du Colombier 
377e47528f3SDavid du Colombier 	n = sizeof hdrs - 1;
378e47528f3SDavid du Colombier 	if(write(cfd, hdrs, n) < n)
379e47528f3SDavid du Colombier 		sysfatal("dialicmp: can't write `%s' to %s: %r", hdrs, name);
380e47528f3SDavid du Colombier 	*ctlfd = cfd;
381e47528f3SDavid du Colombier 	return fd;
382e47528f3SDavid du Colombier }
383e47528f3SDavid du Colombier 
384e47528f3SDavid du Colombier /* add ipv6 addr to an interface */
385e47528f3SDavid du Colombier int
ip6cfg(int autoconf)386e47528f3SDavid du Colombier ip6cfg(int autoconf)
387e47528f3SDavid du Colombier {
388*fafe75d5SDavid du Colombier 	int dupfound = 0, n, nf;
389*fafe75d5SDavid du Colombier 	char *p, *fields[4];
390e47528f3SDavid du Colombier 	char buf[256];
391e47528f3SDavid du Colombier 	uchar ethaddr[6];
392e47528f3SDavid du Colombier 	Biobuf *bp;
393e47528f3SDavid du Colombier 
394e47528f3SDavid du Colombier 	if (autoconf) {			/* create link-local addr */
395e47528f3SDavid du Colombier 		if (myetheraddr(ethaddr, conf.dev) < 0)
396e47528f3SDavid du Colombier 			sysfatal("myetheraddr w/ %s failed: %r", conf.dev);
397e47528f3SDavid du Colombier 		ea2lla(conf.laddr, ethaddr);
398e47528f3SDavid du Colombier 	}
399e47528f3SDavid du Colombier 
400e47528f3SDavid du Colombier 	if (dupl_disc)
401e47528f3SDavid du Colombier 		n = sprint(buf, "try");
402e47528f3SDavid du Colombier 	else
403e47528f3SDavid du Colombier 		n = sprint(buf, "add");
404e47528f3SDavid du Colombier 
405e47528f3SDavid du Colombier 	n += snprint(buf+n, sizeof buf-n, " %I", conf.laddr);
406e47528f3SDavid du Colombier 	if(!validip(conf.mask))
407e47528f3SDavid du Colombier 		ipmove(conf.mask, v6defmask);
408e47528f3SDavid du Colombier 	n += snprint(buf+n, sizeof buf-n, " %M", conf.mask);
409e47528f3SDavid du Colombier 	if(validip(conf.raddr)){
410e47528f3SDavid du Colombier 		n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
411e47528f3SDavid du Colombier 		if(conf.mtu != 0)
412e47528f3SDavid du Colombier 			n += snprint(buf+n, sizeof buf-n, " %d", conf.mtu);
413e47528f3SDavid du Colombier 	}
414e47528f3SDavid du Colombier 
415e47528f3SDavid du Colombier 	if(write(conf.cfd, buf, n) < 0){
4168c6ab946SDavid du Colombier 		warning("write(%s): %r", buf);
417e47528f3SDavid du Colombier 		return -1;
418e47528f3SDavid du Colombier 	}
419e47528f3SDavid du Colombier 
420e47528f3SDavid du Colombier 	if (!dupl_disc)
421e47528f3SDavid du Colombier 		return 0;
422e47528f3SDavid du Colombier 
423e47528f3SDavid du Colombier 	sleep(3000);
424e47528f3SDavid du Colombier 
425e47528f3SDavid du Colombier 	/* read arp table, look for addr duplication */
426e47528f3SDavid du Colombier 	snprint(buf, sizeof buf, "%s/arp", conf.mpoint);
427e47528f3SDavid du Colombier 	bp = Bopen(buf, OREAD);
428e47528f3SDavid du Colombier 	if (bp == 0) {
4298c6ab946SDavid du Colombier 		warning("couldn't open %s: %r", buf);
430e47528f3SDavid du Colombier 		return -1;
431e47528f3SDavid du Colombier 	}
432e47528f3SDavid du Colombier 
433e47528f3SDavid du Colombier 	snprint(buf, sizeof buf, "%I", conf.laddr);
434e47528f3SDavid du Colombier 	while(p = Brdline(bp, '\n')){
435e47528f3SDavid du Colombier 		p[Blinelen(bp)-1] = 0;
436*fafe75d5SDavid du Colombier 		nf = tokenize(p, fields, nelem(fields));
437*fafe75d5SDavid du Colombier 		if(nf >= 4 && cistrcmp(fields[2], buf) == 0) {
4388c6ab946SDavid du Colombier 			warning("found dup entry in arp cache");
439e47528f3SDavid du Colombier 			dupfound = 1;
440e47528f3SDavid du Colombier 			break;
441e47528f3SDavid du Colombier 		}
442e47528f3SDavid du Colombier 	}
443e47528f3SDavid du Colombier 	Bterm(bp);
444e47528f3SDavid du Colombier 
445e47528f3SDavid du Colombier 	if (dupfound)
446e47528f3SDavid du Colombier 		doremove();
447e47528f3SDavid du Colombier 	else {
448e47528f3SDavid du Colombier 		n = sprint(buf, "add %I %M", conf.laddr, conf.mask);
449e47528f3SDavid du Colombier 		if(validip(conf.raddr)){
450e47528f3SDavid du Colombier 			n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
451e47528f3SDavid du Colombier 			if(conf.mtu != 0)
452e47528f3SDavid du Colombier 				n += snprint(buf+n, sizeof buf-n, " %d",
453e47528f3SDavid du Colombier 					conf.mtu);
454e47528f3SDavid du Colombier 		}
455e47528f3SDavid du Colombier 		write(conf.cfd, buf, n);
456e47528f3SDavid du Colombier 	}
457e47528f3SDavid du Colombier 	return 0;
458e47528f3SDavid du Colombier }
459e47528f3SDavid du Colombier 
460e47528f3SDavid du Colombier static int
recvra6on(char * net,int conn)461e47528f3SDavid du Colombier recvra6on(char *net, int conn)
462e47528f3SDavid du Colombier {
463e47528f3SDavid du Colombier 	Ipifc* ifc;
464e47528f3SDavid du Colombier 
465e47528f3SDavid du Colombier 	ifc = readipifc(net, nil, conn);
466e47528f3SDavid du Colombier 	if (ifc == nil)
467e47528f3SDavid du Colombier 		return 0;
468e47528f3SDavid du Colombier 	else if (ifc->sendra6 > 0)
469e47528f3SDavid du Colombier 		return IsRouter;
470e47528f3SDavid du Colombier 	else if (ifc->recvra6 > 0)
471e47528f3SDavid du Colombier 		return IsHostRecv;
472e47528f3SDavid du Colombier 	else
473e47528f3SDavid du Colombier 		return IsHostNoRecv;
474e47528f3SDavid du Colombier }
475e47528f3SDavid du Colombier 
4760a84db5eSDavid du Colombier /* send icmpv6 router solicitation to multicast address for all routers */
477e47528f3SDavid du Colombier static void
sendrs(int fd)478e47528f3SDavid du Colombier sendrs(int fd)
479e47528f3SDavid du Colombier {
480e47528f3SDavid du Colombier 	Routersol *rs;
481e47528f3SDavid du Colombier 	uchar buff[sizeof *rs];
482e47528f3SDavid du Colombier 
483e47528f3SDavid du Colombier 	memset(buff, 0, sizeof buff);
484e47528f3SDavid du Colombier 	rs = (Routersol *)buff;
485e47528f3SDavid du Colombier 	memmove(rs->dst, v6allroutersL, IPaddrlen);
486e47528f3SDavid du Colombier 	memmove(rs->src, v6Unspecified, IPaddrlen);
487e47528f3SDavid du Colombier 	rs->type = ICMP6_RS;
488e47528f3SDavid du Colombier 
489e47528f3SDavid du Colombier 	if(write(fd, rs, sizeof buff) < sizeof buff)
490e47528f3SDavid du Colombier 		ralog("sendrs: write failed, pkt size %d", sizeof buff);
4910a84db5eSDavid du Colombier 	else
4920a84db5eSDavid du Colombier 		ralog("sendrs: sent solicitation to %I from %I on %s",
4930a84db5eSDavid du Colombier 			rs->dst, rs->src, conf.dev);
494e47528f3SDavid du Colombier }
495e47528f3SDavid du Colombier 
496e47528f3SDavid du Colombier /*
497e47528f3SDavid du Colombier  * a router receiving a router adv from another
498e47528f3SDavid du Colombier  * router calls this; it is basically supposed to
499e47528f3SDavid du Colombier  * log the information in the ra and raise a flag
500e47528f3SDavid du Colombier  * if any parameter value is different from its configured values.
501e47528f3SDavid du Colombier  *
502e47528f3SDavid du Colombier  * doing nothing for now since I don't know where to log this yet.
503e47528f3SDavid du Colombier  */
504e47528f3SDavid du Colombier static void
recvrarouter(uchar buf[],int pktlen)505e47528f3SDavid du Colombier recvrarouter(uchar buf[], int pktlen)
506e47528f3SDavid du Colombier {
507e47528f3SDavid du Colombier 	USED(buf, pktlen);
508e47528f3SDavid du Colombier 	ralog("i am a router and got a router advert");
509e47528f3SDavid du Colombier }
510e47528f3SDavid du Colombier 
511e47528f3SDavid du Colombier /* host receiving a router advertisement calls this */
512e47528f3SDavid du Colombier 
513e47528f3SDavid du Colombier static void
ewrite(int fd,char * str)514ccaac148SDavid du Colombier ewrite(int fd, char *str)
515ccaac148SDavid du Colombier {
516ccaac148SDavid du Colombier 	int n;
517ccaac148SDavid du Colombier 
518ccaac148SDavid du Colombier 	n = strlen(str);
519ccaac148SDavid du Colombier 	if (write(fd, str, n) != n)
520ccaac148SDavid du Colombier 		ralog("write(%s) failed: %r", str);
521ccaac148SDavid du Colombier }
522ccaac148SDavid du Colombier 
523ccaac148SDavid du Colombier static void
issuebasera6(Conf * cf)524ccaac148SDavid du Colombier issuebasera6(Conf *cf)
525ccaac148SDavid du Colombier {
526ccaac148SDavid du Colombier 	char *cfg;
527ccaac148SDavid du Colombier 
528ccaac148SDavid du Colombier 	cfg = smprint("ra6 mflag %d oflag %d reachtime %d rxmitra %d "
529ccaac148SDavid du Colombier 		"ttl %d routerlt %d",
530ccaac148SDavid du Colombier 		cf->mflag, cf->oflag, cf->reachtime, cf->rxmitra,
531ccaac148SDavid du Colombier 		cf->ttl, cf->routerlt);
532ccaac148SDavid du Colombier 	ewrite(cf->cfd, cfg);
533ccaac148SDavid du Colombier 	free(cfg);
534ccaac148SDavid du Colombier }
535ccaac148SDavid du Colombier 
536ccaac148SDavid du Colombier static void
issuerara6(Conf * cf)537ccaac148SDavid du Colombier issuerara6(Conf *cf)
538ccaac148SDavid du Colombier {
539ccaac148SDavid du Colombier 	char *cfg;
540ccaac148SDavid du Colombier 
541ccaac148SDavid du Colombier 	cfg = smprint("ra6 sendra %d recvra %d maxraint %d minraint %d "
542ccaac148SDavid du Colombier 		"linkmtu %d",
543ccaac148SDavid du Colombier 		cf->sendra, cf->recvra, cf->maxraint, cf->minraint,
544ccaac148SDavid du Colombier 		cf->linkmtu);
545ccaac148SDavid du Colombier 	ewrite(cf->cfd, cfg);
546ccaac148SDavid du Colombier 	free(cfg);
547ccaac148SDavid du Colombier }
548ccaac148SDavid du Colombier 
549ccaac148SDavid du Colombier static void
issueadd6(Conf * cf)550ccaac148SDavid du Colombier issueadd6(Conf *cf)
551ccaac148SDavid du Colombier {
552ccaac148SDavid du Colombier 	char *cfg;
553ccaac148SDavid du Colombier 
554ccaac148SDavid du Colombier 	cfg = smprint("add6 %I %d %d %d %lud %lud", cf->v6pref, cf->prefixlen,
555ccaac148SDavid du Colombier 		cf->onlink, cf->autoflag, cf->validlt, cf->preflt);
556ccaac148SDavid du Colombier 	ewrite(cf->cfd, cfg);
557ccaac148SDavid du Colombier 	free(cfg);
558ccaac148SDavid du Colombier }
559ccaac148SDavid du Colombier 
560ccaac148SDavid du Colombier static void
recvrahost(uchar buf[],int pktlen)561e47528f3SDavid du Colombier recvrahost(uchar buf[], int pktlen)
562e47528f3SDavid du Colombier {
563e47528f3SDavid du Colombier 	int arpfd, m, n;
564ccaac148SDavid du Colombier 	char abuf[100];
565e47528f3SDavid du Colombier 	uchar optype;
566e47528f3SDavid du Colombier 	Lladdropt *llao;
567e47528f3SDavid du Colombier 	Mtuopt *mtuo;
568e47528f3SDavid du Colombier 	Prefixopt *prfo;
569e47528f3SDavid du Colombier 	Routeradv *ra;
5700a84db5eSDavid du Colombier 	static int first = 1;
571e47528f3SDavid du Colombier 
572e47528f3SDavid du Colombier 	ra = (Routeradv*)buf;
573ccaac148SDavid du Colombier //	memmove(conf.v6gaddr, ra->src, IPaddrlen);
574e47528f3SDavid du Colombier 	conf.ttl = ra->cttl;
575e47528f3SDavid du Colombier 	conf.mflag = (MFMASK & ra->mor);
576e47528f3SDavid du Colombier 	conf.oflag = (OCMASK & ra->mor);
577e47528f3SDavid du Colombier 	conf.routerlt =  nhgets(ra->routerlt);
578e47528f3SDavid du Colombier 	conf.reachtime = nhgetl(ra->rchbltime);
579e47528f3SDavid du Colombier 	conf.rxmitra =   nhgetl(ra->rxmtimer);
580e47528f3SDavid du Colombier 
5810a84db5eSDavid du Colombier //	issueadd6(&conf);		/* for conf.v6gaddr? */
582ccaac148SDavid du Colombier 	if (fprint(conf.cfd, "ra6 recvra 1") < 0)
583ccaac148SDavid du Colombier 		ralog("write(ra6 recvra 1) failed: %r");
584ccaac148SDavid du Colombier 	issuebasera6(&conf);
585e47528f3SDavid du Colombier 
586e47528f3SDavid du Colombier 	m = sizeof *ra;
587e47528f3SDavid du Colombier 	while (pktlen - m > 0) {
588e47528f3SDavid du Colombier 		optype = buf[m];
589e47528f3SDavid du Colombier 		switch (optype) {
5908c6ab946SDavid du Colombier 		case V6nd_srclladdr:
591e47528f3SDavid du Colombier 			llao = (Lladdropt *)&buf[m];
592e47528f3SDavid du Colombier 			m += 8 * buf[m+1];
593e47528f3SDavid du Colombier 			if (llao->len != 1) {
5940a84db5eSDavid du Colombier 				ralog("recvrahost: illegal len (%d) for source "
5950a84db5eSDavid du Colombier 					"link layer address option", llao->len);
596e47528f3SDavid du Colombier 				return;
597e47528f3SDavid du Colombier 			}
5980a84db5eSDavid du Colombier 			if (!ISIPV6LINKLOCAL(ra->src)) {
5990a84db5eSDavid du Colombier 				ralog("recvrahost: non-link-local src addr for "
6000a84db5eSDavid du Colombier 					"router adv %I", ra->src);
601e47528f3SDavid du Colombier 				return;
602e47528f3SDavid du Colombier 			}
603e47528f3SDavid du Colombier 
604e47528f3SDavid du Colombier 			snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
605e47528f3SDavid du Colombier 			arpfd = open(abuf, OWRITE);
606e47528f3SDavid du Colombier 			if (arpfd < 0) {
6070a84db5eSDavid du Colombier 				ralog("recvrahost: couldn't open %s to write: %r",
6080a84db5eSDavid du Colombier 					abuf);
609e47528f3SDavid du Colombier 				return;
610e47528f3SDavid du Colombier 			}
611e47528f3SDavid du Colombier 
612e47528f3SDavid du Colombier 			n = snprint(abuf, sizeof abuf, "add ether %I %E",
613e47528f3SDavid du Colombier 				ra->src, llao->lladdr);
614e47528f3SDavid du Colombier 			if (write(arpfd, abuf, n) < n)
615e47528f3SDavid du Colombier 				ralog("recvrahost: couldn't write to %s/arp",
616e47528f3SDavid du Colombier 					conf.mpoint);
617e47528f3SDavid du Colombier 			close(arpfd);
618e47528f3SDavid du Colombier 			break;
6198c6ab946SDavid du Colombier 		case V6nd_targlladdr:
6208c6ab946SDavid du Colombier 		case V6nd_redirhdr:
621e47528f3SDavid du Colombier 			m += 8 * buf[m+1];
6220a84db5eSDavid du Colombier 			ralog("ignoring unexpected option type `%s' in Routeradv",
6230a84db5eSDavid du Colombier 				optname(optype));
624e47528f3SDavid du Colombier 			break;
6258c6ab946SDavid du Colombier 		case V6nd_mtu:
626e47528f3SDavid du Colombier 			mtuo = (Mtuopt*)&buf[m];
627e47528f3SDavid du Colombier 			m += 8 * mtuo->len;
628e47528f3SDavid du Colombier 			conf.linkmtu = nhgetl(mtuo->mtu);
629e47528f3SDavid du Colombier 			break;
6308c6ab946SDavid du Colombier 		case V6nd_pfxinfo:
631e47528f3SDavid du Colombier 			prfo = (Prefixopt*)&buf[m];
632e47528f3SDavid du Colombier 			m += 8 * prfo->len;
633e47528f3SDavid du Colombier 			if (prfo->len != 4) {
634e47528f3SDavid du Colombier 				ralog("illegal len (%d) for prefix option",
635e47528f3SDavid du Colombier 					prfo->len);
636e47528f3SDavid du Colombier 				return;
637e47528f3SDavid du Colombier 			}
638e47528f3SDavid du Colombier 			memmove(conf.v6pref, prfo->pref, IPaddrlen);
639e47528f3SDavid du Colombier 			conf.prefixlen = prfo->plen;
640e47528f3SDavid du Colombier 			conf.onlink =   ((prfo->lar & OLMASK) != 0);
641e47528f3SDavid du Colombier 			conf.autoflag = ((prfo->lar & AFMASK) != 0);
642e47528f3SDavid du Colombier 			conf.validlt = nhgetl(prfo->validlt);
643e47528f3SDavid du Colombier 			conf.preflt =  nhgetl(prfo->preflt);
644ccaac148SDavid du Colombier 			issueadd6(&conf);
6450a84db5eSDavid du Colombier 			if (first) {
6460a84db5eSDavid du Colombier 				first = 0;
6470a84db5eSDavid du Colombier 				ralog("got initial RA from %I on %s; pfx %I",
6480a84db5eSDavid du Colombier 					ra->src, conf.dev, prfo->pref);
6490a84db5eSDavid du Colombier 			}
6500a84db5eSDavid du Colombier 			break;
65159f7772cSDavid du Colombier 		default:
652e961f303SDavid du Colombier 			if (debug)
65359f7772cSDavid du Colombier 				ralog("ignoring optype %d in Routeradv from %I",
65459f7772cSDavid du Colombier 					optype, ra->src);
65559f7772cSDavid du Colombier 			/* fall through */
6560a84db5eSDavid du Colombier 		case V6nd_srcaddrs:
6570a84db5eSDavid du Colombier 			/* netsbd sends this, so quietly ignore it for now */
6580a84db5eSDavid du Colombier 			m += 8 * buf[m+1];
659e47528f3SDavid du Colombier 			break;
660e47528f3SDavid du Colombier 		}
661e47528f3SDavid du Colombier 	}
662e47528f3SDavid du Colombier }
663e47528f3SDavid du Colombier 
664e47528f3SDavid du Colombier /*
6650a84db5eSDavid du Colombier  * daemon to receive router advertisements from routers
666e47528f3SDavid du Colombier  */
667e47528f3SDavid du Colombier void
recvra6(void)668e47528f3SDavid du Colombier recvra6(void)
669e47528f3SDavid du Colombier {
670e47528f3SDavid du Colombier 	int fd, cfd, n, sendrscnt, sleepfor;
671e47528f3SDavid du Colombier 	uchar buf[4096];
672e47528f3SDavid du Colombier 
6730a84db5eSDavid du Colombier 	/* TODO: why not v6allroutersL? */
674e47528f3SDavid du Colombier 	fd = dialicmp(v6allnodesL, ICMP6_RA, &cfd);
675e47528f3SDavid du Colombier 	if (fd < 0)
676e47528f3SDavid du Colombier 		sysfatal("can't open icmp_ra connection: %r");
677e47528f3SDavid du Colombier 
678e47528f3SDavid du Colombier 	notify(catch);
6795e1edbcaSDavid du Colombier 	sendrscnt = Maxv6rss;
680e47528f3SDavid du Colombier 
681e47528f3SDavid du Colombier 	switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
682e47528f3SDavid du Colombier 	case -1:
683e47528f3SDavid du Colombier 		sysfatal("can't fork: %r");
684e47528f3SDavid du Colombier 	default:
685e47528f3SDavid du Colombier 		return;
686e47528f3SDavid du Colombier 	case 0:
6870a84db5eSDavid du Colombier 		break;
6880a84db5eSDavid du Colombier 	}
6890a84db5eSDavid du Colombier 
690e47528f3SDavid du Colombier 	procsetname("recvra6 on %s", conf.dev);
691e47528f3SDavid du Colombier 	ralog("recvra6 on %s", conf.dev);
6920a84db5eSDavid du Colombier 	sleepfor = jitter();
693e47528f3SDavid du Colombier 	for (;;) {
6940a84db5eSDavid du Colombier 		/*
6950a84db5eSDavid du Colombier 		 * We only get 3 (Maxv6rss) tries, so make sure we
6960a84db5eSDavid du Colombier 		 * wait long enough to be certain that at least one RA
6970a84db5eSDavid du Colombier 		 * will be transmitted.
6980a84db5eSDavid du Colombier 		 */
6990a84db5eSDavid du Colombier 		if (sleepfor < 7000)
7000a84db5eSDavid du Colombier 			sleepfor = 7000;
701e47528f3SDavid du Colombier 		alarm(sleepfor);
702e47528f3SDavid du Colombier 		n = read(fd, buf, sizeof buf);
703e47528f3SDavid du Colombier 		alarm(0);
704e47528f3SDavid du Colombier 		if (n <= 0) {
705e47528f3SDavid du Colombier 			if (sendrscnt > 0) {
706e47528f3SDavid du Colombier 				sendrscnt--;
7070a84db5eSDavid du Colombier 				if (recvra6on(conf.mpoint, myifc) == IsHostRecv)
708e47528f3SDavid du Colombier 					sendrs(fd);
7095e1edbcaSDavid du Colombier 				sleepfor = V6rsintvl + nrand(100);
710e47528f3SDavid du Colombier 			}
711e47528f3SDavid du Colombier 			if (sendrscnt == 0) {
712e47528f3SDavid du Colombier 				sendrscnt--;
713e47528f3SDavid du Colombier 				sleepfor = 0;
7140a84db5eSDavid du Colombier 				ralog("recvra6: no router advs after %d sols on %s",
7155e1edbcaSDavid du Colombier 					Maxv6rss, conf.dev);
716e47528f3SDavid du Colombier 			}
717e47528f3SDavid du Colombier 			continue;
718e47528f3SDavid du Colombier 		}
719e47528f3SDavid du Colombier 
720e47528f3SDavid du Colombier 		sleepfor = 0;
7210a84db5eSDavid du Colombier 		sendrscnt = -1;		/* got at least initial ra; no whining */
722e47528f3SDavid du Colombier 		switch (recvra6on(conf.mpoint, myifc)) {
723e47528f3SDavid du Colombier 		case IsRouter:
724e47528f3SDavid du Colombier 			recvrarouter(buf, n);
725e47528f3SDavid du Colombier 			break;
726e47528f3SDavid du Colombier 		case IsHostRecv:
727e47528f3SDavid du Colombier 			recvrahost(buf, n);
728e47528f3SDavid du Colombier 			break;
729e47528f3SDavid du Colombier 		case IsHostNoRecv:
7300a84db5eSDavid du Colombier 			ralog("recvra6: recvra off, quitting on %s", conf.dev);
731e47528f3SDavid du Colombier 			close(fd);
732e47528f3SDavid du Colombier 			exits(0);
733e47528f3SDavid du Colombier 		default:
7340a84db5eSDavid du Colombier 			ralog("recvra6: unable to read router status on %s",
735e47528f3SDavid du Colombier 				conf.dev);
736e47528f3SDavid du Colombier 			break;
737e47528f3SDavid du Colombier 		}
738e47528f3SDavid du Colombier 	}
739e47528f3SDavid du Colombier }
740e47528f3SDavid du Colombier 
741e47528f3SDavid du Colombier /*
742e47528f3SDavid du Colombier  * return -1 -- error, reading/writing some file,
743e47528f3SDavid du Colombier  *         0 -- no arp table updates
744e47528f3SDavid du Colombier  *         1 -- successful arp table update
745e47528f3SDavid du Colombier  */
746e47528f3SDavid du Colombier int
recvrs(uchar * buf,int pktlen,uchar * sol)747e47528f3SDavid du Colombier recvrs(uchar *buf, int pktlen, uchar *sol)
748e47528f3SDavid du Colombier {
749e47528f3SDavid du Colombier 	int n, optsz, arpfd;
750e47528f3SDavid du Colombier 	char abuf[256];
751e47528f3SDavid du Colombier 	Routersol *rs;
752e47528f3SDavid du Colombier 	Lladdropt *llao;
753e47528f3SDavid du Colombier 
754e47528f3SDavid du Colombier 	rs = (Routersol *)buf;
755e47528f3SDavid du Colombier 	n = sizeof *rs;
756e47528f3SDavid du Colombier 	optsz = pktlen - n;
757e47528f3SDavid du Colombier 	pkt2str(buf, buf+pktlen, abuf, abuf+nelem(abuf));
758e47528f3SDavid du Colombier 
759e47528f3SDavid du Colombier 	if (optsz != sizeof *llao)
760e47528f3SDavid du Colombier 		return 0;
7618c6ab946SDavid du Colombier 	if (buf[n] != V6nd_srclladdr || 8*buf[n+1] != sizeof *llao) {
762e47528f3SDavid du Colombier 		ralog("rs opt err %s", abuf);
763e47528f3SDavid du Colombier 		return -1;
764e47528f3SDavid du Colombier 	}
765e47528f3SDavid du Colombier 
766e47528f3SDavid du Colombier 	ralog("rs recv %s", abuf);
767e47528f3SDavid du Colombier 
768e47528f3SDavid du Colombier 	if (memcmp(rs->src, v6Unspecified, IPaddrlen) == 0)
769e47528f3SDavid du Colombier 		return 0;
770e47528f3SDavid du Colombier 
771e47528f3SDavid du Colombier 	snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
772e47528f3SDavid du Colombier 	arpfd = open(abuf, OWRITE);
773e47528f3SDavid du Colombier 	if (arpfd < 0) {
774e47528f3SDavid du Colombier 		ralog("recvrs: can't open %s/arp to write: %r", conf.mpoint);
775e47528f3SDavid du Colombier 		return -1;
776e47528f3SDavid du Colombier 	}
777e47528f3SDavid du Colombier 
778e47528f3SDavid du Colombier 	llao = (Lladdropt *)buf[n];
779e47528f3SDavid du Colombier 	n = snprint(abuf, sizeof abuf, "add ether %I %E", rs->src, llao->lladdr);
780e47528f3SDavid du Colombier 	if (write(arpfd, abuf, n) < n) {
781e47528f3SDavid du Colombier 		ralog("recvrs: can't write to %s/arp: %r", conf.mpoint);
782e47528f3SDavid du Colombier 		close(arpfd);
783e47528f3SDavid du Colombier 		return -1;
784e47528f3SDavid du Colombier 	}
785e47528f3SDavid du Colombier 
786e47528f3SDavid du Colombier 	memmove(sol, rs->src, IPaddrlen);
787e47528f3SDavid du Colombier 	close(arpfd);
788e47528f3SDavid du Colombier 	return 1;
789e47528f3SDavid du Colombier }
790e47528f3SDavid du Colombier 
791e47528f3SDavid du Colombier void
sendra(int fd,uchar * dst,int rlt)792e47528f3SDavid du Colombier sendra(int fd, uchar *dst, int rlt)
793e47528f3SDavid du Colombier {
794e47528f3SDavid du Colombier 	int pktsz, preflen;
795e47528f3SDavid du Colombier 	char abuf[1024], tmp[40];
796e47528f3SDavid du Colombier 	uchar buf[1024], macaddr[6], src[IPaddrlen];
797e47528f3SDavid du Colombier 	Ipifc *ifc = nil;
798e47528f3SDavid du Colombier 	Iplifc *lifc, *nlifc;
799e47528f3SDavid du Colombier 	Lladdropt *llao;
800e47528f3SDavid du Colombier 	Prefixopt *prfo;
801e47528f3SDavid du Colombier 	Routeradv *ra;
802e47528f3SDavid du Colombier 
803e47528f3SDavid du Colombier 	memset(buf, 0, sizeof buf);
804e47528f3SDavid du Colombier 	ra = (Routeradv *)buf;
805e47528f3SDavid du Colombier 
806e47528f3SDavid du Colombier 	myetheraddr(macaddr, conf.dev);
807e47528f3SDavid du Colombier 	ea2lla(src, macaddr);
808e47528f3SDavid du Colombier 	memmove(ra->src, src, IPaddrlen);
809e47528f3SDavid du Colombier 	memmove(ra->dst, dst, IPaddrlen);
810e47528f3SDavid du Colombier 	ra->type = ICMP6_RA;
811e47528f3SDavid du Colombier 	ra->cttl = conf.ttl;
812e47528f3SDavid du Colombier 
813e47528f3SDavid du Colombier 	if (conf.mflag > 0)
814e47528f3SDavid du Colombier 		ra->mor |= MFMASK;
815e47528f3SDavid du Colombier 	if (conf.oflag > 0)
816e47528f3SDavid du Colombier 		ra->mor |= OCMASK;
817e47528f3SDavid du Colombier 	if (rlt > 0)
818e47528f3SDavid du Colombier 		hnputs(ra->routerlt, conf.routerlt);
819e47528f3SDavid du Colombier 	else
820e47528f3SDavid du Colombier 		hnputs(ra->routerlt, 0);
821e47528f3SDavid du Colombier 	hnputl(ra->rchbltime, conf.reachtime);
822e47528f3SDavid du Colombier 	hnputl(ra->rxmtimer, conf.rxmitra);
823e47528f3SDavid du Colombier 
824e47528f3SDavid du Colombier 	pktsz = sizeof *ra;
825e47528f3SDavid du Colombier 
826e47528f3SDavid du Colombier 	/* include all global unicast prefixes on interface in prefix options */
827e47528f3SDavid du Colombier 	ifc = readipifc(conf.mpoint, ifc, myifc);
828e47528f3SDavid du Colombier 	for (lifc = (ifc? ifc->lifc: nil); lifc; lifc = nlifc) {
829e47528f3SDavid du Colombier 		nlifc = lifc->next;
830e47528f3SDavid du Colombier 		prfo = (Prefixopt *)(buf + pktsz);
8315e1edbcaSDavid du Colombier 		/* global unicast address? */
8325e1edbcaSDavid du Colombier 		if (!ISIPV6LINKLOCAL(lifc->ip) && !ISIPV6MCAST(lifc->ip) &&
8335e1edbcaSDavid du Colombier 		    memcmp(lifc->ip, IPnoaddr, IPaddrlen) != 0 &&
8340a84db5eSDavid du Colombier 		    memcmp(lifc->ip, v6loopback, IPaddrlen) != 0 &&
8350a84db5eSDavid du Colombier 		    !isv4(lifc->ip)) {
836e47528f3SDavid du Colombier 			memmove(prfo->pref, lifc->net, IPaddrlen);
837e47528f3SDavid du Colombier 
838e47528f3SDavid du Colombier 			/* hack to find prefix length */
839e47528f3SDavid du Colombier 			snprint(tmp, sizeof tmp, "%M", lifc->mask);
840e47528f3SDavid du Colombier 			preflen = atoi(&tmp[1]);
841e47528f3SDavid du Colombier 			prfo->plen = preflen & 0xff;
842e47528f3SDavid du Colombier 			if (prfo->plen == 0)
843e47528f3SDavid du Colombier 				continue;
844e47528f3SDavid du Colombier 
8458c6ab946SDavid du Colombier 			prfo->type = V6nd_pfxinfo;
846e47528f3SDavid du Colombier 			prfo->len = 4;
847e47528f3SDavid du Colombier 			prfo->lar = AFMASK;
848e47528f3SDavid du Colombier 			hnputl(prfo->validlt, lifc->validlt);
849e47528f3SDavid du Colombier 			hnputl(prfo->preflt, lifc->preflt);
850e47528f3SDavid du Colombier 			pktsz += sizeof *prfo;
851e47528f3SDavid du Colombier 		}
852e47528f3SDavid du Colombier 	}
853e47528f3SDavid du Colombier 	/*
854e47528f3SDavid du Colombier 	 * include link layer address (mac address for now) in
855e47528f3SDavid du Colombier 	 * link layer address option
856e47528f3SDavid du Colombier 	 */
857e47528f3SDavid du Colombier 	llao = (Lladdropt *)(buf + pktsz);
8588c6ab946SDavid du Colombier 	llao->type = V6nd_srclladdr;
859e47528f3SDavid du Colombier 	llao->len = 1;
860e47528f3SDavid du Colombier 	memmove(llao->lladdr, macaddr, sizeof macaddr);
861e47528f3SDavid du Colombier 	pktsz += sizeof *llao;
862e47528f3SDavid du Colombier 
863e47528f3SDavid du Colombier 	pkt2str(buf+40, buf+pktsz, abuf, abuf+1024);
864e47528f3SDavid du Colombier 	if(write(fd, buf, pktsz) < pktsz)
8650a84db5eSDavid du Colombier 		ralog("sendra fail %s: %r", abuf);
8660a84db5eSDavid du Colombier 	else if (debug)
8670a84db5eSDavid du Colombier 		ralog("sendra succ %s", abuf);
868e47528f3SDavid du Colombier }
869e47528f3SDavid du Colombier 
870e47528f3SDavid du Colombier /*
8710a84db5eSDavid du Colombier  * daemon to send router advertisements to hosts
872e47528f3SDavid du Colombier  */
873e47528f3SDavid du Colombier void
sendra6(void)874e47528f3SDavid du Colombier sendra6(void)
875e47528f3SDavid du Colombier {
876e47528f3SDavid du Colombier 	int fd, cfd, n, dstknown = 0, sendracnt, sleepfor, nquitmsgs;
8770a84db5eSDavid du Colombier 	long lastra, now;
878e47528f3SDavid du Colombier 	uchar buf[4096], dst[IPaddrlen];
879e47528f3SDavid du Colombier 	Ipifc *ifc = nil;
880e47528f3SDavid du Colombier 
881e47528f3SDavid du Colombier 	fd = dialicmp(v6allnodesL, ICMP6_RS, &cfd);
882e47528f3SDavid du Colombier 	if (fd < 0)
883e47528f3SDavid du Colombier 		sysfatal("can't open icmp_rs connection: %r");
884e47528f3SDavid du Colombier 
885e47528f3SDavid du Colombier 	notify(catch);
8865e1edbcaSDavid du Colombier 	sendracnt = Maxv6initras;
8875e1edbcaSDavid du Colombier 	nquitmsgs = Maxv6finalras;
888e47528f3SDavid du Colombier 
889e47528f3SDavid du Colombier 	switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
890e47528f3SDavid du Colombier 	case -1:
891e47528f3SDavid du Colombier 		sysfatal("can't fork: %r");
892e47528f3SDavid du Colombier 	default:
893e47528f3SDavid du Colombier 		return;
894e47528f3SDavid du Colombier 	case 0:
8950a84db5eSDavid du Colombier 		break;
8960a84db5eSDavid du Colombier 	}
8970a84db5eSDavid du Colombier 
898e47528f3SDavid du Colombier 	procsetname("sendra6 on %s", conf.dev);
899e47528f3SDavid du Colombier 	ralog("sendra6 on %s", conf.dev);
9000a84db5eSDavid du Colombier 	sleepfor = jitter();
901e47528f3SDavid du Colombier 	for (;;) {
902e47528f3SDavid du Colombier 		lastra = time(0);
9030a84db5eSDavid du Colombier 		if (sleepfor < 0)
9040a84db5eSDavid du Colombier 			sleepfor = 0;
905e47528f3SDavid du Colombier 		alarm(sleepfor);
906e47528f3SDavid du Colombier 		n = read(fd, buf, sizeof buf);
907e47528f3SDavid du Colombier 		alarm(0);
908e47528f3SDavid du Colombier 
909e47528f3SDavid du Colombier 		ifc = readipifc(conf.mpoint, ifc, myifc);
910e47528f3SDavid du Colombier 		if (ifc == nil) {
9110a84db5eSDavid du Colombier 			ralog("sendra6: can't read router params on %s",
912e47528f3SDavid du Colombier 				conf.mpoint);
913e47528f3SDavid du Colombier 			continue;
914e47528f3SDavid du Colombier 		}
915e47528f3SDavid du Colombier 
916e47528f3SDavid du Colombier 		if (ifc->sendra6 <= 0)
917e47528f3SDavid du Colombier 			if (nquitmsgs > 0) {
918e47528f3SDavid du Colombier 				sendra(fd, v6allnodesL, 0);
919e47528f3SDavid du Colombier 				nquitmsgs--;
9200a84db5eSDavid du Colombier 				sleepfor = Minv6interradelay + jitter();
921e47528f3SDavid du Colombier 				continue;
922e47528f3SDavid du Colombier 			} else {
9230a84db5eSDavid du Colombier 				ralog("sendra6: sendra off, quitting on %s",
9240a84db5eSDavid du Colombier 					conf.dev);
925e47528f3SDavid du Colombier 				exits(0);
926e47528f3SDavid du Colombier 			}
927e47528f3SDavid du Colombier 
9285e1edbcaSDavid du Colombier 		nquitmsgs = Maxv6finalras;
929e47528f3SDavid du Colombier 
9308c6ab946SDavid du Colombier 		if (n <= 0) {			/* no RS */
931e47528f3SDavid du Colombier 			if (sendracnt > 0)
932e47528f3SDavid du Colombier 				sendracnt--;
9338c6ab946SDavid du Colombier 		} else {			/* respond to RS */
934e47528f3SDavid du Colombier 			dstknown = recvrs(buf, n, dst);
9350a84db5eSDavid du Colombier 			now = time(0);
936e47528f3SDavid du Colombier 
9370a84db5eSDavid du Colombier 			if (now - lastra < Minv6interradelay) {
938e47528f3SDavid du Colombier 				/* too close, skip */
9390a84db5eSDavid du Colombier 				sleepfor = lastra + Minv6interradelay +
9400a84db5eSDavid du Colombier 					jitter() - now;
941e47528f3SDavid du Colombier 				continue;
942e47528f3SDavid du Colombier 			}
9430a84db5eSDavid du Colombier 			sleep(jitter());
944e47528f3SDavid du Colombier 		}
9450a84db5eSDavid du Colombier 		sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint);
946e47528f3SDavid du Colombier 		if (dstknown > 0)
947e47528f3SDavid du Colombier 			sendra(fd, dst, 1);
948e47528f3SDavid du Colombier 		else
949e47528f3SDavid du Colombier 			sendra(fd, v6allnodesL, 1);
950e47528f3SDavid du Colombier 	}
951e47528f3SDavid du Colombier }
952e47528f3SDavid du Colombier 
953e47528f3SDavid du Colombier void
startra6(void)954ccaac148SDavid du Colombier startra6(void)
955e47528f3SDavid du Colombier {
956e47528f3SDavid du Colombier 	static char routeon[] = "iprouting 1";
957e47528f3SDavid du Colombier 
958e47528f3SDavid du Colombier 	if (conf.recvra > 0)
959e47528f3SDavid du Colombier 		recvra6();
960e47528f3SDavid du Colombier 
961e47528f3SDavid du Colombier 	if (conf.sendra > 0) {
962e47528f3SDavid du Colombier 		if (write(conf.cfd, routeon, sizeof routeon - 1) < 0) {
9638c6ab946SDavid du Colombier 			warning("write (iprouting 1) failed: %r");
964e47528f3SDavid du Colombier 			return;
965e47528f3SDavid du Colombier 		}
966e47528f3SDavid du Colombier 		sendra6();
967e47528f3SDavid du Colombier 		if (conf.recvra <= 0)
968e47528f3SDavid du Colombier 			recvra6();
969e47528f3SDavid du Colombier 	}
970e47528f3SDavid du Colombier }
971e47528f3SDavid du Colombier 
972e47528f3SDavid du Colombier void
doipv6(int what)973ccaac148SDavid du Colombier doipv6(int what)
974e47528f3SDavid du Colombier {
975e47528f3SDavid du Colombier 	nip = nipifcs(conf.mpoint);
976e47528f3SDavid du Colombier 	if(!noconfig){
977e47528f3SDavid du Colombier 		lookforip(conf.mpoint);
978e47528f3SDavid du Colombier 		controldevice();
979e47528f3SDavid du Colombier 		binddevice();
980e47528f3SDavid du Colombier 	}
981e47528f3SDavid du Colombier 
982e47528f3SDavid du Colombier 	switch (what) {
983e47528f3SDavid du Colombier 	default:
9848c6ab946SDavid du Colombier 		sysfatal("unknown IPv6 verb");
985e47528f3SDavid du Colombier 	case Vaddpref6:
986ccaac148SDavid du Colombier 		issueadd6(&conf);
987e47528f3SDavid du Colombier 		break;
988e47528f3SDavid du Colombier 	case Vra6:
989ccaac148SDavid du Colombier 		issuebasera6(&conf);
990ccaac148SDavid du Colombier 		issuerara6(&conf);
9910a84db5eSDavid du Colombier 		dolog = 1;
992ccaac148SDavid du Colombier 		startra6();
993e47528f3SDavid du Colombier 		break;
994e47528f3SDavid du Colombier 	}
995e47528f3SDavid du Colombier }
996