• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * dhcpcd - DHCP client daemon
3  * Copyright (c) 2006-2012 Roy Marples <roy@marples.name>
4  * All rights reserved
5 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 #include <sys/socket.h>
30 #include <net/if.h>
31 #include <netinet/in.h>
32 #include <netinet/ip6.h>
33 #include <netinet/icmp6.h>
34 
35 #include <errno.h>
36 #include <stddef.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <syslog.h>
40 
41 #ifdef __linux__
42 #  define _LINUX_IN6_H
43 #  include <linux/ipv6.h>
44 #endif
45 
46 #define ELOOP_QUEUE 1
47 #include "bind.h"
48 #include "common.h"
49 #include "configure.h"
50 #include "dhcpcd.h"
51 #include "eloop.h"
52 #include "ipv6rs.h"
53 
54 #define ALLROUTERS "ff02::2"
55 #define HOPLIMIT 255
56 
57 #define ROUNDUP8(a) (1 + (((a) - 1) | 7))
58 
59 #define RTR_SOLICITATION_INTERVAL       4 /* seconds */
60 #define MAX_RTR_SOLICITATIONS           3 /* times */
61 
62 #ifndef ND_OPT_RDNSS
63 #define ND_OPT_RDNSS			25
64 struct nd_opt_rdnss {           /* RDNSS option RFC 6106 */
65 	uint8_t		nd_opt_rdnss_type;
66 	uint8_t		nd_opt_rdnss_len;
67 	uint16_t	nd_opt_rdnss_reserved;
68 	uint32_t	nd_opt_rdnss_lifetime;
69         /* followed by list of IP prefixes */
70 } _packed;
71 #endif
72 
73 #ifndef ND_OPT_DNSSL
74 #define ND_OPT_DNSSL			31
75 struct nd_opt_dnssl {		/* DNSSL option RFC 6106 */
76 	uint8_t		nd_opt_dnssl_type;
77 	uint8_t		nd_opt_dnssl_len;
78 	uint16_t	nd_opt_dnssl_reserved;
79 	uint32_t	nd_opt_dnssl_lifetime;
80 	/* followed by list of DNS servers */
81 } _packed;
82 #endif
83 
84 static int sock;
85 static struct sockaddr_in6 allrouters, from;
86 static struct msghdr sndhdr;
87 static struct iovec sndiov[2];
88 static unsigned char *sndbuf;
89 static struct msghdr rcvhdr;
90 static struct iovec rcviov[2];
91 static unsigned char *rcvbuf;
92 static unsigned char ansbuf[1500];
93 static char ntopbuf[INET6_ADDRSTRLEN];
94 
95 #if DEBUG_MEMORY
96 static void
ipv6rs_cleanup(void)97 ipv6rs_cleanup(void)
98 {
99 
100 	free(sndbuf);
101 	free(rcvbuf);
102 }
103 #endif
104 
105 int
ipv6rs_open(void)106 ipv6rs_open(void)
107 {
108 	int on;
109 	int len;
110 	struct icmp6_filter filt;
111 
112 	memset(&allrouters, 0, sizeof(allrouters));
113 	allrouters.sin6_family = AF_INET6;
114 #ifdef SIN6_LEN
115 	allrouters.sin6_len = sizeof(allrouters);
116 #endif
117 	if (inet_pton(AF_INET6, ALLROUTERS, &allrouters.sin6_addr.s6_addr) != 1)
118 		return -1;
119 	sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
120 	if (sock == -1)
121 		return -1;
122 	on = 1;
123 	if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
124 		&on, sizeof(on)) == -1)
125 		return -1;
126 
127 	on = 1;
128 	if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
129 		&on, sizeof(on)) == -1)
130 		return -1;
131 
132 	ICMP6_FILTER_SETBLOCKALL(&filt);
133 	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
134 	if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER,
135 		&filt, sizeof(filt)) == -1)
136 		return -1;
137 
138 #if DEBUG_MEMORY
139 	atexit(ipv6rs_cleanup);
140 #endif
141 
142 	len = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int));
143 	sndbuf = xzalloc(len);
144 	if (sndbuf == NULL)
145 		return -1;
146 	sndhdr.msg_namelen = sizeof(struct sockaddr_in6);
147 	sndhdr.msg_iov = sndiov;
148 	sndhdr.msg_iovlen = 1;
149 	sndhdr.msg_control = sndbuf;
150 	sndhdr.msg_controllen = len;
151 	rcvbuf = xzalloc(len);
152 	if (rcvbuf == NULL)
153 		return -1;
154 	rcvhdr.msg_name = &from;
155 	rcvhdr.msg_namelen = sizeof(from);
156 	rcvhdr.msg_iov = rcviov;
157 	rcvhdr.msg_iovlen = 1;
158 	rcvhdr.msg_control = rcvbuf;
159 	rcvhdr.msg_controllen = len;
160 	rcviov[0].iov_base = ansbuf;
161 	rcviov[0].iov_len = sizeof(ansbuf);
162 	return sock;
163 }
164 
165 static int
ipv6rs_makeprobe(struct interface * ifp)166 ipv6rs_makeprobe(struct interface *ifp)
167 {
168 	struct nd_router_solicit *rs;
169 	struct nd_opt_hdr *nd;
170 
171 	free(ifp->rs);
172 	ifp->rslen = sizeof(*rs) + ROUNDUP8(ifp->hwlen + 2);
173 	ifp->rs = xzalloc(ifp->rslen);
174 	if (ifp->rs == NULL)
175 		return -1;
176 	rs = (struct nd_router_solicit *)ifp->rs;
177 	rs->nd_rs_type = ND_ROUTER_SOLICIT;
178 	rs->nd_rs_code = 0;
179 	rs->nd_rs_cksum = 0;
180 	rs->nd_rs_reserved = 0;
181 	nd = (struct nd_opt_hdr *)(ifp->rs + sizeof(*rs));
182 	nd->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
183 	nd->nd_opt_len = (ROUNDUP8(ifp->hwlen + 2)) >> 3;
184 	memcpy(nd + 1, ifp->hwaddr, ifp->hwlen);
185 	return 0;
186 }
187 
188 static void
ipv6rs_sendprobe(void * arg)189 ipv6rs_sendprobe(void *arg)
190 {
191 	struct interface *ifp = arg;
192 	struct sockaddr_in6 dst;
193 	struct cmsghdr *cm;
194 	struct in6_pktinfo pi;
195 	int hoplimit = HOPLIMIT;
196 
197 	dst = allrouters;
198 	//dst.sin6_scope_id = ifp->linkid;
199 
200 	ipv6rs_makeprobe(ifp);
201 	sndhdr.msg_name = (caddr_t)&dst;
202 	sndhdr.msg_iov[0].iov_base = ifp->rs;
203 	sndhdr.msg_iov[0].iov_len = ifp->rslen;
204 
205 	/* Set the outbound interface */
206 	cm = CMSG_FIRSTHDR(&sndhdr);
207 	cm->cmsg_level = IPPROTO_IPV6;
208 	cm->cmsg_type = IPV6_PKTINFO;
209 	cm->cmsg_len = CMSG_LEN(sizeof(pi));
210 	memset(&pi, 0, sizeof(pi));
211 	pi.ipi6_ifindex = if_nametoindex(ifp->name);
212 	memcpy(CMSG_DATA(cm), &pi, sizeof(pi));
213 
214 	/* Hop limit */
215 	cm = CMSG_NXTHDR(&sndhdr, cm);
216 	cm->cmsg_level = IPPROTO_IPV6;
217 	cm->cmsg_type = IPV6_HOPLIMIT;
218 	cm->cmsg_len = CMSG_LEN(sizeof(hoplimit));
219 	memcpy(CMSG_DATA(cm), &hoplimit, sizeof(hoplimit));
220 
221 	syslog(LOG_INFO, "%s: sending IPv6 Router Solicitation", ifp->name);
222 	if (sendmsg(sock, &sndhdr, 0) == -1)
223 		syslog(LOG_ERR, "%s: sendmsg: %m", ifp->name);
224 
225 	if (ifp->rsprobes++ < MAX_RTR_SOLICITATIONS)
226 		add_timeout_sec(RTR_SOLICITATION_INTERVAL,
227 		    ipv6rs_sendprobe, ifp);
228 	else
229 		syslog(LOG_INFO, "%s: no IPv6 Routers available", ifp->name);
230 }
231 
232 static void
ipv6rs_sort(struct interface * ifp)233 ipv6rs_sort(struct interface *ifp)
234 {
235 	struct ra *rap, *sorted, *ran, *rat;
236 
237 	if (ifp->ras == NULL || ifp->ras->next == NULL)
238 		return;
239 
240 	/* Sort our RA's - most recent first */
241 	sorted = ifp->ras;
242 	ifp->ras = ifp->ras->next;
243 	sorted->next = NULL;
244 	for (rap = ifp->ras; rap && (ran = rap->next, 1); rap = ran) {
245 		/* Are we the new head? */
246 		if (timercmp(&rap->received, &sorted->received, <)) {
247 			rap->next = sorted;
248 			sorted = rap;
249 			continue;
250 		}
251 		/* Do we fit in the middle? */
252 		for (rat = sorted; rat->next; rat = rat->next) {
253 			if (timercmp(&rap->received, &rat->next->received, <)) {
254 				rap->next = rat->next;
255 				rat->next = rap;
256 				break;
257 			}
258 		}
259 		/* We must be at the end */
260 		if (!rat->next) {
261 			rat->next = rap;
262 			rap->next = NULL;
263 		}
264 	}
265 }
266 
267 void
ipv6rs_handledata(_unused void * arg)268 ipv6rs_handledata(_unused void *arg)
269 {
270 	ssize_t len, l, n, olen;
271 	struct cmsghdr *cm;
272 	int hoplimit;
273 	struct in6_pktinfo pkt;
274 	struct icmp6_hdr *icp;
275 	struct interface *ifp;
276 	const char *sfrom;
277 	struct nd_router_advert *nd_ra;
278 	struct nd_opt_prefix_info *pi;
279 	struct nd_opt_mtu *mtu;
280 	struct nd_opt_rdnss *rdnss;
281 	struct nd_opt_dnssl *dnssl;
282 	uint32_t lifetime;
283 	uint8_t *p, *op;
284 	struct in6_addr addr;
285 	char buf[INET6_ADDRSTRLEN];
286 	const char *cbp;
287 	struct ra *rap;
288 	struct nd_opt_hdr *ndo;
289 	struct ra_opt *rao, *raol;
290 	char *opt;
291 	struct timeval expire;
292 	int has_dns;
293 
294 	len = recvmsg(sock, &rcvhdr, 0);
295 	if (len == -1) {
296 		syslog(LOG_ERR, "recvmsg: %m");
297 		return;
298 	}
299 	sfrom = inet_ntop(AF_INET6, &from.sin6_addr,
300 	    ntopbuf, INET6_ADDRSTRLEN);
301 	if ((size_t)len < sizeof(struct nd_router_advert)) {
302 		syslog(LOG_ERR, "IPv6 RA packet too short from %s", sfrom);
303 		return;
304 	}
305 
306 	pkt.ipi6_ifindex = hoplimit = 0;
307 	for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvhdr);
308 	     cm;
309 	     cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvhdr, cm))
310 	{
311 		if (cm->cmsg_level != IPPROTO_IPV6)
312 			continue;
313 		switch(cm->cmsg_type) {
314 		case IPV6_PKTINFO:
315 			if (cm->cmsg_len == CMSG_LEN(sizeof(pkt)))
316 				memcpy(&pkt, CMSG_DATA(cm), sizeof(pkt));
317 			break;
318 		case IPV6_HOPLIMIT:
319 			if (cm->cmsg_len == CMSG_LEN(sizeof(int)))
320 				memcpy(&hoplimit, CMSG_DATA(cm), sizeof(int));
321 			break;
322 		}
323 	}
324 
325 	if (pkt.ipi6_ifindex == 0 || hoplimit == 0) {
326 		syslog(LOG_ERR,
327 		    "IPv6 RA did not contain index or hop limit from %s",
328 		    sfrom);
329 		return;
330 	}
331 
332 	icp = (struct icmp6_hdr *)rcvhdr.msg_iov[0].iov_base;
333 	if (icp->icmp6_type != ND_ROUTER_ADVERT ||
334 	    icp->icmp6_code != 0)
335 	{
336 		syslog(LOG_ERR, "invalid IPv6 type or code from %s", sfrom);
337 		return;
338 	}
339 
340 	if (!IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
341 		syslog(LOG_ERR, "RA recieved from non local IPv6 address %s",
342 		    sfrom);
343 		return;
344 	}
345 
346 	for (ifp = ifaces; ifp; ifp = ifp->next)
347 		if (if_nametoindex(ifp->name) == (unsigned int)pkt.ipi6_ifindex)
348 			break;
349 	if (ifp == NULL) {
350 		syslog(LOG_ERR,"received RA for unexpected interface from %s",
351 		    sfrom);
352 		return;
353 	}
354 	for (rap = ifp->ras; rap; rap = rap->next) {
355 		if (memcmp(rap->from.s6_addr, from.sin6_addr.s6_addr,
356 		    sizeof(rap->from.s6_addr)) == 0)
357 			break;
358 	}
359 
360 	/* We don't want to spam the log with the fact we got an RA every
361 	 * 30 seconds or so, so only spam the log if it's different. */
362 	if (options & DHCPCD_DEBUG || rap == NULL ||
363 	    (rap->expired || rap->data_len != len ||
364 	     memcmp(rap->data, (unsigned char *)icp, rap->data_len) != 0))
365 	{
366 		if (rap) {
367 			free(rap->data);
368 			rap->data_len = 0;
369 		}
370 		syslog(LOG_INFO, "%s: Router Advertisement from %s",
371 		    ifp->name, sfrom);
372 	}
373 
374 	if (rap == NULL) {
375 		rap = xmalloc(sizeof(*rap));
376 		rap->next = ifp->ras;
377 		rap->options = NULL;
378 		ifp->ras = rap;
379 		memcpy(rap->from.s6_addr, from.sin6_addr.s6_addr,
380 		    sizeof(rap->from.s6_addr));
381 		strlcpy(rap->sfrom, sfrom, sizeof(rap->sfrom));
382 		rap->data_len = 0;
383 	}
384 	if (rap->data_len == 0) {
385 		rap->data = xmalloc(len);
386 		memcpy(rap->data, icp, len);
387 		rap->data_len = len;
388 	}
389 
390 	get_monotonic(&rap->received);
391 	nd_ra = (struct nd_router_advert *)icp;
392 	rap->lifetime = ntohs(nd_ra->nd_ra_router_lifetime);
393 	rap->expired = 0;
394 
395 	len -= sizeof(struct nd_router_advert);
396 	p = ((uint8_t *)icp) + sizeof(struct nd_router_advert);
397 	olen = 0;
398 	lifetime = ~0U;
399 	has_dns = 0;
400 	for (olen = 0; len > 0; p += olen, len -= olen) {
401 		if ((size_t)len < sizeof(struct nd_opt_hdr)) {
402 			syslog(LOG_ERR, "%s: Short option", ifp->name);
403 			break;
404 		}
405 		ndo = (struct nd_opt_hdr *)p;
406 		olen = ndo->nd_opt_len * 8 ;
407 		if (olen == 0) {
408 			syslog(LOG_ERR, "%s: zero length option", ifp->name);
409 			break;
410 		}
411 		if (olen > len) {
412 			syslog(LOG_ERR,
413 			    "%s: Option length exceeds message", ifp->name);
414 			break;
415 		}
416 
417 		opt = NULL;
418 		switch (ndo->nd_opt_type) {
419 		case ND_OPT_PREFIX_INFORMATION:
420 			pi = (struct nd_opt_prefix_info *)ndo;
421 			if (pi->nd_opt_pi_len != 4) {
422 				syslog(LOG_ERR,
423 				    "%s: invalid option len for prefix",
424 				    ifp->name);
425 				break;
426 			}
427 			if (pi->nd_opt_pi_prefix_len > 128) {
428 				syslog(LOG_ERR, "%s: invalid prefix len",
429 				    ifp->name);
430 				break;
431 			}
432 			if (IN6_IS_ADDR_MULTICAST(&pi->nd_opt_pi_prefix) ||
433 			    IN6_IS_ADDR_LINKLOCAL(&pi->nd_opt_pi_prefix))
434 			{
435 				syslog(LOG_ERR,
436 				    "%s: invalid prefix in RA", ifp->name);
437 				break;
438 			}
439 			opt = xstrdup(inet_ntop(AF_INET6,
440 			    pi->nd_opt_pi_prefix.s6_addr,
441 			    ntopbuf, INET6_ADDRSTRLEN));
442 			if (opt) {
443 				rap->prefix_len = pi->nd_opt_pi_prefix_len;
444 				rap->prefix_vltime =
445 					ntohl(pi->nd_opt_pi_valid_time);
446 				rap->prefix_pltime =
447 					ntohl(pi->nd_opt_pi_preferred_time);
448 			}
449 			break;
450 
451 		case ND_OPT_MTU:
452 			mtu = (struct nd_opt_mtu *)p;
453 			snprintf(buf, sizeof(buf), "%d",
454 			    ntohl(mtu->nd_opt_mtu_mtu));
455 			opt = xstrdup(buf);
456 			break;
457 
458 		case ND_OPT_RDNSS:
459 			rdnss = (struct nd_opt_rdnss *)p;
460 			lifetime = ntohl(rdnss->nd_opt_rdnss_lifetime);
461 			op = (uint8_t *)ndo;
462 			op += offsetof(struct nd_opt_rdnss,
463 			    nd_opt_rdnss_lifetime);
464 			op += sizeof(rdnss->nd_opt_rdnss_lifetime);
465 			l = 0;
466 			for (n = ndo->nd_opt_len - 1; n > 1; n -= 2) {
467 				memcpy(&addr.s6_addr, op, sizeof(addr.s6_addr));
468 				cbp = inet_ntop(AF_INET6, &addr,
469 				    ntopbuf, INET6_ADDRSTRLEN);
470 				if (cbp == NULL) {
471 					syslog(LOG_ERR,
472 					    "%s: invalid RDNSS address",
473 					    ifp->name);
474 				} else {
475 					if (opt) {
476 						l = strlen(opt);
477 						opt = xrealloc(opt,
478 							l + strlen(cbp) + 2);
479 						opt[l] = ' ';
480 						strcpy(opt + l + 1, cbp);
481 					} else
482 						opt = xstrdup(cbp);
483 					if (lifetime > 0)
484 						has_dns = 1;
485 				}
486 		        	op += sizeof(addr.s6_addr);
487 			}
488 			break;
489 
490 		case ND_OPT_DNSSL:
491 			dnssl = (struct nd_opt_dnssl *)p;
492 			lifetime = ntohl(dnssl->nd_opt_dnssl_lifetime);
493 			op = p + offsetof(struct nd_opt_dnssl,
494 			    nd_opt_dnssl_lifetime);
495 			op += sizeof(dnssl->nd_opt_dnssl_lifetime);
496 			n = (dnssl->nd_opt_dnssl_len - 1) * 8;
497 			l = decode_rfc3397(NULL, 0, n, op);
498 			if (l < 1) {
499 				syslog(LOG_ERR, "%s: invalid DNSSL option",
500 				    ifp->name);
501 			} else {
502 				opt = xmalloc(l);
503 				decode_rfc3397(opt, l, n, op);
504 			}
505 			break;
506 		}
507 
508 		if (opt == NULL)
509 			continue;
510 		for (raol = NULL, rao = rap->options;
511 		    rao;
512 		    raol = rao, rao = rao->next)
513 		{
514 			if (rao->type == ndo->nd_opt_type &&
515 			    strcmp(rao->option, opt) == 0)
516 				break;
517 		}
518 		if (lifetime == 0) {
519 			if (rao) {
520 				if (raol)
521 					raol->next = rao->next;
522 				else
523 					rap->options = rao->next;
524 				free(rao->option);
525 				free(rao);
526 			}
527 			continue;
528 		}
529 
530 		if (rao == NULL) {
531 			rao = xmalloc(sizeof(*rao));
532 			rao->next = rap->options;
533 			rap->options = rao;
534 			rao->type = ndo->nd_opt_type;
535 			rao->option = opt;
536 		} else
537 			free(opt);
538 		if (lifetime == ~0U)
539 			timerclear(&rao->expire);
540 		else {
541 			expire.tv_sec = lifetime;
542 			expire.tv_usec = 0;
543 			timeradd(&rap->received, &expire, &rao->expire);
544 		}
545 	}
546 
547 	ipv6rs_sort(ifp);
548 	run_script_reason(ifp, options & DHCPCD_TEST ? "TEST" : "ROUTERADVERT");
549 	if (options & DHCPCD_TEST)
550 		exit(EXIT_SUCCESS);
551 
552 	/* If we don't require RDNSS then set has_dns = 1 so we fork */
553 	if (!(ifp->state->options->options & DHCPCD_IPV6RA_REQRDNSS))
554 		has_dns = 1;
555 
556 	if (has_dns)
557 		delete_q_timeout(0, handle_exit_timeout, NULL);
558 	delete_timeout(NULL, ifp);
559 	ipv6rs_expire(ifp);
560 	if (has_dns)
561 		daemonise();
562 	else if (options & DHCPCD_DAEMONISE && !(options & DHCPCD_DAEMONISED))
563 		syslog(LOG_WARNING,
564 		    "%s: did not fork due to an absent RDNSS option in the RA",
565 		    ifp->name);
566 }
567 
568 ssize_t
ipv6rs_env(char ** env,const char * prefix,const struct interface * ifp)569 ipv6rs_env(char **env, const char *prefix, const struct interface *ifp)
570 {
571 	ssize_t l;
572 	struct timeval now;
573 	const struct ra *rap;
574 	const struct ra_opt *rao;
575 	int i;
576 	char buffer[32], buffer2[32];
577 	const char *optn;
578 
579 	l = 0;
580 	get_monotonic(&now);
581 	for (rap = ifp->ras, i = 1; rap; rap = rap->next, i++) {
582 		if (env) {
583 			snprintf(buffer, sizeof(buffer),
584 			    "ra%d_from", i);
585 			setvar(&env, prefix, buffer, rap->sfrom);
586 		}
587 		l++;
588 
589 		for (rao = rap->options; rao; rao = rao->next) {
590 			if (rao->option == NULL)
591 				continue;
592 			if (env == NULL) {
593 				switch (rao->type) {
594 				case ND_OPT_PREFIX_INFORMATION:
595 					l += 4;
596 					break;
597 				default:
598 					l++;
599 				}
600 				continue;
601 			}
602 			switch (rao->type) {
603 			case ND_OPT_PREFIX_INFORMATION:
604 				optn = "prefix";
605 				break;
606 			case ND_OPT_MTU:
607 				optn = "mtu";
608 				break;
609 			case ND_OPT_RDNSS:
610 				optn = "rdnss";
611 				break;
612 			case ND_OPT_DNSSL:
613 				optn = "dnssl";
614 				break;
615 			default:
616 				continue;
617 			}
618 			snprintf(buffer, sizeof(buffer), "ra%d_%s", i, optn);
619 			setvar(&env, prefix, buffer, rao->option);
620 			l++;
621 			switch (rao->type) {
622 			case ND_OPT_PREFIX_INFORMATION:
623 				snprintf(buffer, sizeof(buffer),
624 				    "ra%d_prefix_len", i);
625 				snprintf(buffer2, sizeof(buffer2),
626 				    "%d", rap->prefix_len);
627 				setvar(&env, prefix, buffer, buffer2);
628 
629 				snprintf(buffer, sizeof(buffer),
630 				    "ra%d_prefix_vltime", i);
631 				snprintf(buffer2, sizeof(buffer2),
632 				    "%d", rap->prefix_vltime);
633 				setvar(&env, prefix, buffer, buffer2);
634 
635 				snprintf(buffer, sizeof(buffer),
636 				    "ra%d_prefix_pltime", i);
637 				snprintf(buffer2, sizeof(buffer2),
638 				    "%d", rap->prefix_pltime);
639 				setvar(&env, prefix, buffer, buffer2);
640 				l += 3;
641 				break;
642 			}
643 
644 		}
645 	}
646 
647 	if (env)
648 		setvard(&env, prefix, "ra_count", i - 1);
649 	l++;
650 	return l;
651 }
652 
653 static void
ipv6rs_free_opts(struct ra * rap)654 ipv6rs_free_opts(struct ra *rap)
655 {
656 	struct ra_opt *rao, *raon;
657 
658 	for (rao = rap->options; rao && (raon = rao->next, 1); rao = raon) {
659 		free(rao->option);
660 		free(rao);
661 	}
662 }
663 
664 void
ipv6rs_free(struct interface * ifp)665 ipv6rs_free(struct interface *ifp)
666 {
667 	struct ra *rap, *ran;
668 
669 	free(ifp->rs);
670 	ifp->rs = NULL;
671 	for (rap = ifp->ras; rap && (ran = rap->next, 1); rap = ran) {
672 		ipv6rs_free_opts(rap);
673 		free(rap->data);
674 		free(rap);
675 	}
676 	ifp->ras = NULL;
677 }
678 
679 void
ipv6rs_expire(void * arg)680 ipv6rs_expire(void *arg)
681 {
682 	struct interface *ifp;
683 	struct ra *rap, *ran, *ral;
684 	struct ra_opt *rao, *raol, *raon;
685 	struct timeval now, lt, expire, next;
686 	int expired;
687 	uint32_t expire_secs;
688 
689 	ifp = arg;
690 	get_monotonic(&now);
691 	expired = 0;
692 	expire_secs = ~0U;
693 	timerclear(&next);
694 
695 	for (rap = ifp->ras, ral = NULL;
696 	    rap && (ran = rap->next, 1);
697 	    ral = rap, rap = ran)
698 	{
699 		lt.tv_sec = rap->lifetime;
700 		lt.tv_usec = 0;
701 		timeradd(&rap->received, &lt, &expire);
702 		if (timercmp(&now, &expire, >)) {
703 			syslog(LOG_INFO, "%s: %s: expired Router Advertisement",
704 			    ifp->name, rap->sfrom);
705 			rap->expired = expired = 1;
706 			if (ral)
707 				ral->next = ran;
708 			else
709 				ifp->ras = ran;
710 			ipv6rs_free_opts(rap);
711 			free(rap);
712 			continue;
713 		}
714 		timersub(&expire, &now, &lt);
715 		if (!timerisset(&next) || timercmp(&next, &lt, >))
716 			next = lt;
717 
718 		for (rao = rap->options, raol = NULL;
719 		    rao && (raon = rao->next);
720 		    raol = rao, rao = raon)
721 		{
722 			if (!timerisset(&rao->expire))
723 				continue;
724 			if (timercmp(&now, &rao->expire, >)) {
725 				syslog(LOG_INFO,
726 				    "%s: %s: expired option %d",
727 				    ifp->name, rap->sfrom, rao->type);
728 				rap->expired = expired = 1;
729 				if (raol)
730 					raol = raon;
731 				else
732 					rap->options = raon;
733 				continue;
734 			}
735 			timersub(&rao->expire, &now, &lt);
736 			if (!timerisset(&next) || timercmp(&next, &lt, >))
737 				next = lt;
738 		}
739 	}
740 
741 	if (timerisset(&next))
742 		add_timeout_tv(&next, ipv6rs_expire, ifp);
743 	if (expired)
744 		run_script_reason(ifp, "ROUTERADVERT");
745 }
746 
747 int
ipv6rs_start(struct interface * ifp)748 ipv6rs_start(struct interface *ifp)
749 {
750 
751 	delete_timeout(NULL, ifp);
752 
753 	/* Always make a new probe as the underlying hardware
754 	 * address could have changed. */
755 	ipv6rs_makeprobe(ifp);
756 	if (ifp->rs == NULL)
757 		return -1;
758 
759 	ifp->rsprobes = 0;
760 	ipv6rs_sendprobe(ifp);
761 	return 0;
762 }
763