xref: /plan9/sys/src/cmd/ip/ipconfig/ipv6.c (revision e961f3039bf6d23366e7e2a4bf39abb552dedc4f)
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 {
388e47528f3SDavid du Colombier 	int dupfound = 0, n;
389e47528f3SDavid du Colombier 	char *p;
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;
436e47528f3SDavid du Colombier 		if(cistrstr(p, buf) != 0) {
4378c6ab946SDavid du Colombier 			warning("found dup entry in arp cache");
438e47528f3SDavid du Colombier 			dupfound = 1;
439e47528f3SDavid du Colombier 			break;
440e47528f3SDavid du Colombier 		}
441e47528f3SDavid du Colombier 	}
442e47528f3SDavid du Colombier 	Bterm(bp);
443e47528f3SDavid du Colombier 
444e47528f3SDavid du Colombier 	if (dupfound)
445e47528f3SDavid du Colombier 		doremove();
446e47528f3SDavid du Colombier 	else {
447e47528f3SDavid du Colombier 		n = sprint(buf, "add %I %M", conf.laddr, conf.mask);
448e47528f3SDavid du Colombier 		if(validip(conf.raddr)){
449e47528f3SDavid du Colombier 			n += snprint(buf+n, sizeof buf-n, " %I", conf.raddr);
450e47528f3SDavid du Colombier 			if(conf.mtu != 0)
451e47528f3SDavid du Colombier 				n += snprint(buf+n, sizeof buf-n, " %d",
452e47528f3SDavid du Colombier 					conf.mtu);
453e47528f3SDavid du Colombier 		}
454e47528f3SDavid du Colombier 		write(conf.cfd, buf, n);
455e47528f3SDavid du Colombier 	}
456e47528f3SDavid du Colombier 	return 0;
457e47528f3SDavid du Colombier }
458e47528f3SDavid du Colombier 
459e47528f3SDavid du Colombier static int
recvra6on(char * net,int conn)460e47528f3SDavid du Colombier recvra6on(char *net, int conn)
461e47528f3SDavid du Colombier {
462e47528f3SDavid du Colombier 	Ipifc* ifc;
463e47528f3SDavid du Colombier 
464e47528f3SDavid du Colombier 	ifc = readipifc(net, nil, conn);
465e47528f3SDavid du Colombier 	if (ifc == nil)
466e47528f3SDavid du Colombier 		return 0;
467e47528f3SDavid du Colombier 	else if (ifc->sendra6 > 0)
468e47528f3SDavid du Colombier 		return IsRouter;
469e47528f3SDavid du Colombier 	else if (ifc->recvra6 > 0)
470e47528f3SDavid du Colombier 		return IsHostRecv;
471e47528f3SDavid du Colombier 	else
472e47528f3SDavid du Colombier 		return IsHostNoRecv;
473e47528f3SDavid du Colombier }
474e47528f3SDavid du Colombier 
4750a84db5eSDavid du Colombier /* send icmpv6 router solicitation to multicast address for all routers */
476e47528f3SDavid du Colombier static void
sendrs(int fd)477e47528f3SDavid du Colombier sendrs(int fd)
478e47528f3SDavid du Colombier {
479e47528f3SDavid du Colombier 	Routersol *rs;
480e47528f3SDavid du Colombier 	uchar buff[sizeof *rs];
481e47528f3SDavid du Colombier 
482e47528f3SDavid du Colombier 	memset(buff, 0, sizeof buff);
483e47528f3SDavid du Colombier 	rs = (Routersol *)buff;
484e47528f3SDavid du Colombier 	memmove(rs->dst, v6allroutersL, IPaddrlen);
485e47528f3SDavid du Colombier 	memmove(rs->src, v6Unspecified, IPaddrlen);
486e47528f3SDavid du Colombier 	rs->type = ICMP6_RS;
487e47528f3SDavid du Colombier 
488e47528f3SDavid du Colombier 	if(write(fd, rs, sizeof buff) < sizeof buff)
489e47528f3SDavid du Colombier 		ralog("sendrs: write failed, pkt size %d", sizeof buff);
4900a84db5eSDavid du Colombier 	else
4910a84db5eSDavid du Colombier 		ralog("sendrs: sent solicitation to %I from %I on %s",
4920a84db5eSDavid du Colombier 			rs->dst, rs->src, conf.dev);
493e47528f3SDavid du Colombier }
494e47528f3SDavid du Colombier 
495e47528f3SDavid du Colombier /*
496e47528f3SDavid du Colombier  * a router receiving a router adv from another
497e47528f3SDavid du Colombier  * router calls this; it is basically supposed to
498e47528f3SDavid du Colombier  * log the information in the ra and raise a flag
499e47528f3SDavid du Colombier  * if any parameter value is different from its configured values.
500e47528f3SDavid du Colombier  *
501e47528f3SDavid du Colombier  * doing nothing for now since I don't know where to log this yet.
502e47528f3SDavid du Colombier  */
503e47528f3SDavid du Colombier static void
recvrarouter(uchar buf[],int pktlen)504e47528f3SDavid du Colombier recvrarouter(uchar buf[], int pktlen)
505e47528f3SDavid du Colombier {
506e47528f3SDavid du Colombier 	USED(buf, pktlen);
507e47528f3SDavid du Colombier 	ralog("i am a router and got a router advert");
508e47528f3SDavid du Colombier }
509e47528f3SDavid du Colombier 
510e47528f3SDavid du Colombier /* host receiving a router advertisement calls this */
511e47528f3SDavid du Colombier 
512e47528f3SDavid du Colombier static void
ewrite(int fd,char * str)513ccaac148SDavid du Colombier ewrite(int fd, char *str)
514ccaac148SDavid du Colombier {
515ccaac148SDavid du Colombier 	int n;
516ccaac148SDavid du Colombier 
517ccaac148SDavid du Colombier 	n = strlen(str);
518ccaac148SDavid du Colombier 	if (write(fd, str, n) != n)
519ccaac148SDavid du Colombier 		ralog("write(%s) failed: %r", str);
520ccaac148SDavid du Colombier }
521ccaac148SDavid du Colombier 
522ccaac148SDavid du Colombier static void
issuebasera6(Conf * cf)523ccaac148SDavid du Colombier issuebasera6(Conf *cf)
524ccaac148SDavid du Colombier {
525ccaac148SDavid du Colombier 	char *cfg;
526ccaac148SDavid du Colombier 
527ccaac148SDavid du Colombier 	cfg = smprint("ra6 mflag %d oflag %d reachtime %d rxmitra %d "
528ccaac148SDavid du Colombier 		"ttl %d routerlt %d",
529ccaac148SDavid du Colombier 		cf->mflag, cf->oflag, cf->reachtime, cf->rxmitra,
530ccaac148SDavid du Colombier 		cf->ttl, cf->routerlt);
531ccaac148SDavid du Colombier 	ewrite(cf->cfd, cfg);
532ccaac148SDavid du Colombier 	free(cfg);
533ccaac148SDavid du Colombier }
534ccaac148SDavid du Colombier 
535ccaac148SDavid du Colombier static void
issuerara6(Conf * cf)536ccaac148SDavid du Colombier issuerara6(Conf *cf)
537ccaac148SDavid du Colombier {
538ccaac148SDavid du Colombier 	char *cfg;
539ccaac148SDavid du Colombier 
540ccaac148SDavid du Colombier 	cfg = smprint("ra6 sendra %d recvra %d maxraint %d minraint %d "
541ccaac148SDavid du Colombier 		"linkmtu %d",
542ccaac148SDavid du Colombier 		cf->sendra, cf->recvra, cf->maxraint, cf->minraint,
543ccaac148SDavid du Colombier 		cf->linkmtu);
544ccaac148SDavid du Colombier 	ewrite(cf->cfd, cfg);
545ccaac148SDavid du Colombier 	free(cfg);
546ccaac148SDavid du Colombier }
547ccaac148SDavid du Colombier 
548ccaac148SDavid du Colombier static void
issueadd6(Conf * cf)549ccaac148SDavid du Colombier issueadd6(Conf *cf)
550ccaac148SDavid du Colombier {
551ccaac148SDavid du Colombier 	char *cfg;
552ccaac148SDavid du Colombier 
553ccaac148SDavid du Colombier 	cfg = smprint("add6 %I %d %d %d %lud %lud", cf->v6pref, cf->prefixlen,
554ccaac148SDavid du Colombier 		cf->onlink, cf->autoflag, cf->validlt, cf->preflt);
555ccaac148SDavid du Colombier 	ewrite(cf->cfd, cfg);
556ccaac148SDavid du Colombier 	free(cfg);
557ccaac148SDavid du Colombier }
558ccaac148SDavid du Colombier 
559ccaac148SDavid du Colombier static void
recvrahost(uchar buf[],int pktlen)560e47528f3SDavid du Colombier recvrahost(uchar buf[], int pktlen)
561e47528f3SDavid du Colombier {
562e47528f3SDavid du Colombier 	int arpfd, m, n;
563ccaac148SDavid du Colombier 	char abuf[100];
564e47528f3SDavid du Colombier 	uchar optype;
565e47528f3SDavid du Colombier 	Lladdropt *llao;
566e47528f3SDavid du Colombier 	Mtuopt *mtuo;
567e47528f3SDavid du Colombier 	Prefixopt *prfo;
568e47528f3SDavid du Colombier 	Routeradv *ra;
5690a84db5eSDavid du Colombier 	static int first = 1;
570e47528f3SDavid du Colombier 
571e47528f3SDavid du Colombier 	ra = (Routeradv*)buf;
572ccaac148SDavid du Colombier //	memmove(conf.v6gaddr, ra->src, IPaddrlen);
573e47528f3SDavid du Colombier 	conf.ttl = ra->cttl;
574e47528f3SDavid du Colombier 	conf.mflag = (MFMASK & ra->mor);
575e47528f3SDavid du Colombier 	conf.oflag = (OCMASK & ra->mor);
576e47528f3SDavid du Colombier 	conf.routerlt =  nhgets(ra->routerlt);
577e47528f3SDavid du Colombier 	conf.reachtime = nhgetl(ra->rchbltime);
578e47528f3SDavid du Colombier 	conf.rxmitra =   nhgetl(ra->rxmtimer);
579e47528f3SDavid du Colombier 
5800a84db5eSDavid du Colombier //	issueadd6(&conf);		/* for conf.v6gaddr? */
581ccaac148SDavid du Colombier 	if (fprint(conf.cfd, "ra6 recvra 1") < 0)
582ccaac148SDavid du Colombier 		ralog("write(ra6 recvra 1) failed: %r");
583ccaac148SDavid du Colombier 	issuebasera6(&conf);
584e47528f3SDavid du Colombier 
585e47528f3SDavid du Colombier 	m = sizeof *ra;
586e47528f3SDavid du Colombier 	while (pktlen - m > 0) {
587e47528f3SDavid du Colombier 		optype = buf[m];
588e47528f3SDavid du Colombier 		switch (optype) {
5898c6ab946SDavid du Colombier 		case V6nd_srclladdr:
590e47528f3SDavid du Colombier 			llao = (Lladdropt *)&buf[m];
591e47528f3SDavid du Colombier 			m += 8 * buf[m+1];
592e47528f3SDavid du Colombier 			if (llao->len != 1) {
5930a84db5eSDavid du Colombier 				ralog("recvrahost: illegal len (%d) for source "
5940a84db5eSDavid du Colombier 					"link layer address option", llao->len);
595e47528f3SDavid du Colombier 				return;
596e47528f3SDavid du Colombier 			}
5970a84db5eSDavid du Colombier 			if (!ISIPV6LINKLOCAL(ra->src)) {
5980a84db5eSDavid du Colombier 				ralog("recvrahost: non-link-local src addr for "
5990a84db5eSDavid du Colombier 					"router adv %I", ra->src);
600e47528f3SDavid du Colombier 				return;
601e47528f3SDavid du Colombier 			}
602e47528f3SDavid du Colombier 
603e47528f3SDavid du Colombier 			snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
604e47528f3SDavid du Colombier 			arpfd = open(abuf, OWRITE);
605e47528f3SDavid du Colombier 			if (arpfd < 0) {
6060a84db5eSDavid du Colombier 				ralog("recvrahost: couldn't open %s to write: %r",
6070a84db5eSDavid du Colombier 					abuf);
608e47528f3SDavid du Colombier 				return;
609e47528f3SDavid du Colombier 			}
610e47528f3SDavid du Colombier 
611e47528f3SDavid du Colombier 			n = snprint(abuf, sizeof abuf, "add ether %I %E",
612e47528f3SDavid du Colombier 				ra->src, llao->lladdr);
613e47528f3SDavid du Colombier 			if (write(arpfd, abuf, n) < n)
614e47528f3SDavid du Colombier 				ralog("recvrahost: couldn't write to %s/arp",
615e47528f3SDavid du Colombier 					conf.mpoint);
616e47528f3SDavid du Colombier 			close(arpfd);
617e47528f3SDavid du Colombier 			break;
6188c6ab946SDavid du Colombier 		case V6nd_targlladdr:
6198c6ab946SDavid du Colombier 		case V6nd_redirhdr:
620e47528f3SDavid du Colombier 			m += 8 * buf[m+1];
6210a84db5eSDavid du Colombier 			ralog("ignoring unexpected option type `%s' in Routeradv",
6220a84db5eSDavid du Colombier 				optname(optype));
623e47528f3SDavid du Colombier 			break;
6248c6ab946SDavid du Colombier 		case V6nd_mtu:
625e47528f3SDavid du Colombier 			mtuo = (Mtuopt*)&buf[m];
626e47528f3SDavid du Colombier 			m += 8 * mtuo->len;
627e47528f3SDavid du Colombier 			conf.linkmtu = nhgetl(mtuo->mtu);
628e47528f3SDavid du Colombier 			break;
6298c6ab946SDavid du Colombier 		case V6nd_pfxinfo:
630e47528f3SDavid du Colombier 			prfo = (Prefixopt*)&buf[m];
631e47528f3SDavid du Colombier 			m += 8 * prfo->len;
632e47528f3SDavid du Colombier 			if (prfo->len != 4) {
633e47528f3SDavid du Colombier 				ralog("illegal len (%d) for prefix option",
634e47528f3SDavid du Colombier 					prfo->len);
635e47528f3SDavid du Colombier 				return;
636e47528f3SDavid du Colombier 			}
637e47528f3SDavid du Colombier 			memmove(conf.v6pref, prfo->pref, IPaddrlen);
638e47528f3SDavid du Colombier 			conf.prefixlen = prfo->plen;
639e47528f3SDavid du Colombier 			conf.onlink =   ((prfo->lar & OLMASK) != 0);
640e47528f3SDavid du Colombier 			conf.autoflag = ((prfo->lar & AFMASK) != 0);
641e47528f3SDavid du Colombier 			conf.validlt = nhgetl(prfo->validlt);
642e47528f3SDavid du Colombier 			conf.preflt =  nhgetl(prfo->preflt);
643ccaac148SDavid du Colombier 			issueadd6(&conf);
6440a84db5eSDavid du Colombier 			if (first) {
6450a84db5eSDavid du Colombier 				first = 0;
6460a84db5eSDavid du Colombier 				ralog("got initial RA from %I on %s; pfx %I",
6470a84db5eSDavid du Colombier 					ra->src, conf.dev, prfo->pref);
6480a84db5eSDavid du Colombier 			}
6490a84db5eSDavid du Colombier 			break;
65059f7772cSDavid du Colombier 		default:
651*e961f303SDavid du Colombier 			if (debug)
65259f7772cSDavid du Colombier 				ralog("ignoring optype %d in Routeradv from %I",
65359f7772cSDavid du Colombier 					optype, ra->src);
65459f7772cSDavid du Colombier 			/* fall through */
6550a84db5eSDavid du Colombier 		case V6nd_srcaddrs:
6560a84db5eSDavid du Colombier 			/* netsbd sends this, so quietly ignore it for now */
6570a84db5eSDavid du Colombier 			m += 8 * buf[m+1];
658e47528f3SDavid du Colombier 			break;
659e47528f3SDavid du Colombier 		}
660e47528f3SDavid du Colombier 	}
661e47528f3SDavid du Colombier }
662e47528f3SDavid du Colombier 
663e47528f3SDavid du Colombier /*
6640a84db5eSDavid du Colombier  * daemon to receive router advertisements from routers
665e47528f3SDavid du Colombier  */
666e47528f3SDavid du Colombier void
recvra6(void)667e47528f3SDavid du Colombier recvra6(void)
668e47528f3SDavid du Colombier {
669e47528f3SDavid du Colombier 	int fd, cfd, n, sendrscnt, sleepfor;
670e47528f3SDavid du Colombier 	uchar buf[4096];
671e47528f3SDavid du Colombier 
6720a84db5eSDavid du Colombier 	/* TODO: why not v6allroutersL? */
673e47528f3SDavid du Colombier 	fd = dialicmp(v6allnodesL, ICMP6_RA, &cfd);
674e47528f3SDavid du Colombier 	if (fd < 0)
675e47528f3SDavid du Colombier 		sysfatal("can't open icmp_ra connection: %r");
676e47528f3SDavid du Colombier 
677e47528f3SDavid du Colombier 	notify(catch);
6785e1edbcaSDavid du Colombier 	sendrscnt = Maxv6rss;
679e47528f3SDavid du Colombier 
680e47528f3SDavid du Colombier 	switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
681e47528f3SDavid du Colombier 	case -1:
682e47528f3SDavid du Colombier 		sysfatal("can't fork: %r");
683e47528f3SDavid du Colombier 	default:
684e47528f3SDavid du Colombier 		return;
685e47528f3SDavid du Colombier 	case 0:
6860a84db5eSDavid du Colombier 		break;
6870a84db5eSDavid du Colombier 	}
6880a84db5eSDavid du Colombier 
689e47528f3SDavid du Colombier 	procsetname("recvra6 on %s", conf.dev);
690e47528f3SDavid du Colombier 	ralog("recvra6 on %s", conf.dev);
6910a84db5eSDavid du Colombier 	sleepfor = jitter();
692e47528f3SDavid du Colombier 	for (;;) {
6930a84db5eSDavid du Colombier 		/*
6940a84db5eSDavid du Colombier 		 * We only get 3 (Maxv6rss) tries, so make sure we
6950a84db5eSDavid du Colombier 		 * wait long enough to be certain that at least one RA
6960a84db5eSDavid du Colombier 		 * will be transmitted.
6970a84db5eSDavid du Colombier 		 */
6980a84db5eSDavid du Colombier 		if (sleepfor < 7000)
6990a84db5eSDavid du Colombier 			sleepfor = 7000;
700e47528f3SDavid du Colombier 		alarm(sleepfor);
701e47528f3SDavid du Colombier 		n = read(fd, buf, sizeof buf);
702e47528f3SDavid du Colombier 		alarm(0);
703e47528f3SDavid du Colombier 		if (n <= 0) {
704e47528f3SDavid du Colombier 			if (sendrscnt > 0) {
705e47528f3SDavid du Colombier 				sendrscnt--;
7060a84db5eSDavid du Colombier 				if (recvra6on(conf.mpoint, myifc) == IsHostRecv)
707e47528f3SDavid du Colombier 					sendrs(fd);
7085e1edbcaSDavid du Colombier 				sleepfor = V6rsintvl + nrand(100);
709e47528f3SDavid du Colombier 			}
710e47528f3SDavid du Colombier 			if (sendrscnt == 0) {
711e47528f3SDavid du Colombier 				sendrscnt--;
712e47528f3SDavid du Colombier 				sleepfor = 0;
7130a84db5eSDavid du Colombier 				ralog("recvra6: no router advs after %d sols on %s",
7145e1edbcaSDavid du Colombier 					Maxv6rss, conf.dev);
715e47528f3SDavid du Colombier 			}
716e47528f3SDavid du Colombier 			continue;
717e47528f3SDavid du Colombier 		}
718e47528f3SDavid du Colombier 
719e47528f3SDavid du Colombier 		sleepfor = 0;
7200a84db5eSDavid du Colombier 		sendrscnt = -1;		/* got at least initial ra; no whining */
721e47528f3SDavid du Colombier 		switch (recvra6on(conf.mpoint, myifc)) {
722e47528f3SDavid du Colombier 		case IsRouter:
723e47528f3SDavid du Colombier 			recvrarouter(buf, n);
724e47528f3SDavid du Colombier 			break;
725e47528f3SDavid du Colombier 		case IsHostRecv:
726e47528f3SDavid du Colombier 			recvrahost(buf, n);
727e47528f3SDavid du Colombier 			break;
728e47528f3SDavid du Colombier 		case IsHostNoRecv:
7290a84db5eSDavid du Colombier 			ralog("recvra6: recvra off, quitting on %s", conf.dev);
730e47528f3SDavid du Colombier 			close(fd);
731e47528f3SDavid du Colombier 			exits(0);
732e47528f3SDavid du Colombier 		default:
7330a84db5eSDavid du Colombier 			ralog("recvra6: unable to read router status on %s",
734e47528f3SDavid du Colombier 				conf.dev);
735e47528f3SDavid du Colombier 			break;
736e47528f3SDavid du Colombier 		}
737e47528f3SDavid du Colombier 	}
738e47528f3SDavid du Colombier }
739e47528f3SDavid du Colombier 
740e47528f3SDavid du Colombier /*
741e47528f3SDavid du Colombier  * return -1 -- error, reading/writing some file,
742e47528f3SDavid du Colombier  *         0 -- no arp table updates
743e47528f3SDavid du Colombier  *         1 -- successful arp table update
744e47528f3SDavid du Colombier  */
745e47528f3SDavid du Colombier int
recvrs(uchar * buf,int pktlen,uchar * sol)746e47528f3SDavid du Colombier recvrs(uchar *buf, int pktlen, uchar *sol)
747e47528f3SDavid du Colombier {
748e47528f3SDavid du Colombier 	int n, optsz, arpfd;
749e47528f3SDavid du Colombier 	char abuf[256];
750e47528f3SDavid du Colombier 	Routersol *rs;
751e47528f3SDavid du Colombier 	Lladdropt *llao;
752e47528f3SDavid du Colombier 
753e47528f3SDavid du Colombier 	rs = (Routersol *)buf;
754e47528f3SDavid du Colombier 	n = sizeof *rs;
755e47528f3SDavid du Colombier 	optsz = pktlen - n;
756e47528f3SDavid du Colombier 	pkt2str(buf, buf+pktlen, abuf, abuf+nelem(abuf));
757e47528f3SDavid du Colombier 
758e47528f3SDavid du Colombier 	if (optsz != sizeof *llao)
759e47528f3SDavid du Colombier 		return 0;
7608c6ab946SDavid du Colombier 	if (buf[n] != V6nd_srclladdr || 8*buf[n+1] != sizeof *llao) {
761e47528f3SDavid du Colombier 		ralog("rs opt err %s", abuf);
762e47528f3SDavid du Colombier 		return -1;
763e47528f3SDavid du Colombier 	}
764e47528f3SDavid du Colombier 
765e47528f3SDavid du Colombier 	ralog("rs recv %s", abuf);
766e47528f3SDavid du Colombier 
767e47528f3SDavid du Colombier 	if (memcmp(rs->src, v6Unspecified, IPaddrlen) == 0)
768e47528f3SDavid du Colombier 		return 0;
769e47528f3SDavid du Colombier 
770e47528f3SDavid du Colombier 	snprint(abuf, sizeof abuf, "%s/arp", conf.mpoint);
771e47528f3SDavid du Colombier 	arpfd = open(abuf, OWRITE);
772e47528f3SDavid du Colombier 	if (arpfd < 0) {
773e47528f3SDavid du Colombier 		ralog("recvrs: can't open %s/arp to write: %r", conf.mpoint);
774e47528f3SDavid du Colombier 		return -1;
775e47528f3SDavid du Colombier 	}
776e47528f3SDavid du Colombier 
777e47528f3SDavid du Colombier 	llao = (Lladdropt *)buf[n];
778e47528f3SDavid du Colombier 	n = snprint(abuf, sizeof abuf, "add ether %I %E", rs->src, llao->lladdr);
779e47528f3SDavid du Colombier 	if (write(arpfd, abuf, n) < n) {
780e47528f3SDavid du Colombier 		ralog("recvrs: can't write to %s/arp: %r", conf.mpoint);
781e47528f3SDavid du Colombier 		close(arpfd);
782e47528f3SDavid du Colombier 		return -1;
783e47528f3SDavid du Colombier 	}
784e47528f3SDavid du Colombier 
785e47528f3SDavid du Colombier 	memmove(sol, rs->src, IPaddrlen);
786e47528f3SDavid du Colombier 	close(arpfd);
787e47528f3SDavid du Colombier 	return 1;
788e47528f3SDavid du Colombier }
789e47528f3SDavid du Colombier 
790e47528f3SDavid du Colombier void
sendra(int fd,uchar * dst,int rlt)791e47528f3SDavid du Colombier sendra(int fd, uchar *dst, int rlt)
792e47528f3SDavid du Colombier {
793e47528f3SDavid du Colombier 	int pktsz, preflen;
794e47528f3SDavid du Colombier 	char abuf[1024], tmp[40];
795e47528f3SDavid du Colombier 	uchar buf[1024], macaddr[6], src[IPaddrlen];
796e47528f3SDavid du Colombier 	Ipifc *ifc = nil;
797e47528f3SDavid du Colombier 	Iplifc *lifc, *nlifc;
798e47528f3SDavid du Colombier 	Lladdropt *llao;
799e47528f3SDavid du Colombier 	Prefixopt *prfo;
800e47528f3SDavid du Colombier 	Routeradv *ra;
801e47528f3SDavid du Colombier 
802e47528f3SDavid du Colombier 	memset(buf, 0, sizeof buf);
803e47528f3SDavid du Colombier 	ra = (Routeradv *)buf;
804e47528f3SDavid du Colombier 
805e47528f3SDavid du Colombier 	myetheraddr(macaddr, conf.dev);
806e47528f3SDavid du Colombier 	ea2lla(src, macaddr);
807e47528f3SDavid du Colombier 	memmove(ra->src, src, IPaddrlen);
808e47528f3SDavid du Colombier 	memmove(ra->dst, dst, IPaddrlen);
809e47528f3SDavid du Colombier 	ra->type = ICMP6_RA;
810e47528f3SDavid du Colombier 	ra->cttl = conf.ttl;
811e47528f3SDavid du Colombier 
812e47528f3SDavid du Colombier 	if (conf.mflag > 0)
813e47528f3SDavid du Colombier 		ra->mor |= MFMASK;
814e47528f3SDavid du Colombier 	if (conf.oflag > 0)
815e47528f3SDavid du Colombier 		ra->mor |= OCMASK;
816e47528f3SDavid du Colombier 	if (rlt > 0)
817e47528f3SDavid du Colombier 		hnputs(ra->routerlt, conf.routerlt);
818e47528f3SDavid du Colombier 	else
819e47528f3SDavid du Colombier 		hnputs(ra->routerlt, 0);
820e47528f3SDavid du Colombier 	hnputl(ra->rchbltime, conf.reachtime);
821e47528f3SDavid du Colombier 	hnputl(ra->rxmtimer, conf.rxmitra);
822e47528f3SDavid du Colombier 
823e47528f3SDavid du Colombier 	pktsz = sizeof *ra;
824e47528f3SDavid du Colombier 
825e47528f3SDavid du Colombier 	/* include all global unicast prefixes on interface in prefix options */
826e47528f3SDavid du Colombier 	ifc = readipifc(conf.mpoint, ifc, myifc);
827e47528f3SDavid du Colombier 	for (lifc = (ifc? ifc->lifc: nil); lifc; lifc = nlifc) {
828e47528f3SDavid du Colombier 		nlifc = lifc->next;
829e47528f3SDavid du Colombier 		prfo = (Prefixopt *)(buf + pktsz);
8305e1edbcaSDavid du Colombier 		/* global unicast address? */
8315e1edbcaSDavid du Colombier 		if (!ISIPV6LINKLOCAL(lifc->ip) && !ISIPV6MCAST(lifc->ip) &&
8325e1edbcaSDavid du Colombier 		    memcmp(lifc->ip, IPnoaddr, IPaddrlen) != 0 &&
8330a84db5eSDavid du Colombier 		    memcmp(lifc->ip, v6loopback, IPaddrlen) != 0 &&
8340a84db5eSDavid du Colombier 		    !isv4(lifc->ip)) {
835e47528f3SDavid du Colombier 			memmove(prfo->pref, lifc->net, IPaddrlen);
836e47528f3SDavid du Colombier 
837e47528f3SDavid du Colombier 			/* hack to find prefix length */
838e47528f3SDavid du Colombier 			snprint(tmp, sizeof tmp, "%M", lifc->mask);
839e47528f3SDavid du Colombier 			preflen = atoi(&tmp[1]);
840e47528f3SDavid du Colombier 			prfo->plen = preflen & 0xff;
841e47528f3SDavid du Colombier 			if (prfo->plen == 0)
842e47528f3SDavid du Colombier 				continue;
843e47528f3SDavid du Colombier 
8448c6ab946SDavid du Colombier 			prfo->type = V6nd_pfxinfo;
845e47528f3SDavid du Colombier 			prfo->len = 4;
846e47528f3SDavid du Colombier 			prfo->lar = AFMASK;
847e47528f3SDavid du Colombier 			hnputl(prfo->validlt, lifc->validlt);
848e47528f3SDavid du Colombier 			hnputl(prfo->preflt, lifc->preflt);
849e47528f3SDavid du Colombier 			pktsz += sizeof *prfo;
850e47528f3SDavid du Colombier 		}
851e47528f3SDavid du Colombier 	}
852e47528f3SDavid du Colombier 	/*
853e47528f3SDavid du Colombier 	 * include link layer address (mac address for now) in
854e47528f3SDavid du Colombier 	 * link layer address option
855e47528f3SDavid du Colombier 	 */
856e47528f3SDavid du Colombier 	llao = (Lladdropt *)(buf + pktsz);
8578c6ab946SDavid du Colombier 	llao->type = V6nd_srclladdr;
858e47528f3SDavid du Colombier 	llao->len = 1;
859e47528f3SDavid du Colombier 	memmove(llao->lladdr, macaddr, sizeof macaddr);
860e47528f3SDavid du Colombier 	pktsz += sizeof *llao;
861e47528f3SDavid du Colombier 
862e47528f3SDavid du Colombier 	pkt2str(buf+40, buf+pktsz, abuf, abuf+1024);
863e47528f3SDavid du Colombier 	if(write(fd, buf, pktsz) < pktsz)
8640a84db5eSDavid du Colombier 		ralog("sendra fail %s: %r", abuf);
8650a84db5eSDavid du Colombier 	else if (debug)
8660a84db5eSDavid du Colombier 		ralog("sendra succ %s", abuf);
867e47528f3SDavid du Colombier }
868e47528f3SDavid du Colombier 
869e47528f3SDavid du Colombier /*
8700a84db5eSDavid du Colombier  * daemon to send router advertisements to hosts
871e47528f3SDavid du Colombier  */
872e47528f3SDavid du Colombier void
sendra6(void)873e47528f3SDavid du Colombier sendra6(void)
874e47528f3SDavid du Colombier {
875e47528f3SDavid du Colombier 	int fd, cfd, n, dstknown = 0, sendracnt, sleepfor, nquitmsgs;
8760a84db5eSDavid du Colombier 	long lastra, now;
877e47528f3SDavid du Colombier 	uchar buf[4096], dst[IPaddrlen];
878e47528f3SDavid du Colombier 	Ipifc *ifc = nil;
879e47528f3SDavid du Colombier 
880e47528f3SDavid du Colombier 	fd = dialicmp(v6allnodesL, ICMP6_RS, &cfd);
881e47528f3SDavid du Colombier 	if (fd < 0)
882e47528f3SDavid du Colombier 		sysfatal("can't open icmp_rs connection: %r");
883e47528f3SDavid du Colombier 
884e47528f3SDavid du Colombier 	notify(catch);
8855e1edbcaSDavid du Colombier 	sendracnt = Maxv6initras;
8865e1edbcaSDavid du Colombier 	nquitmsgs = Maxv6finalras;
887e47528f3SDavid du Colombier 
888e47528f3SDavid du Colombier 	switch(rfork(RFPROC|RFMEM|RFFDG|RFNOWAIT|RFNOTEG)){
889e47528f3SDavid du Colombier 	case -1:
890e47528f3SDavid du Colombier 		sysfatal("can't fork: %r");
891e47528f3SDavid du Colombier 	default:
892e47528f3SDavid du Colombier 		return;
893e47528f3SDavid du Colombier 	case 0:
8940a84db5eSDavid du Colombier 		break;
8950a84db5eSDavid du Colombier 	}
8960a84db5eSDavid du Colombier 
897e47528f3SDavid du Colombier 	procsetname("sendra6 on %s", conf.dev);
898e47528f3SDavid du Colombier 	ralog("sendra6 on %s", conf.dev);
8990a84db5eSDavid du Colombier 	sleepfor = jitter();
900e47528f3SDavid du Colombier 	for (;;) {
901e47528f3SDavid du Colombier 		lastra = time(0);
9020a84db5eSDavid du Colombier 		if (sleepfor < 0)
9030a84db5eSDavid du Colombier 			sleepfor = 0;
904e47528f3SDavid du Colombier 		alarm(sleepfor);
905e47528f3SDavid du Colombier 		n = read(fd, buf, sizeof buf);
906e47528f3SDavid du Colombier 		alarm(0);
907e47528f3SDavid du Colombier 
908e47528f3SDavid du Colombier 		ifc = readipifc(conf.mpoint, ifc, myifc);
909e47528f3SDavid du Colombier 		if (ifc == nil) {
9100a84db5eSDavid du Colombier 			ralog("sendra6: can't read router params on %s",
911e47528f3SDavid du Colombier 				conf.mpoint);
912e47528f3SDavid du Colombier 			continue;
913e47528f3SDavid du Colombier 		}
914e47528f3SDavid du Colombier 
915e47528f3SDavid du Colombier 		if (ifc->sendra6 <= 0)
916e47528f3SDavid du Colombier 			if (nquitmsgs > 0) {
917e47528f3SDavid du Colombier 				sendra(fd, v6allnodesL, 0);
918e47528f3SDavid du Colombier 				nquitmsgs--;
9190a84db5eSDavid du Colombier 				sleepfor = Minv6interradelay + jitter();
920e47528f3SDavid du Colombier 				continue;
921e47528f3SDavid du Colombier 			} else {
9220a84db5eSDavid du Colombier 				ralog("sendra6: sendra off, quitting on %s",
9230a84db5eSDavid du Colombier 					conf.dev);
924e47528f3SDavid du Colombier 				exits(0);
925e47528f3SDavid du Colombier 			}
926e47528f3SDavid du Colombier 
9275e1edbcaSDavid du Colombier 		nquitmsgs = Maxv6finalras;
928e47528f3SDavid du Colombier 
9298c6ab946SDavid du Colombier 		if (n <= 0) {			/* no RS */
930e47528f3SDavid du Colombier 			if (sendracnt > 0)
931e47528f3SDavid du Colombier 				sendracnt--;
9328c6ab946SDavid du Colombier 		} else {			/* respond to RS */
933e47528f3SDavid du Colombier 			dstknown = recvrs(buf, n, dst);
9340a84db5eSDavid du Colombier 			now = time(0);
935e47528f3SDavid du Colombier 
9360a84db5eSDavid du Colombier 			if (now - lastra < Minv6interradelay) {
937e47528f3SDavid du Colombier 				/* too close, skip */
9380a84db5eSDavid du Colombier 				sleepfor = lastra + Minv6interradelay +
9390a84db5eSDavid du Colombier 					jitter() - now;
940e47528f3SDavid du Colombier 				continue;
941e47528f3SDavid du Colombier 			}
9420a84db5eSDavid du Colombier 			sleep(jitter());
943e47528f3SDavid du Colombier 		}
9440a84db5eSDavid du Colombier 		sleepfor = randint(ifc->rp.minraint, ifc->rp.maxraint);
945e47528f3SDavid du Colombier 		if (dstknown > 0)
946e47528f3SDavid du Colombier 			sendra(fd, dst, 1);
947e47528f3SDavid du Colombier 		else
948e47528f3SDavid du Colombier 			sendra(fd, v6allnodesL, 1);
949e47528f3SDavid du Colombier 	}
950e47528f3SDavid du Colombier }
951e47528f3SDavid du Colombier 
952e47528f3SDavid du Colombier void
startra6(void)953ccaac148SDavid du Colombier startra6(void)
954e47528f3SDavid du Colombier {
955e47528f3SDavid du Colombier 	static char routeon[] = "iprouting 1";
956e47528f3SDavid du Colombier 
957e47528f3SDavid du Colombier 	if (conf.recvra > 0)
958e47528f3SDavid du Colombier 		recvra6();
959e47528f3SDavid du Colombier 
960e47528f3SDavid du Colombier 	if (conf.sendra > 0) {
961e47528f3SDavid du Colombier 		if (write(conf.cfd, routeon, sizeof routeon - 1) < 0) {
9628c6ab946SDavid du Colombier 			warning("write (iprouting 1) failed: %r");
963e47528f3SDavid du Colombier 			return;
964e47528f3SDavid du Colombier 		}
965e47528f3SDavid du Colombier 		sendra6();
966e47528f3SDavid du Colombier 		if (conf.recvra <= 0)
967e47528f3SDavid du Colombier 			recvra6();
968e47528f3SDavid du Colombier 	}
969e47528f3SDavid du Colombier }
970e47528f3SDavid du Colombier 
971e47528f3SDavid du Colombier void
doipv6(int what)972ccaac148SDavid du Colombier doipv6(int what)
973e47528f3SDavid du Colombier {
974e47528f3SDavid du Colombier 	nip = nipifcs(conf.mpoint);
975e47528f3SDavid du Colombier 	if(!noconfig){
976e47528f3SDavid du Colombier 		lookforip(conf.mpoint);
977e47528f3SDavid du Colombier 		controldevice();
978e47528f3SDavid du Colombier 		binddevice();
979e47528f3SDavid du Colombier 	}
980e47528f3SDavid du Colombier 
981e47528f3SDavid du Colombier 	switch (what) {
982e47528f3SDavid du Colombier 	default:
9838c6ab946SDavid du Colombier 		sysfatal("unknown IPv6 verb");
984e47528f3SDavid du Colombier 	case Vaddpref6:
985ccaac148SDavid du Colombier 		issueadd6(&conf);
986e47528f3SDavid du Colombier 		break;
987e47528f3SDavid du Colombier 	case Vra6:
988ccaac148SDavid du Colombier 		issuebasera6(&conf);
989ccaac148SDavid du Colombier 		issuerara6(&conf);
9900a84db5eSDavid du Colombier 		dolog = 1;
991ccaac148SDavid du Colombier 		startra6();
992e47528f3SDavid du Colombier 		break;
993e47528f3SDavid du Colombier 	}
994e47528f3SDavid du Colombier }
995