xref: /openbsd-src/usr.sbin/rad/engine.c (revision fcde59b201a29a2b4570b00b71e7aa25d61cb5c1)
1 /*	$OpenBSD: engine.c,v 1.16 2020/05/20 10:37:02 claudio Exp $	*/
2 
3 /*
4  * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
5  * Copyright (c) 2004, 2005 Claudio Jeker <claudio@openbsd.org>
6  * Copyright (c) 2004 Esben Norby <norby@openbsd.org>
7  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
16  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
19  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20  */
21 
22 #include <sys/types.h>
23 #include <sys/queue.h>
24 #include <sys/socket.h>
25 #include <sys/syslog.h>
26 #include <sys/uio.h>
27 
28 #include <netinet/in.h>
29 #include <net/if.h>
30 #include <arpa/inet.h>
31 #include <netinet/icmp6.h>
32 
33 #include <errno.h>
34 #include <event.h>
35 #include <imsg.h>
36 #include <signal.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <pwd.h>
40 #include <unistd.h>
41 
42 #include "log.h"
43 #include "rad.h"
44 #include "engine.h"
45 
46 struct engine_iface {
47 	TAILQ_ENTRY(engine_iface)	entry;
48 	struct event			timer;
49 	uint32_t			if_index;
50 };
51 
52 TAILQ_HEAD(, engine_iface)	engine_interfaces;
53 
54 
55 __dead void		 engine_shutdown(void);
56 void			 engine_sig_handler(int sig, short, void *);
57 void			 engine_dispatch_frontend(int, short, void *);
58 void			 engine_dispatch_main(int, short, void *);
59 void			 parse_ra_rs(struct imsg_ra_rs *);
60 void			 parse_ra(struct imsg_ra_rs *);
61 void			 parse_rs(struct imsg_ra_rs *);
62 void			 update_iface(uint32_t);
63 void			 remove_iface(uint32_t);
64 struct engine_iface	*find_engine_iface_by_id(uint32_t);
65 void			 iface_timeout(int, short, void *);
66 
67 struct rad_conf	*engine_conf;
68 struct imsgev		*iev_frontend;
69 struct imsgev		*iev_main;
70 struct sockaddr_in6	 all_nodes;
71 
72 void
73 engine_sig_handler(int sig, short event, void *arg)
74 {
75 	/*
76 	 * Normal signal handler rules don't apply because libevent
77 	 * decouples for us.
78 	 */
79 
80 	switch (sig) {
81 	case SIGINT:
82 	case SIGTERM:
83 		engine_shutdown();
84 	default:
85 		fatalx("unexpected signal");
86 	}
87 }
88 
89 void
90 engine(int debug, int verbose)
91 {
92 	struct event		 ev_sigint, ev_sigterm;
93 	struct passwd		*pw;
94 
95 	engine_conf = config_new_empty();
96 
97 	log_init(debug, LOG_DAEMON);
98 	log_setverbose(verbose);
99 
100 	if ((pw = getpwnam(RAD_USER)) == NULL)
101 		fatal("getpwnam");
102 
103 	if (chroot(pw->pw_dir) == -1)
104 		fatal("chroot");
105 	if (chdir("/") == -1)
106 		fatal("chdir(\"/\")");
107 
108 	rad_process = PROC_ENGINE;
109 	setproctitle("%s", log_procnames[rad_process]);
110 	log_procinit(log_procnames[rad_process]);
111 
112 	if (setgroups(1, &pw->pw_gid) ||
113 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
114 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
115 		fatal("can't drop privileges");
116 
117 	if (pledge("stdio recvfd", NULL) == -1)
118 		fatal("pledge");
119 
120 	event_init();
121 
122 	/* Setup signal handler(s). */
123 	signal_set(&ev_sigint, SIGINT, engine_sig_handler, NULL);
124 	signal_set(&ev_sigterm, SIGTERM, engine_sig_handler, NULL);
125 	signal_add(&ev_sigint, NULL);
126 	signal_add(&ev_sigterm, NULL);
127 	signal(SIGPIPE, SIG_IGN);
128 	signal(SIGHUP, SIG_IGN);
129 
130 	/* Setup pipe and event handler to the main process. */
131 	if ((iev_main = malloc(sizeof(struct imsgev))) == NULL)
132 		fatal(NULL);
133 
134 	imsg_init(&iev_main->ibuf, 3);
135 	iev_main->handler = engine_dispatch_main;
136 
137 	/* Setup event handlers. */
138 	iev_main->events = EV_READ;
139 	event_set(&iev_main->ev, iev_main->ibuf.fd, iev_main->events,
140 	    iev_main->handler, iev_main);
141 	event_add(&iev_main->ev, NULL);
142 
143 	all_nodes.sin6_len = sizeof(all_nodes);
144 	all_nodes.sin6_family = AF_INET6;
145 	if (inet_pton(AF_INET6, "ff02::1", &all_nodes.sin6_addr) != 1)
146 		fatal("inet_pton");
147 
148 	TAILQ_INIT(&engine_interfaces);
149 
150 	event_dispatch();
151 
152 	engine_shutdown();
153 }
154 
155 __dead void
156 engine_shutdown(void)
157 {
158 	/* Close pipes. */
159 	msgbuf_clear(&iev_frontend->ibuf.w);
160 	close(iev_frontend->ibuf.fd);
161 	msgbuf_clear(&iev_main->ibuf.w);
162 	close(iev_main->ibuf.fd);
163 
164 	config_clear(engine_conf);
165 
166 	free(iev_frontend);
167 	free(iev_main);
168 
169 	log_info("engine exiting");
170 	exit(0);
171 }
172 
173 int
174 engine_imsg_compose_frontend(int type, pid_t pid, void *data, uint16_t datalen)
175 {
176 	return (imsg_compose_event(iev_frontend, type, 0, pid, -1,
177 	    data, datalen));
178 }
179 
180 void
181 engine_dispatch_frontend(int fd, short event, void *bula)
182 {
183 	struct imsgev		*iev = bula;
184 	struct imsgbuf		*ibuf;
185 	struct imsg		 imsg;
186 	struct imsg_ra_rs	 ra_rs;
187 	ssize_t			 n;
188 	uint32_t		 if_index;
189 	int			 shut = 0, verbose;
190 
191 	ibuf = &iev->ibuf;
192 
193 	if (event & EV_READ) {
194 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
195 			fatal("imsg_read error");
196 		if (n == 0)	/* Connection closed. */
197 			shut = 1;
198 	}
199 	if (event & EV_WRITE) {
200 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
201 			fatal("msgbuf_write");
202 		if (n == 0)	/* Connection closed. */
203 			shut = 1;
204 	}
205 
206 	for (;;) {
207 		if ((n = imsg_get(ibuf, &imsg)) == -1)
208 			fatal("%s: imsg_get error", __func__);
209 		if (n == 0)	/* No more messages. */
210 			break;
211 
212 		switch (imsg.hdr.type) {
213 		case IMSG_RA_RS:
214 			if (IMSG_DATA_SIZE(imsg) != sizeof(ra_rs))
215 				fatalx("%s: IMSG_RA_RS wrong length: %lu",
216 				    __func__, IMSG_DATA_SIZE(imsg));
217 			memcpy(&ra_rs, imsg.data, sizeof(ra_rs));
218 			parse_ra_rs(&ra_rs);
219 			break;
220 		case IMSG_UPDATE_IF:
221 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
222 				fatalx("%s: IMSG_UPDATE_IF wrong length: %lu",
223 				    __func__, IMSG_DATA_SIZE(imsg));
224 			memcpy(&if_index, imsg.data, sizeof(if_index));
225 			update_iface(if_index);
226 			break;
227 		case IMSG_REMOVE_IF:
228 			if (IMSG_DATA_SIZE(imsg) != sizeof(if_index))
229 				fatalx("%s: IMSG_REMOVE_IF wrong length: %lu",
230 				    __func__, IMSG_DATA_SIZE(imsg));
231 			memcpy(&if_index, imsg.data, sizeof(if_index));
232 			remove_iface(if_index);
233 			break;
234 		case IMSG_CTL_LOG_VERBOSE:
235 			if (IMSG_DATA_SIZE(imsg) != sizeof(verbose))
236 				fatalx("%s: IMSG_CTL_LOG_VERBOSE wrong length: "
237 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
238 			memcpy(&verbose, imsg.data, sizeof(verbose));
239 			log_setverbose(verbose);
240 			break;
241 		default:
242 			log_debug("%s: unexpected imsg %d", __func__,
243 			    imsg.hdr.type);
244 			break;
245 		}
246 		imsg_free(&imsg);
247 	}
248 	if (!shut)
249 		imsg_event_add(iev);
250 	else {
251 		/* This pipe is dead. Remove its event handler. */
252 		event_del(&iev->ev);
253 		event_loopexit(NULL);
254 	}
255 }
256 
257 void
258 engine_dispatch_main(int fd, short event, void *bula)
259 {
260 	static struct rad_conf		*nconf;
261 	static struct ra_iface_conf	*ra_iface_conf;
262 	static struct ra_options_conf	*ra_options;
263 	struct imsg			 imsg;
264 	struct imsgev			*iev = bula;
265 	struct imsgbuf			*ibuf;
266 	struct ra_prefix_conf		*ra_prefix_conf;
267 	struct ra_rdnss_conf		*ra_rdnss_conf;
268 	struct ra_dnssl_conf		*ra_dnssl_conf;
269 	ssize_t				 n;
270 	int				 shut = 0;
271 
272 	ibuf = &iev->ibuf;
273 
274 	if (event & EV_READ) {
275 		if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
276 			fatal("imsg_read error");
277 		if (n == 0)	/* Connection closed. */
278 			shut = 1;
279 	}
280 	if (event & EV_WRITE) {
281 		if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
282 			fatal("msgbuf_write");
283 		if (n == 0)	/* Connection closed. */
284 			shut = 1;
285 	}
286 
287 	for (;;) {
288 		if ((n = imsg_get(ibuf, &imsg)) == -1)
289 			fatal("%s: imsg_get error", __func__);
290 		if (n == 0)	/* No more messages. */
291 			break;
292 
293 		switch (imsg.hdr.type) {
294 		case IMSG_SOCKET_IPC:
295 			/*
296 			 * Setup pipe and event handler to the frontend
297 			 * process.
298 			 */
299 			if (iev_frontend)
300 				fatalx("%s: received unexpected imsg fd "
301 				    "to engine", __func__);
302 
303 			if ((fd = imsg.fd) == -1)
304 				fatalx("%s: expected to receive imsg fd to "
305 				   "engine but didn't receive any", __func__);
306 
307 			iev_frontend = malloc(sizeof(struct imsgev));
308 			if (iev_frontend == NULL)
309 				fatal(NULL);
310 
311 			imsg_init(&iev_frontend->ibuf, fd);
312 			iev_frontend->handler = engine_dispatch_frontend;
313 			iev_frontend->events = EV_READ;
314 
315 			event_set(&iev_frontend->ev, iev_frontend->ibuf.fd,
316 			iev_frontend->events, iev_frontend->handler,
317 			    iev_frontend);
318 			event_add(&iev_frontend->ev, NULL);
319 			break;
320 		case IMSG_RECONF_CONF:
321 			if (nconf != NULL)
322 				fatalx("%s: IMSG_RECONF_CONF already in "
323 				    "progress", __func__);
324 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct rad_conf))
325 				fatalx("%s: IMSG_RECONF_CONF wrong length: %lu",
326 				    __func__, IMSG_DATA_SIZE(imsg));
327 			if ((nconf = malloc(sizeof(struct rad_conf))) == NULL)
328 				fatal(NULL);
329 			memcpy(nconf, imsg.data, sizeof(struct rad_conf));
330 			SIMPLEQ_INIT(&nconf->ra_iface_list);
331 			SIMPLEQ_INIT(&nconf->ra_options.ra_rdnss_list);
332 			SIMPLEQ_INIT(&nconf->ra_options.ra_dnssl_list);
333 			ra_options = &nconf->ra_options;
334 			break;
335 		case IMSG_RECONF_RA_IFACE:
336 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
337 			    ra_iface_conf))
338 				fatalx("%s: IMSG_RECONF_RA_IFACE wrong length: "
339 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
340 			if ((ra_iface_conf = malloc(sizeof(struct
341 			    ra_iface_conf))) == NULL)
342 				fatal(NULL);
343 			memcpy(ra_iface_conf, imsg.data,
344 			    sizeof(struct ra_iface_conf));
345 			ra_iface_conf->autoprefix = NULL;
346 			SIMPLEQ_INIT(&ra_iface_conf->ra_prefix_list);
347 			SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_rdnss_list);
348 			SIMPLEQ_INIT(&ra_iface_conf->ra_options.ra_dnssl_list);
349 			SIMPLEQ_INSERT_TAIL(&nconf->ra_iface_list,
350 			    ra_iface_conf, entry);
351 			ra_options = &ra_iface_conf->ra_options;
352 			break;
353 		case IMSG_RECONF_RA_AUTOPREFIX:
354 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
355 			    ra_prefix_conf))
356 				fatalx("%s: IMSG_RECONF_RA_AUTOPREFIX wrong "
357 				    "length: %lu", __func__,
358 				    IMSG_DATA_SIZE(imsg));
359 			if ((ra_iface_conf->autoprefix = malloc(sizeof(struct
360 			    ra_prefix_conf))) == NULL)
361 				fatal(NULL);
362 			memcpy(ra_iface_conf->autoprefix, imsg.data,
363 			    sizeof(struct ra_prefix_conf));
364 			break;
365 		case IMSG_RECONF_RA_PREFIX:
366 			if (IMSG_DATA_SIZE(imsg) != sizeof(struct
367 			    ra_prefix_conf))
368 				fatalx("%s: IMSG_RECONF_RA_PREFIX wrong "
369 				    "length: %lu", __func__,
370 				    IMSG_DATA_SIZE(imsg));
371 			if ((ra_prefix_conf = malloc(sizeof(struct
372 			    ra_prefix_conf))) == NULL)
373 				fatal(NULL);
374 			memcpy(ra_prefix_conf, imsg.data, sizeof(struct
375 			    ra_prefix_conf));
376 			SIMPLEQ_INSERT_TAIL(&ra_iface_conf->ra_prefix_list,
377 			    ra_prefix_conf, entry);
378 			break;
379 		case IMSG_RECONF_RA_RDNSS:
380 			if(IMSG_DATA_SIZE(imsg) != sizeof(struct
381 			    ra_rdnss_conf))
382 				fatalx("%s: IMSG_RECONF_RA_RDNSS wrong length: "
383 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
384 			if ((ra_rdnss_conf = malloc(sizeof(struct
385 			    ra_rdnss_conf))) == NULL)
386 				fatal(NULL);
387 			memcpy(ra_rdnss_conf, imsg.data, sizeof(struct
388 			    ra_rdnss_conf));
389 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_rdnss_list,
390 			    ra_rdnss_conf, entry);
391 			break;
392 		case IMSG_RECONF_RA_DNSSL:
393 			if(IMSG_DATA_SIZE(imsg) != sizeof(struct
394 			    ra_dnssl_conf))
395 				fatalx("%s: IMSG_RECONF_RA_DNSSL wrong length: "
396 				    "%lu", __func__, IMSG_DATA_SIZE(imsg));
397 			if ((ra_dnssl_conf = malloc(sizeof(struct
398 			    ra_dnssl_conf))) == NULL)
399 				fatal(NULL);
400 			memcpy(ra_dnssl_conf, imsg.data, sizeof(struct
401 			    ra_dnssl_conf));
402 			SIMPLEQ_INSERT_TAIL(&ra_options->ra_dnssl_list,
403 			    ra_dnssl_conf, entry);
404 			break;
405 		case IMSG_RECONF_END:
406 			if (nconf == NULL)
407 				fatalx("%s: IMSG_RECONF_END without "
408 				    "IMSG_RECONF_CONF", __func__);
409 			merge_config(engine_conf, nconf);
410 			nconf = NULL;
411 			break;
412 		default:
413 			log_debug("%s: unexpected imsg %d", __func__,
414 			    imsg.hdr.type);
415 			break;
416 		}
417 		imsg_free(&imsg);
418 	}
419 	if (!shut)
420 		imsg_event_add(iev);
421 	else {
422 		/* This pipe is dead. Remove its event handler. */
423 		event_del(&iev->ev);
424 		event_loopexit(NULL);
425 	}
426 }
427 
428 
429 void
430 parse_ra_rs(struct imsg_ra_rs *ra_rs)
431 {
432 	char			 ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
433 	struct icmp6_hdr	*hdr;
434 
435 	hdr = (struct icmp6_hdr *) ra_rs->packet;
436 
437 	switch (hdr->icmp6_type) {
438 	case ND_ROUTER_ADVERT:
439 		parse_ra(ra_rs);
440 		break;
441 	case ND_ROUTER_SOLICIT:
442 		parse_rs(ra_rs);
443 		break;
444 	default:
445 		log_warnx("unexpected icmp6_type: %d from %s on %s",
446 		    hdr->icmp6_type, inet_ntop(AF_INET6, &ra_rs->from.sin6_addr,
447 		    ntopbuf, INET6_ADDRSTRLEN), if_indextoname(ra_rs->if_index,
448 		    ifnamebuf));
449 		break;
450 	}
451 }
452 
453 void
454 parse_ra(struct imsg_ra_rs *ra)
455 {
456 	char			 ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
457 	log_debug("got RA from %s on %s",
458 	    inet_ntop(AF_INET6, &ra->from.sin6_addr, ntopbuf,
459 	    INET6_ADDRSTRLEN), if_indextoname(ra->if_index,
460 	    ifnamebuf));
461 	/* XXX not yet */
462 }
463 
464 void
465 parse_rs(struct imsg_ra_rs *rs)
466 {
467 	struct nd_router_solicit	*nd_rs;
468 	struct imsg_send_ra		 send_ra;
469 	ssize_t				 len;
470 	const char			*hbuf;
471 	char				 ifnamebuf[IFNAMSIZ];
472 	uint8_t				*p;
473 
474 	hbuf = sin6_to_str(&rs->from);
475 
476 	send_ra.if_index = rs->if_index;
477 	memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
478 
479 	log_debug("got RS from %s on %s", hbuf, if_indextoname(rs->if_index,
480 	    ifnamebuf));
481 
482 	len = rs->len;
483 
484 	if (!IN6_IS_ADDR_LINKLOCAL(&rs->from.sin6_addr)) {
485 		log_warnx("RA from non link local address %s on %s", hbuf,
486 		    if_indextoname(rs->if_index, ifnamebuf));
487 		return;
488 	}
489 
490 	if ((size_t)len < sizeof(struct nd_router_solicit)) {
491 		log_warnx("received too short message (%ld) from %s", len,
492 		    hbuf);
493 		return;
494 	}
495 
496 	p = rs->packet;
497 	nd_rs = (struct nd_router_solicit *)p;
498 	len -= sizeof(struct nd_router_solicit);
499 	p += sizeof(struct nd_router_solicit);
500 
501 	if (nd_rs->nd_rs_code != 0) {
502 		log_warnx("invalid ICMPv6 code (%d) from %s", nd_rs->nd_rs_code,
503 		    hbuf);
504 		return;
505 	}
506 	while ((size_t)len >= sizeof(struct nd_opt_hdr)) {
507 		struct nd_opt_hdr *nd_opt_hdr = (struct nd_opt_hdr *)p;
508 
509 		len -= sizeof(struct nd_opt_hdr);
510 		p += sizeof(struct nd_opt_hdr);
511 
512 		if (nd_opt_hdr->nd_opt_len * 8 - 2 > len) {
513 			log_warnx("invalid option len: %u > %ld",
514 			    nd_opt_hdr->nd_opt_len, len);
515 			return;
516 		}
517 		switch (nd_opt_hdr->nd_opt_type) {
518 		case ND_OPT_SOURCE_LINKADDR:
519 			log_debug("got RS with source linkaddr option");
520 			memcpy(&send_ra.to, &rs->from, sizeof(send_ra.to));
521 			break;
522 		default:
523 			log_debug("\t\tUNKNOWN: %d", nd_opt_hdr->nd_opt_type);
524 			break;
525 		}
526 		len -= nd_opt_hdr->nd_opt_len * 8 - 2;
527 		p += nd_opt_hdr->nd_opt_len * 8 - 2;
528 	}
529 	engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
530 	    sizeof(send_ra));
531 }
532 
533 struct engine_iface*
534 find_engine_iface_by_id(uint32_t if_index)
535 {
536 	struct engine_iface	*engine_iface;
537 
538 	TAILQ_FOREACH(engine_iface, &engine_interfaces, entry) {
539 		if (engine_iface->if_index == if_index)
540 			return engine_iface;
541 	}
542 	return (NULL);
543 }
544 
545 void
546 update_iface(uint32_t if_index)
547 {
548 	struct engine_iface	*engine_iface;
549 	struct timeval		 tv;
550 
551 	if ((engine_iface = find_engine_iface_by_id(if_index)) == NULL) {
552 		engine_iface = calloc(1, sizeof(*engine_iface));
553 		engine_iface->if_index = if_index;
554 		evtimer_set(&engine_iface->timer, iface_timeout, engine_iface);
555 		TAILQ_INSERT_TAIL(&engine_interfaces, engine_iface, entry);
556 	}
557 
558 	tv.tv_sec = 0;
559 	tv.tv_usec = arc4random_uniform(1000000);
560 	evtimer_add(&engine_iface->timer, &tv);
561 }
562 
563 void
564 remove_iface(uint32_t if_index)
565 {
566 	struct engine_iface	*engine_iface;
567 	struct imsg_send_ra	 send_ra;
568 	char			 if_name[IF_NAMESIZE];
569 
570 	if ((engine_iface = find_engine_iface_by_id(if_index)) == NULL) {
571 		/* we don't know this interface, frontend can delete it */
572 		engine_imsg_compose_frontend(IMSG_REMOVE_IF, 0,
573 		    &if_index, sizeof(if_index));
574 		return;
575 	}
576 
577 	send_ra.if_index = engine_iface->if_index;
578 	memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
579 
580 	TAILQ_REMOVE(&engine_interfaces, engine_iface, entry);
581 	evtimer_del(&engine_iface->timer);
582 
583 	if (if_indextoname(if_index, if_name) != NULL)
584 		engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
585 		    sizeof(send_ra));
586 	engine_imsg_compose_frontend(IMSG_REMOVE_IF, 0,
587 	    &engine_iface->if_index, sizeof(engine_iface->if_index));
588 	free(engine_iface);
589 }
590 
591 void
592 iface_timeout(int fd, short events, void *arg)
593 {
594 	struct engine_iface	*engine_iface = (struct engine_iface *)arg;
595 	struct imsg_send_ra	 send_ra;
596 	struct timeval		 tv;
597 
598 	tv.tv_sec = MIN_RTR_ADV_INTERVAL +
599 	    arc4random_uniform(MAX_RTR_ADV_INTERVAL - MIN_RTR_ADV_INTERVAL);
600 	tv.tv_usec = arc4random_uniform(1000000);
601 
602 	log_debug("%s new timeout in %lld", __func__, tv.tv_sec);
603 
604 	evtimer_add(&engine_iface->timer, &tv);
605 
606 	send_ra.if_index = engine_iface->if_index;
607 	memcpy(&send_ra.to, &all_nodes, sizeof(send_ra.to));
608 	engine_imsg_compose_frontend(IMSG_SEND_RA, 0, &send_ra,
609 	    sizeof(send_ra));
610 }
611