xref: /openbsd/usr.sbin/dvmrpd/interface.c (revision df69c215)
1 /*	$OpenBSD: interface.c,v 1.12 2019/06/28 13:32:47 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 #include <sys/ioctl.h>
22 #include <sys/time.h>
23 #include <sys/socket.h>
24 
25 #include <netinet/in.h>
26 #include <netinet/ip_mroute.h>
27 #include <arpa/inet.h>
28 #include <net/if.h>
29 #include <net/if_types.h>
30 
31 #include <ctype.h>
32 #include <err.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <event.h>
38 
39 #include "igmp.h"
40 #include "dvmrpd.h"
41 #include "dvmrp.h"
42 #include "log.h"
43 #include "dvmrpe.h"
44 
45 extern struct dvmrpd_conf	*conf;
46 
47 void	 if_probe_timer(int, short, void *);
48 int	 if_start_probe_timer(struct iface *);
49 int	 if_stop_probe_timer(struct iface *);
50 void	 if_query_timer(int, short, void *);
51 int	 if_start_query_timer(struct iface *);
52 int	 if_stop_query_timer(struct iface *);
53 void	 if_querier_present_timer(int, short, void *);
54 int	 if_start_querier_present_timer(struct iface *);
55 int	 if_stop_querier_present_timer(struct iface *);
56 int	 if_reset_querier_present_timer(struct iface *);
57 int	 if_act_start(struct iface *);
58 int	 if_act_query_seen(struct iface *);
59 int	 if_act_reset(struct iface *);
60 
61 struct {
62 	int			state;
63 	enum iface_event	event;
64 	enum iface_action	action;
65 	int			new_state;
66 } iface_fsm[] = {
67     /* current state	event that happened	action to take	resulting state */
68     {IF_STA_DOWN,	IF_EVT_UP,		IF_ACT_STRT,	0},
69     {IF_STA_ACTIVE,	IF_EVT_QRECVD,		IF_ACT_QPRSNT,	0},
70     {IF_STA_NONQUERIER, IF_EVT_QPRSNTTMOUT,	IF_ACT_STRT,	0},
71     {IF_STA_ANY,	IF_EVT_DOWN,		IF_ACT_RST,	IF_STA_DOWN},
72     {-1,		IF_EVT_NOTHING,		IF_ACT_NOTHING,	0},
73 };
74 
75 const char * const if_action_names[] = {
76 	"NOTHING",
77 	"START",
78 	"QPRSNT",
79 	"RESET"
80 };
81 
82 static const char * const if_event_names[] = {
83 	"NOTHING",
84 	"UP",
85 	"QTMOUT",
86 	"QRECVD",
87 	"QPRSNTTMOUT",
88 	"DOWN"
89 };
90 
91 int
if_fsm(struct iface * iface,enum iface_event event)92 if_fsm(struct iface *iface, enum iface_event event)
93 {
94 	int	old_state;
95 	int	new_state = 0;
96 	int	i, ret = 0;
97 
98 	old_state = iface->state;
99 
100 	for (i = 0; iface_fsm[i].state != -1; i++)
101 		if ((iface_fsm[i].state & old_state) &&
102 		    (iface_fsm[i].event == event)) {
103 			new_state = iface_fsm[i].new_state;
104 			break;
105 		}
106 
107 	if (iface_fsm[i].state == -1) {
108 		/* XXX event outside of the defined fsm, ignore it. */
109 		log_debug("fsm_if: interface %s, "
110 		    "event '%s' not expected in state '%s'", iface->name,
111 		    if_event_name(event), if_state_name(old_state));
112 		return (0);
113 	}
114 
115 	switch (iface_fsm[i].action) {
116 	case IF_ACT_STRT:
117 		ret = if_act_start(iface);
118 		break;
119 	case IF_ACT_QPRSNT:
120 		ret = if_act_query_seen(iface);
121 		break;
122 	case IF_ACT_RST:
123 		ret = if_act_reset(iface);
124 		break;
125 	case IF_ACT_NOTHING:
126 		/* do nothing */
127 		break;
128 	}
129 
130 	if (ret) {
131 		log_debug("fsm_if: error changing state for interface %s, "
132 		    "event '%s', state '%s'", iface->name, if_event_name(event),
133 		    if_state_name(old_state));
134 		return (-1);
135 	}
136 
137 	if (new_state != 0)
138 		iface->state = new_state;
139 
140 	log_debug("fsm_if: event '%s' resulted in action '%s' and changing "
141 	    "state for interface %s from '%s' to '%s'",
142 	    if_event_name(event), if_action_name(iface_fsm[i].action),
143 	    iface->name, if_state_name(old_state), if_state_name(iface->state));
144 
145 	return (ret);
146 }
147 
148 struct iface *
if_find_index(u_short ifindex)149 if_find_index(u_short ifindex)
150 {
151 	struct iface	*iface;
152 
153 	LIST_FOREACH(iface, &conf->iface_list, entry) {
154 		if (iface->ifindex == ifindex)
155 			return (iface);
156 	}
157 
158 	return (NULL);
159 }
160 
161 struct iface *
if_new(struct kif * kif)162 if_new(struct kif *kif)
163 {
164 	struct sockaddr_in	*sain;
165 	struct iface		*iface;
166 	struct ifreq		*ifr;
167 	int			 s;
168 
169 	if ((iface = calloc(1, sizeof(*iface))) == NULL)
170 		err(1, "if_new: calloc");
171 
172 	iface->state = IF_STA_DOWN;
173 	iface->passive = 1;
174 
175 	LIST_INIT(&iface->nbr_list);
176 	TAILQ_INIT(&iface->group_list);
177 	TAILQ_INIT(&iface->rde_group_list);
178 	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
179 
180 	if ((ifr = calloc(1, sizeof(*ifr))) == NULL)
181 		err(1, "if_new: calloc");
182 
183 	/* set up ifreq */
184 	strlcpy(ifr->ifr_name, kif->ifname, sizeof(ifr->ifr_name));
185 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
186 		err(1, "if_new: socket");
187 
188 	/* get type */
189 	if ((kif->flags & IFF_POINTOPOINT))
190 		iface->type = IF_TYPE_POINTOPOINT;
191 	if ((kif->flags & IFF_BROADCAST) &&
192 	    (kif->flags & IFF_MULTICAST))
193 		iface->type = IF_TYPE_BROADCAST;
194 
195 	/* get mtu, index and flags */
196 	iface->mtu = kif->mtu;
197 	iface->ifindex = kif->ifindex;
198 	iface->flags = kif->flags;
199 	iface->linkstate = kif->link_state;
200 	iface->if_type = kif->if_type;
201 	iface->baudrate = kif->baudrate;
202 
203 	/* get address */
204 	if (ioctl(s, SIOCGIFADDR, (caddr_t)ifr) == -1)
205 		err(1, "if_new: cannot get address");
206 	sain = (struct sockaddr_in *) &ifr->ifr_addr;
207 	iface->addr = sain->sin_addr;
208 
209 	/* get mask */
210 	if (ioctl(s, SIOCGIFNETMASK, (caddr_t)ifr) == -1)
211 		err(1, "if_new: cannot get mask");
212 	sain = (struct sockaddr_in *) &ifr->ifr_addr;
213 	iface->mask = sain->sin_addr;
214 
215 	/* get p2p dst address */
216 	if (iface->type == IF_TYPE_POINTOPOINT) {
217 		if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)ifr) == -1)
218 			err(1, "if_new: cannot get dst addr");
219 		sain = (struct sockaddr_in *) &ifr->ifr_addr;
220 		iface->dst = sain->sin_addr;
221 	}
222 
223 	free(ifr);
224 	close(s);
225 
226 	return (iface);
227 }
228 
229 void
if_init(struct dvmrpd_conf * xconf,struct iface * iface)230 if_init(struct dvmrpd_conf *xconf, struct iface *iface)
231 {
232 	/* set event handlers for interface */
233 	evtimer_set(&iface->probe_timer, if_probe_timer, iface);
234 	evtimer_set(&iface->query_timer, if_query_timer, iface);
235 	evtimer_set(&iface->querier_present_timer, if_querier_present_timer,
236 	    iface);
237 
238 	TAILQ_INIT(&iface->rr_list);
239 
240 	iface->fd = xconf->dvmrp_socket;
241 	iface->gen_id = xconf->gen_id;
242 }
243 
244 int
if_del(struct iface * iface)245 if_del(struct iface *iface)
246 {
247 	struct nbr	*nbr = NULL;
248 
249 	/* clear lists etc */
250 	while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) {
251 		LIST_REMOVE(nbr, entry);
252 		nbr_del(nbr);
253 	}
254 	group_list_clr(iface);
255 
256 	return (-1);
257 }
258 
259 int
if_nbr_list_empty(struct iface * iface)260 if_nbr_list_empty(struct iface *iface)
261 {
262 	return (LIST_EMPTY(&iface->nbr_list));
263 }
264 
265 /* timers */
266 void
if_probe_timer(int fd,short event,void * arg)267 if_probe_timer(int fd, short event, void *arg)
268 {
269 	struct iface *iface = arg;
270 	struct timeval tv;
271 
272 	send_probe(iface);
273 
274 	/* reschedule probe_timer */
275 	if (!iface->passive) {
276 		timerclear(&tv);
277 		tv.tv_sec = iface->probe_interval;
278 		evtimer_add(&iface->probe_timer, &tv);
279 	}
280 }
281 
282 int
if_start_probe_timer(struct iface * iface)283 if_start_probe_timer(struct iface *iface)
284 {
285 	struct timeval tv;
286 
287 	timerclear(&tv);
288 	return (evtimer_add(&iface->probe_timer, &tv));
289 }
290 
291 int
if_stop_probe_timer(struct iface * iface)292 if_stop_probe_timer(struct iface *iface)
293 {
294 	return (evtimer_del(&iface->probe_timer));
295 }
296 
297 void
if_query_timer(int fd,short event,void * arg)298 if_query_timer(int fd, short event, void *arg)
299 {
300 	struct iface *iface = arg;
301 	struct timeval tv;
302 
303 	/* send a general query */
304 	send_igmp_query(iface, NULL);
305 
306 	/* reschedule query_timer */
307 	if (!iface->passive) {
308 		timerclear(&tv);
309 		if (iface->startup_query_counter != 0) {
310 			tv.tv_sec = iface->startup_query_interval;
311 			iface->startup_query_counter--;
312 		} else
313 			tv.tv_sec = iface->query_interval;
314 
315 		evtimer_add(&iface->query_timer, &tv);
316 	}
317 }
318 
319 int
if_start_query_timer(struct iface * iface)320 if_start_query_timer(struct iface *iface)
321 {
322 	struct timeval tv;
323 
324 	timerclear(&tv);
325 	return (evtimer_add(&iface->query_timer, &tv));
326 }
327 
328 int
if_stop_query_timer(struct iface * iface)329 if_stop_query_timer(struct iface *iface)
330 {
331 	return (evtimer_del(&iface->query_timer));
332 }
333 
334 void
if_querier_present_timer(int fd,short event,void * arg)335 if_querier_present_timer(int fd, short event, void *arg)
336 {
337 	struct iface *iface = arg;
338 
339 	if_fsm(iface, IF_EVT_QPRSNTTMOUT);
340 }
341 
342 int
if_start_querier_present_timer(struct iface * iface)343 if_start_querier_present_timer(struct iface *iface)
344 {
345 	struct timeval tv;
346 
347 	/* Other Querier Present Interval */
348 	timerclear(&tv);
349 	tv.tv_sec = iface->robustness * iface->query_interval +
350 	    (iface->query_resp_interval / 2);
351 
352 	return (evtimer_add(&iface->querier_present_timer, &tv));
353 }
354 
355 int
if_stop_querier_present_timer(struct iface * iface)356 if_stop_querier_present_timer(struct iface *iface)
357 {
358 	return (evtimer_del(&iface->querier_present_timer));
359 }
360 
361 int
if_reset_querier_present_timer(struct iface * iface)362 if_reset_querier_present_timer(struct iface *iface)
363 {
364 	struct timeval	tv;
365 
366 	/* Other Querier Present Interval */
367 	timerclear(&tv);
368 	tv.tv_sec = iface->robustness * iface->query_interval +
369 	    (iface->query_resp_interval / 2);
370 
371 	return (evtimer_add(&iface->querier_present_timer, &tv));
372 }
373 
374 /* actions */
375 int
if_act_start(struct iface * iface)376 if_act_start(struct iface *iface)
377 {
378 	struct in_addr	 addr;
379 	struct timeval	 now;
380 
381 	if (iface->passive) {
382 		log_debug("if_act_start: cannot start passive interface %s",
383 		    iface->name);
384 		return (-1);
385 	}
386 
387 	if (!((iface->flags & IFF_UP) && LINK_STATE_IS_UP(iface->linkstate))) {
388 		log_debug("if_act_start: interface %s link down",
389 		    iface->name);
390 		return (0);
391 	}
392 
393 	gettimeofday(&now, NULL);
394 	iface->uptime = now.tv_sec;
395 
396 	switch (iface->type) {
397 	case IF_TYPE_POINTOPOINT:
398 	case IF_TYPE_BROADCAST:
399 		inet_aton(AllSystems, &addr);
400 		if (if_join_group(iface, &addr)) {
401 			log_warnx("if_act_start: error joining group %s, "
402 			    "interface %s", inet_ntoa(addr), iface->name);
403 			return (-1);
404 		}
405 		inet_aton(AllRouters, &addr);
406 		if (if_join_group(iface, &addr)) {
407 			log_warnx("if_act_start: error joining group %s, "
408 			    "interface %s", inet_ntoa(addr), iface->name);
409 			return (-1);
410 		}
411 		inet_aton(AllDVMRPRouters, &addr);
412 		if (if_join_group(iface, &addr)) {
413 			log_warnx("if_act_start: error joining group %s, "
414 			    "interface %s", inet_ntoa(addr), iface->name);
415 			return (-1);
416 		}
417 
418 		iface->state = IF_STA_QUERIER;
419 		if_start_query_timer(iface);
420 		if_start_probe_timer(iface);
421 		iface->startup_query_counter = iface->startup_query_cnt;
422 		break;
423 	default:
424 		fatalx("if_act_start: unknown type");
425 	}
426 
427 	return (0);
428 }
429 
430 int
if_act_query_seen(struct iface * iface)431 if_act_query_seen(struct iface *iface)
432 {
433 	log_debug("if_act_query_seen: interface %s", iface->name);
434 
435 	switch (iface->type) {
436 	case IF_TYPE_POINTOPOINT:
437 	case IF_TYPE_BROADCAST:
438 		iface->state = IF_STA_NONQUERIER;
439 		if_stop_query_timer(iface);
440 		if_reset_querier_present_timer(iface);
441 		break;
442 	default:
443 		fatalx("if_act_querier_seen: unknown type");
444 	}
445 
446 	return (0);
447 }
448 
449 int
if_act_reset(struct iface * iface)450 if_act_reset(struct iface *iface)
451 {
452 	struct in_addr	 addr;
453 	struct nbr	*nbr;
454 
455 	switch (iface->type) {
456 	case IF_TYPE_POINTOPOINT:
457 	case IF_TYPE_BROADCAST:
458 		inet_aton(AllSystems, &addr);
459 		if (if_leave_group(iface, &addr)) {
460 			log_warnx("if_act_reset: error leaving group %s, "
461 			    "interface %s", inet_ntoa(addr), iface->name);
462 			return (-1);
463 		}
464 		inet_aton(AllRouters, &addr);
465 		if (if_leave_group(iface, &addr)) {
466 			log_warnx("if_act_reset: error leaving group %s, "
467 			    "interface %s", inet_ntoa(addr), iface->name);
468 			return (-1);
469 		}
470 		inet_aton(AllDVMRPRouters, &addr);
471 		if (if_leave_group(iface, &addr)) {
472 			log_warnx("if_act_reset: error leaving group %s, "
473 			    "interface %s", inet_ntoa(addr), iface->name);
474 			return (-1);
475 		}
476 
477 		iface->state = IF_STA_DOWN;
478 		iface->gen_id++;
479 		if_stop_query_timer(iface);
480 		if_stop_querier_present_timer(iface);
481 		/* XXX clear nbr list? */
482 		break;
483 	default:
484 		fatalx("if_act_reset: unknown type");
485 	}
486 
487 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
488 		if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) {
489 			log_debug("if_act_reset: error killing neighbor %s",
490 			    inet_ntoa(nbr->id));
491 		}
492 	}
493 
494 	group_list_clr(iface);	/* XXX clear group list? */
495 
496 	return (0);
497 }
498 
499 const char *
if_event_name(int event)500 if_event_name(int event)
501 {
502 	return (if_event_names[event]);
503 }
504 
505 const char *
if_action_name(int action)506 if_action_name(int action)
507 {
508 	return (if_action_names[action]);
509 }
510 
511 /* misc */
512 int
if_set_mcast_ttl(int fd,u_int8_t ttl)513 if_set_mcast_ttl(int fd, u_int8_t ttl)
514 {
515 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
516 	    (char *)&ttl, sizeof(ttl)) == -1) {
517 		log_warn("if_set_mcast_ttl: error setting "
518 		    "IP_MULTICAST_TTL to %d", ttl);
519 		return (-1);
520 	}
521 
522 	return (0);
523 }
524 
525 int
if_set_tos(int fd,int tos)526 if_set_tos(int fd, int tos)
527 {
528 	if (setsockopt(fd, IPPROTO_IP, IP_TOS,
529 	    (int *)&tos, sizeof(tos)) == -1) {
530 		log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
531 		return (-1);
532 	}
533 
534 	return (0);
535 }
536 
537 void
if_set_recvbuf(int fd)538 if_set_recvbuf(int fd)
539 {
540 	int	bsize;
541 
542 	bsize = 65535;
543 	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
544 	    sizeof(bsize)) == -1)
545 		bsize /= 2;
546 }
547 
548 int
if_join_group(struct iface * iface,struct in_addr * addr)549 if_join_group(struct iface *iface, struct in_addr *addr)
550 {
551 	struct ip_mreq	 mreq;
552 
553 	switch (iface->type) {
554 	case IF_TYPE_POINTOPOINT:
555 	case IF_TYPE_BROADCAST:
556 		mreq.imr_multiaddr.s_addr = addr->s_addr;
557 		mreq.imr_interface.s_addr = iface->addr.s_addr;
558 
559 		if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
560 		    (void *)&mreq, sizeof(mreq)) == -1) {
561 			log_debug("if_join_group: error IP_ADD_MEMBERSHIP, "
562 			    "interface %s", iface->name);
563 			return (-1);
564 		}
565 		break;
566 	default:
567 		fatalx("if_join_group: unknown interface type");
568 	}
569 
570 	return (0);
571 }
572 
573 int
if_leave_group(struct iface * iface,struct in_addr * addr)574 if_leave_group(struct iface *iface, struct in_addr *addr)
575 {
576 	struct ip_mreq	 mreq;
577 
578 	switch (iface->type) {
579 	case IF_TYPE_POINTOPOINT:
580 	case IF_TYPE_BROADCAST:
581 		mreq.imr_multiaddr.s_addr = addr->s_addr;
582 		mreq.imr_interface.s_addr = iface->addr.s_addr;
583 
584 		if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
585 		    (void *)&mreq, sizeof(mreq)) == -1) {
586 			log_debug("if_leave_group: error IP_DROP_MEMBERSHIP, "
587 			    "interface %s", iface->name);
588 			return (-1);
589 		}
590 		break;
591 	default:
592 		fatalx("if_leave_group: unknown interface type");
593 	}
594 
595 	return (0);
596 }
597 
598 int
if_set_mcast(struct iface * iface)599 if_set_mcast(struct iface *iface)
600 {
601 	switch (iface->type) {
602 	case IF_TYPE_POINTOPOINT:
603 	case IF_TYPE_BROADCAST:
604 		if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF,
605 		    (char *)&iface->addr.s_addr,
606 		    sizeof(iface->addr.s_addr)) == -1) {
607 			log_debug("if_set_mcast: error setting "
608 			    "IP_MULTICAST_IF, interface %s", iface->name);
609 			return (-1);
610 		}
611 		break;
612 	default:
613 		fatalx("if_set_mcast: unknown interface type");
614 	}
615 
616 	return (0);
617 }
618 
619 int
if_set_mcast_loop(int fd)620 if_set_mcast_loop(int fd)
621 {
622 	u_int8_t	loop = 0;
623 
624 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
625 	    (char *)&loop, sizeof(loop)) == -1) {
626 		log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
627 		return (-1);
628 	}
629 
630 	return (0);
631 }
632 
633 struct ctl_iface *
if_to_ctl(struct iface * iface)634 if_to_ctl(struct iface *iface)
635 {
636 	static struct ctl_iface	 ictl;
637 	struct timeval		 tv, now, res;
638 
639 	memcpy(ictl.name, iface->name, sizeof(ictl.name));
640 	memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr));
641 	memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask));
642 	memcpy(&ictl.querier, &iface->querier, sizeof(ictl.querier));
643 
644 	ictl.ifindex = iface->ifindex;
645 	ictl.state = iface->state;
646 	ictl.mtu = iface->mtu;
647 	ictl.nbr_cnt = iface->nbr_cnt;
648 	ictl.adj_cnt = iface->adj_cnt;
649 
650 	ictl.gen_id = iface->gen_id;
651 	ictl.group_cnt = iface->group_cnt;
652 	ictl.probe_interval = iface->probe_interval;
653 	ictl.query_interval = iface->query_interval;
654 	ictl.query_resp_interval = iface->query_resp_interval;
655 	ictl.recv_query_resp_interval = iface->recv_query_resp_interval;
656 	ictl.group_member_interval = iface->group_member_interval;
657 	ictl.querier_present_interval = iface->querier_present_interval;
658 	ictl.startup_query_interval = iface->startup_query_interval;
659 	ictl.startup_query_cnt = iface->startup_query_cnt;
660 	ictl.last_member_query_interval = iface->last_member_query_interval;
661 	ictl.last_member_query_cnt = iface->last_member_query_cnt;
662 	ictl.last_member_query_time = iface->last_member_query_time;
663 	ictl.v1_querier_present_tmout = iface->v1_querier_present_tmout;
664 	ictl.v1_host_present_interval = iface->v1_host_present_interval;
665 	ictl.dead_interval = iface->dead_interval;
666 
667 	ictl.baudrate = iface->baudrate;
668 	ictl.flags = iface->flags;
669 	ictl.metric = iface->metric;
670 	ictl.type = iface->type;
671 	ictl.robustness = iface->robustness;
672 	ictl.linkstate = iface->linkstate;
673 	ictl.passive = iface->passive;
674 	ictl.igmp_version = iface->igmp_version;
675 	ictl.if_type = iface->if_type;
676 
677 	gettimeofday(&now, NULL);
678 	if (evtimer_pending(&iface->probe_timer, &tv)) {
679 		timersub(&tv, &now, &res);
680 		ictl.probe_timer = res.tv_sec;
681 	} else
682 		ictl.probe_timer = -1;
683 
684 	if (evtimer_pending(&iface->query_timer, &tv)) {
685 		timersub(&tv, &now, &res);
686 		ictl.query_timer = res.tv_sec;
687 	} else
688 		ictl.query_timer = -1;
689 
690 	if (evtimer_pending(&iface->querier_present_timer, &tv)) {
691 		timersub(&tv, &now, &res);
692 		ictl.querier_present_timer = res.tv_sec;
693 	} else
694 		ictl.querier_present_timer = -1;
695 
696 	if (iface->state != IF_STA_DOWN) {
697 		ictl.uptime = now.tv_sec - iface->uptime;
698 	} else
699 		ictl.uptime = 0;
700 
701 	return (&ictl);
702 }
703