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