xref: /openbsd-src/usr.sbin/dvmrpd/interface.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: interface.c,v 1.8 2009/03/06 18:39:13 michele 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 	TAILQ_INIT(&iface->rde_group_list);
177 	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
178 
179 	if ((ifr = calloc(1, sizeof(*ifr))) == NULL)
180 		err(1, "if_new: calloc");
181 
182 	/* set up ifreq */
183 	strlcpy(ifr->ifr_name, kif->ifname, sizeof(ifr->ifr_name));
184 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
185 		err(1, "if_new: socket");
186 
187 	/* get type */
188 	if ((kif->flags & IFF_POINTOPOINT))
189 		iface->type = IF_TYPE_POINTOPOINT;
190 	if ((kif->flags & IFF_BROADCAST) &&
191 	    (kif->flags & IFF_MULTICAST))
192 		iface->type = IF_TYPE_BROADCAST;
193 
194 	/* get mtu, index and flags */
195 	iface->mtu = kif->mtu;
196 	iface->ifindex = kif->ifindex;
197 	iface->flags = kif->flags;
198 	iface->linkstate = kif->link_state;
199 	iface->media_type = kif->media_type;
200 	iface->baudrate = kif->baudrate;
201 
202 	/* get address */
203 	if (ioctl(s, SIOCGIFADDR, (caddr_t)ifr) < 0)
204 		err(1, "if_new: cannot get address");
205 	sain = (struct sockaddr_in *) &ifr->ifr_addr;
206 	iface->addr = sain->sin_addr;
207 
208 	/* get mask */
209 	if (ioctl(s, SIOCGIFNETMASK, (caddr_t)ifr) < 0)
210 		err(1, "if_new: cannot get mask");
211 	sain = (struct sockaddr_in *) &ifr->ifr_addr;
212 	iface->mask = sain->sin_addr;
213 
214 	/* get p2p dst address */
215 	if (iface->type == IF_TYPE_POINTOPOINT) {
216 		if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)ifr) < 0)
217 			err(1, "if_new: cannot get dst addr");
218 		sain = (struct sockaddr_in *) &ifr->ifr_addr;
219 		iface->dst = sain->sin_addr;
220 	}
221 
222 	free(ifr);
223 	close(s);
224 
225 	return (iface);
226 }
227 
228 void
229 if_init(struct dvmrpd_conf *xconf, struct iface *iface)
230 {
231 	/* set event handlers for interface */
232 	evtimer_set(&iface->probe_timer, if_probe_timer, iface);
233 	evtimer_set(&iface->query_timer, if_query_timer, iface);
234 	evtimer_set(&iface->querier_present_timer, if_querier_present_timer,
235 	    iface);
236 
237 	TAILQ_INIT(&iface->rr_list);
238 
239 	iface->fd = xconf->dvmrp_socket;
240 	iface->gen_id = xconf->gen_id;
241 }
242 
243 int
244 if_del(struct iface *iface)
245 {
246 	struct nbr	*nbr = NULL;
247 
248 	/* clear lists etc */
249 	while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) {
250 		LIST_REMOVE(nbr, entry);
251 		nbr_del(nbr);
252 	}
253 	group_list_clr(iface);
254 
255 	return (-1);
256 }
257 
258 int
259 if_nbr_list_empty(struct iface *iface)
260 {
261 	return (LIST_EMPTY(&iface->nbr_list));
262 }
263 
264 /* timers */
265 void
266 if_probe_timer(int fd, short event, void *arg)
267 {
268 	struct iface *iface = arg;
269 	struct timeval tv;
270 
271 	send_probe(iface);
272 
273 	/* reschedule probe_timer */
274 	if (!iface->passive) {
275 		timerclear(&tv);
276 		tv.tv_sec = iface->probe_interval;
277 		evtimer_add(&iface->probe_timer, &tv);
278 	}
279 }
280 
281 int
282 if_start_probe_timer(struct iface *iface)
283 {
284 	struct timeval tv;
285 
286 	timerclear(&tv);
287 	return (evtimer_add(&iface->probe_timer, &tv));
288 }
289 
290 int
291 if_stop_probe_timer(struct iface *iface)
292 {
293 	return (evtimer_del(&iface->probe_timer));
294 }
295 
296 void
297 if_query_timer(int fd, short event, void *arg)
298 {
299 	struct iface *iface = arg;
300 	struct timeval tv;
301 
302 	/* send a general query */
303 	send_igmp_query(iface, NULL);
304 
305 	/* reschedule query_timer */
306 	if (!iface->passive) {
307 		timerclear(&tv);
308 		if (iface->startup_query_counter != 0) {
309 			tv.tv_sec = iface->startup_query_interval;
310 			iface->startup_query_counter--;
311 		} else
312 			tv.tv_sec = iface->query_interval;
313 
314 		evtimer_add(&iface->query_timer, &tv);
315 	}
316 }
317 
318 int
319 if_start_query_timer(struct iface *iface)
320 {
321 	struct timeval tv;
322 
323 	timerclear(&tv);
324 	return (evtimer_add(&iface->query_timer, &tv));
325 }
326 
327 int
328 if_stop_query_timer(struct iface *iface)
329 {
330 	return (evtimer_del(&iface->query_timer));
331 }
332 
333 void
334 if_querier_present_timer(int fd, short event, void *arg)
335 {
336 	struct iface *iface = arg;
337 
338 	if_fsm(iface, IF_EVT_QPRSNTTMOUT);
339 }
340 
341 int
342 if_start_querier_present_timer(struct iface *iface)
343 {
344 	struct timeval tv;
345 
346 	/* Other Querier Present Interval */
347 	timerclear(&tv);
348 	tv.tv_sec = iface->robustness * iface->query_interval +
349 	    (iface->query_resp_interval / 2);
350 
351 	return (evtimer_add(&iface->querier_present_timer, &tv));
352 }
353 
354 int
355 if_stop_querier_present_timer(struct iface *iface)
356 {
357 	return (evtimer_del(&iface->querier_present_timer));
358 }
359 
360 int
361 if_reset_querier_present_timer(struct iface *iface)
362 {
363 	struct timeval	tv;
364 
365 	/* Other Querier Present Interval */
366 	timerclear(&tv);
367 	tv.tv_sec = iface->robustness * iface->query_interval +
368 	    (iface->query_resp_interval / 2);
369 
370 	return (evtimer_add(&iface->querier_present_timer, &tv));
371 }
372 
373 /* actions */
374 int
375 if_act_start(struct iface *iface)
376 {
377 	struct in_addr	 addr;
378 	struct timeval	 now;
379 
380 	if (iface->passive) {
381 		log_debug("if_act_start: cannot start passive interface %s",
382 		    iface->name);
383 		return (-1);
384 	}
385 
386 	if (!((iface->flags & IFF_UP) &&
387 	    (iface->linkstate != LINK_STATE_DOWN))) {
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
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
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 *
500 if_event_name(int event)
501 {
502 	return (if_event_names[event]);
503 }
504 
505 const char *
506 if_action_name(int action)
507 {
508 	return (if_action_names[action]);
509 }
510 
511 /* misc */
512 int
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)) < 0) {
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
526 if_set_tos(int fd, int tos)
527 {
528 	if (setsockopt(fd, IPPROTO_IP, IP_TOS,
529 	    (int *)&tos, sizeof(tos)) < 0) {
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
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
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)) < 0) {
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
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)) < 0) {
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
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)) < 0) {
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
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)) < 0) {
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 *
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.mediatype = iface->media_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