xref: /openbsd-src/usr.sbin/ospfd/interface.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: interface.c,v 1.62 2009/04/26 12:48:06 sthen Exp $ */
2 
3 /*
4  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5  * Copyright (c) 2004, 2005 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 #include <netinet/in.h>
25 #include <arpa/inet.h>
26 #include <net/if.h>
27 #include <net/if_types.h>
28 #include <ctype.h>
29 #include <err.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <event.h>
35 
36 #include "ospfd.h"
37 #include "ospf.h"
38 #include "log.h"
39 #include "ospfe.h"
40 
41 void		 if_hello_timer(int, short, void *);
42 void		 if_start_hello_timer(struct iface *);
43 void		 if_stop_hello_timer(struct iface *);
44 void		 if_stop_wait_timer(struct iface *);
45 void		 if_wait_timer(int, short, void *);
46 void		 if_start_wait_timer(struct iface *);
47 void		 if_stop_wait_timer(struct iface *);
48 struct nbr	*if_elect(struct nbr *, struct nbr *);
49 
50 struct {
51 	int			state;
52 	enum iface_event	event;
53 	enum iface_action	action;
54 	int			new_state;
55 } iface_fsm[] = {
56     /* current state	event that happened	action to take	resulting state */
57     {IF_STA_DOWN,	IF_EVT_UP,		IF_ACT_STRT,	0},
58     {IF_STA_WAITING,	IF_EVT_BACKUP_SEEN,	IF_ACT_ELECT,	0},
59     {IF_STA_WAITING,	IF_EVT_WTIMER,		IF_ACT_ELECT,	0},
60     {IF_STA_ANY,	IF_EVT_WTIMER,		IF_ACT_NOTHING,	0},
61     {IF_STA_WAITING,	IF_EVT_NBR_CHNG,	IF_ACT_NOTHING,	0},
62     {IF_STA_MULTI,	IF_EVT_NBR_CHNG,	IF_ACT_ELECT,	0},
63     {IF_STA_ANY,	IF_EVT_NBR_CHNG,	IF_ACT_NOTHING,	0},
64     {IF_STA_ANY,	IF_EVT_DOWN,		IF_ACT_RST,	IF_STA_DOWN},
65     {IF_STA_ANY,	IF_EVT_LOOP,		IF_ACT_RST,	IF_STA_LOOPBACK},
66     {IF_STA_LOOPBACK,	IF_EVT_UNLOOP,		IF_ACT_NOTHING,	IF_STA_DOWN},
67     {-1,		IF_EVT_NOTHING,		IF_ACT_NOTHING,	0},
68 };
69 
70 static int vlink_cnt = 0;
71 
72 const char * const if_event_names[] = {
73 	"NOTHING",
74 	"UP",
75 	"WAITTIMER",
76 	"BACKUPSEEN",
77 	"NEIGHBORCHANGE",
78 	"LOOP",
79 	"UNLOOP",
80 	"DOWN"
81 };
82 
83 const char * const if_action_names[] = {
84 	"NOTHING",
85 	"START",
86 	"ELECT",
87 	"RESET"
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 		/* event outside of the defined fsm, ignore it. */
108 		log_debug("if_fsm: interface %s, "
109 		    "event %s not expected in state %s", iface->name,
110 		    if_event_names[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_ELECT:
119 		ret = if_act_elect(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("if_fsm: error changing state for interface %s, "
131 		    "event %s, state %s", iface->name, if_event_names[event],
132 		    if_state_name(old_state));
133 		return (-1);
134 	}
135 
136 	if (new_state != 0)
137 		iface->state = new_state;
138 
139 	if (iface->state != old_state)
140 		orig_rtr_lsa(iface->area);
141 
142 	if (old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT) &&
143 	    (iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0)
144 		ospfe_demote_iface(iface, 0);
145 	if ((old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0 &&
146 	    iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT))
147 		ospfe_demote_iface(iface, 1);
148 
149 	log_debug("if_fsm: event %s resulted in action %s and changing "
150 	    "state for interface %s from %s to %s",
151 	    if_event_names[event], if_action_names[iface_fsm[i].action],
152 	    iface->name, if_state_name(old_state), if_state_name(iface->state));
153 
154 	return (ret);
155 }
156 
157 struct iface *
158 if_new(struct kif *kif, struct kif_addr *ka)
159 {
160 	struct iface		*iface;
161 
162 	if ((iface = calloc(1, sizeof(*iface))) == NULL)
163 		err(1, "if_new: calloc");
164 
165 	iface->state = IF_STA_DOWN;
166 
167 	LIST_INIT(&iface->nbr_list);
168 	TAILQ_INIT(&iface->ls_ack_list);
169 	TAILQ_INIT(&iface->auth_md_list);
170 
171 	iface->crypt_seq_num = arc4random() & 0x0fffffff;
172 
173 	if (kif == NULL) {
174 		iface->type = IF_TYPE_VIRTUALLINK;
175 		snprintf(iface->name, sizeof(iface->name), "vlink%d",
176 		    vlink_cnt++);
177 		iface->flags |= IFF_UP;
178 		iface->mtu = IP_MSS;
179 		return (iface);
180 	}
181 
182 	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
183 
184 	/* get type */
185 	if (kif->flags & IFF_POINTOPOINT)
186 		iface->type = IF_TYPE_POINTOPOINT;
187 	if (kif->flags & IFF_BROADCAST &&
188 	    kif->flags & IFF_MULTICAST)
189 		iface->type = IF_TYPE_BROADCAST;
190 	if (kif->flags & IFF_LOOPBACK) {
191 		iface->type = IF_TYPE_POINTOPOINT;
192 		iface->state = IF_STA_LOOPBACK;
193 	}
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->media_type = kif->media_type;
201 	iface->baudrate = kif->baudrate;
202 
203 	/* set address, mask and p2p addr */
204 	iface->addr = ka->addr;
205 	iface->mask = ka->mask;
206 	if (kif->flags & IFF_POINTOPOINT) {
207 		iface->dst = ka->dstbrd;
208 	}
209 
210 	return (iface);
211 }
212 
213 void
214 if_del(struct iface *iface)
215 {
216 	struct nbr	*nbr = NULL;
217 
218 	/* revert the demotion when the interface is deleted */
219 	if ((iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0)
220 		ospfe_demote_iface(iface, 1);
221 
222 	/* clear lists etc */
223 	while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL)
224 		nbr_del(nbr);
225 
226 	if (evtimer_pending(&iface->hello_timer, NULL))
227 		evtimer_del(&iface->hello_timer);
228 	if (evtimer_pending(&iface->wait_timer, NULL))
229 		evtimer_del(&iface->wait_timer);
230 	if (evtimer_pending(&iface->lsack_tx_timer, NULL))
231 		evtimer_del(&iface->lsack_tx_timer);
232 
233 	ls_ack_list_clr(iface);
234 	md_list_clr(&iface->auth_md_list);
235 	free(iface);
236 }
237 
238 void
239 if_init(struct ospfd_conf *xconf, struct iface *iface)
240 {
241 	/* init the dummy local neighbor */
242 	iface->self = nbr_new(ospfe_router_id(), iface, 1);
243 
244 	/* set event handlers for interface */
245 	evtimer_set(&iface->lsack_tx_timer, ls_ack_tx_timer, iface);
246 	evtimer_set(&iface->hello_timer, if_hello_timer, iface);
247 	evtimer_set(&iface->wait_timer, if_wait_timer, iface);
248 
249 	iface->fd = xconf->ospf_socket;
250 
251 	ospfe_demote_iface(iface, 0);
252 }
253 
254 /* timers */
255 /* ARGSUSED */
256 void
257 if_hello_timer(int fd, short event, void *arg)
258 {
259 	struct iface *iface = arg;
260 	struct timeval tv;
261 
262 	send_hello(iface);
263 
264 	/* reschedule hello_timer */
265 	timerclear(&tv);
266 	tv.tv_sec = iface->hello_interval;
267 	if (evtimer_add(&iface->hello_timer, &tv) == -1)
268 		fatal("if_hello_timer");
269 }
270 
271 void
272 if_start_hello_timer(struct iface *iface)
273 {
274 	struct timeval tv;
275 
276 	timerclear(&tv);
277 	if (evtimer_add(&iface->hello_timer, &tv) == -1)
278 		fatal("if_start_hello_timer");
279 }
280 
281 void
282 if_stop_hello_timer(struct iface *iface)
283 {
284 	if (evtimer_del(&iface->hello_timer) == -1)
285 		fatal("if_stop_hello_timer");
286 }
287 
288 /* ARGSUSED */
289 void
290 if_wait_timer(int fd, short event, void *arg)
291 {
292 	struct iface *iface = arg;
293 
294 	if_fsm(iface, IF_EVT_WTIMER);
295 }
296 
297 void
298 if_start_wait_timer(struct iface *iface)
299 {
300 	struct timeval	tv;
301 
302 	timerclear(&tv);
303 	tv.tv_sec = iface->dead_interval;
304 	if (evtimer_add(&iface->wait_timer, &tv) == -1)
305 		fatal("if_start_wait_timer");
306 }
307 
308 void
309 if_stop_wait_timer(struct iface *iface)
310 {
311 	if (evtimer_del(&iface->wait_timer) == -1)
312 		fatal("if_stop_wait_timer");
313 }
314 
315 /* actions */
316 int
317 if_act_start(struct iface *iface)
318 {
319 	struct in_addr		 addr;
320 	struct timeval		 now;
321 
322 	if (!((iface->flags & IFF_UP) &&
323 	    (LINK_STATE_IS_UP(iface->linkstate) ||
324 	    (iface->linkstate == LINK_STATE_UNKNOWN &&
325 	    iface->media_type != IFT_CARP))))
326 		return (0);
327 
328 	if (iface->media_type == IFT_CARP && iface->passive == 0) {
329 		/* force passive mode on carp interfaces */
330 		log_warnx("if_act_start: forcing interface %s to passive",
331 		    iface->name);
332 		iface->passive = 1;
333 	}
334 
335 	if (iface->passive) {
336 		/* for an update of stub network entries */
337 		orig_rtr_lsa(iface->area);
338 		return (0);
339 	}
340 
341 	gettimeofday(&now, NULL);
342 	iface->uptime = now.tv_sec;
343 
344 	switch (iface->type) {
345 	case IF_TYPE_POINTOPOINT:
346 		inet_aton(AllSPFRouters, &addr);
347 		if (if_join_group(iface, &addr))
348 			return (-1);
349 		iface->state = IF_STA_POINTTOPOINT;
350 		break;
351 	case IF_TYPE_VIRTUALLINK:
352 		iface->state = IF_STA_POINTTOPOINT;
353 		break;
354 	case IF_TYPE_POINTOMULTIPOINT:
355 	case IF_TYPE_NBMA:
356 		log_debug("if_act_start: type %s not supported, interface %s",
357 		    if_type_name(iface->type), iface->name);
358 		return (-1);
359 	case IF_TYPE_BROADCAST:
360 		inet_aton(AllSPFRouters, &addr);
361 		if (if_join_group(iface, &addr))
362 			return (-1);
363 		if (iface->priority == 0) {
364 			iface->state = IF_STA_DROTHER;
365 		} else {
366 			iface->state = IF_STA_WAITING;
367 			if_start_wait_timer(iface);
368 		}
369 		break;
370 	default:
371 		fatalx("if_act_start: unknown interface type");
372 	}
373 
374 	/* hello timer needs to be started in any case */
375 	if_start_hello_timer(iface);
376 	return (0);
377 }
378 
379 struct nbr *
380 if_elect(struct nbr *a, struct nbr *b)
381 {
382 	if (a->priority > b->priority)
383 		return (a);
384 	if (a->priority < b->priority)
385 		return (b);
386 	if (ntohl(a->id.s_addr) > ntohl(b->id.s_addr))
387 		return (a);
388 	return (b);
389 }
390 
391 int
392 if_act_elect(struct iface *iface)
393 {
394 	struct in_addr	 addr;
395 	struct nbr	*nbr, *bdr = NULL, *dr = NULL;
396 	int		 round = 0;
397 	int		 changed = 0;
398 	int		 old_state;
399 	char		 b1[16], b2[16], b3[16], b4[16];
400 
401 start:
402 	/* elect backup designated router */
403 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
404 		if (nbr->priority == 0 || nbr == dr ||	/* not electable */
405 		    nbr->state & NBR_STA_PRELIM ||	/* not available */
406 		    nbr->dr.s_addr == nbr->addr.s_addr)	/* don't elect DR */
407 			continue;
408 		if (bdr != NULL) {
409 			/*
410 			 * routers announcing themselves as BDR have higher
411 			 * precedence over those routers announcing a
412 			 * different BDR.
413 			 */
414 			if (nbr->bdr.s_addr == nbr->addr.s_addr) {
415 				if (bdr->bdr.s_addr == bdr->addr.s_addr)
416 					bdr = if_elect(bdr, nbr);
417 				else
418 					bdr = nbr;
419 			} else if (bdr->bdr.s_addr != bdr->addr.s_addr)
420 					bdr = if_elect(bdr, nbr);
421 		} else
422 			bdr = nbr;
423 	}
424 
425 	/* elect designated router */
426 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
427 		if (nbr->priority == 0 || nbr->state & NBR_STA_PRELIM ||
428 		    (nbr != dr && nbr->dr.s_addr != nbr->addr.s_addr))
429 			/* only DR may be elected check priority too */
430 			continue;
431 		if (dr == NULL)
432 			dr = nbr;
433 		else
434 			dr = if_elect(dr, nbr);
435 	}
436 
437 	if (dr == NULL) {
438 		/* no designated router found use backup DR */
439 		dr = bdr;
440 		bdr = NULL;
441 	}
442 
443 	/*
444 	 * if we are involved in the election (e.g. new DR or no
445 	 * longer BDR) redo the election
446 	 */
447 	if (round == 0 &&
448 	    ((iface->self == dr && iface->self != iface->dr) ||
449 	    (iface->self != dr && iface->self == iface->dr) ||
450 	    (iface->self == bdr && iface->self != iface->bdr) ||
451 	    (iface->self != bdr && iface->self == iface->bdr))) {
452 		/*
453 		 * Reset announced DR/BDR to calculated one, so
454 		 * that we may get elected in the second round.
455 		 * This is needed to drop from a DR to a BDR.
456 		 */
457 		iface->self->dr.s_addr = dr->addr.s_addr;
458 		if (bdr)
459 			iface->self->bdr.s_addr = bdr->addr.s_addr;
460 		round = 1;
461 		goto start;
462 	}
463 
464 	log_debug("if_act_elect: interface %s old dr %s new dr %s, "
465 	    "old bdr %s new bdr %s", iface->name,
466 	    iface->dr ? inet_ntop(AF_INET, &iface->dr->addr, b1, sizeof(b1)) :
467 	    "none", dr ? inet_ntop(AF_INET, &dr->addr, b2, sizeof(b2)) : "none",
468 	    iface->bdr ? inet_ntop(AF_INET, &iface->bdr->addr, b3, sizeof(b3)) :
469 	    "none", bdr ? inet_ntop(AF_INET, &bdr->addr, b4, sizeof(b4)) :
470 	    "none");
471 
472 	/*
473 	 * After the second round still DR or BDR change state to DR or BDR,
474 	 * etc.
475 	 */
476 	old_state = iface->state;
477 	if (dr == iface->self)
478 		iface->state = IF_STA_DR;
479 	else if (bdr == iface->self)
480 		iface->state = IF_STA_BACKUP;
481 	else
482 		iface->state = IF_STA_DROTHER;
483 
484 	/* TODO if iface is NBMA send all non eligible neighbors event Start */
485 
486 	/*
487 	 * if DR or BDR changed issue a AdjOK? event for all neighbors > 2-Way
488 	 */
489 	if (iface->dr != dr || iface->bdr != bdr)
490 		changed = 1;
491 
492 	iface->dr = dr;
493 	iface->bdr = bdr;
494 
495 	if (changed) {
496 		inet_aton(AllDRouters, &addr);
497 		if (old_state & IF_STA_DRORBDR &&
498 		    (iface->state & IF_STA_DRORBDR) == 0) {
499 			if (if_leave_group(iface, &addr))
500 				return (-1);
501 		} else if ((old_state & IF_STA_DRORBDR) == 0 &&
502 		    iface->state & IF_STA_DRORBDR) {
503 			if (if_join_group(iface, &addr))
504 				return (-1);
505 		}
506 
507 		LIST_FOREACH(nbr, &iface->nbr_list, entry) {
508 			if (nbr->state & NBR_STA_BIDIR)
509 				nbr_fsm(nbr, NBR_EVT_ADJ_OK);
510 		}
511 
512 		orig_rtr_lsa(iface->area);
513 		if (iface->state & IF_STA_DR || old_state & IF_STA_DR)
514 			orig_net_lsa(iface);
515 	}
516 
517 	if_start_hello_timer(iface);
518 	return (0);
519 }
520 
521 int
522 if_act_reset(struct iface *iface)
523 {
524 	struct nbr		*nbr = NULL;
525 	struct in_addr		 addr;
526 
527 	if (iface->passive) {
528 		/* for an update of stub network entries */
529 		orig_rtr_lsa(iface->area);
530 		return (0);
531 	}
532 
533 	switch (iface->type) {
534 	case IF_TYPE_POINTOPOINT:
535 	case IF_TYPE_BROADCAST:
536 		inet_aton(AllSPFRouters, &addr);
537 		if (if_leave_group(iface, &addr)) {
538 			log_warnx("if_act_reset: error leaving group %s, "
539 			    "interface %s", inet_ntoa(addr), iface->name);
540 		}
541 		if (iface->state & IF_STA_DRORBDR) {
542 			inet_aton(AllDRouters, &addr);
543 			if (if_leave_group(iface, &addr)) {
544 				log_warnx("if_act_reset: "
545 				    "error leaving group %s, interface %s",
546 				    inet_ntoa(addr), iface->name);
547 			}
548 		}
549 		break;
550 	case IF_TYPE_VIRTUALLINK:
551 		/* nothing */
552 		break;
553 	case IF_TYPE_NBMA:
554 	case IF_TYPE_POINTOMULTIPOINT:
555 		log_debug("if_act_reset: type %s not supported, interface %s",
556 		    if_type_name(iface->type), iface->name);
557 		return (-1);
558 	default:
559 		fatalx("if_act_reset: unknown interface type");
560 	}
561 
562 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
563 		if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) {
564 			log_debug("if_act_reset: error killing neighbor %s",
565 			    inet_ntoa(nbr->id));
566 		}
567 	}
568 
569 	iface->dr = NULL;
570 	iface->bdr = NULL;
571 
572 	ls_ack_list_clr(iface);
573 	stop_ls_ack_tx_timer(iface);
574 	if_stop_hello_timer(iface);
575 	if_stop_wait_timer(iface);
576 
577 	/* send empty hello to tell everybody that we are going down */
578 	send_hello(iface);
579 
580 	return (0);
581 }
582 
583 struct ctl_iface *
584 if_to_ctl(struct iface *iface)
585 {
586 	static struct ctl_iface	 ictl;
587 	struct timeval		 tv, now, res;
588 	struct nbr		*nbr;
589 
590 	memcpy(ictl.name, iface->name, sizeof(ictl.name));
591 	memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr));
592 	memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask));
593 	ictl.rtr_id.s_addr = ospfe_router_id();
594 	memcpy(&ictl.area, &iface->area->id, sizeof(ictl.area));
595 	if (iface->dr) {
596 		memcpy(&ictl.dr_id, &iface->dr->id, sizeof(ictl.dr_id));
597 		memcpy(&ictl.dr_addr, &iface->dr->addr, sizeof(ictl.dr_addr));
598 	} else {
599 		bzero(&ictl.dr_id, sizeof(ictl.dr_id));
600 		bzero(&ictl.dr_addr, sizeof(ictl.dr_addr));
601 	}
602 	if (iface->bdr) {
603 		memcpy(&ictl.bdr_id, &iface->bdr->id, sizeof(ictl.bdr_id));
604 		memcpy(&ictl.bdr_addr, &iface->bdr->addr,
605 		    sizeof(ictl.bdr_addr));
606 	} else {
607 		bzero(&ictl.bdr_id, sizeof(ictl.bdr_id));
608 		bzero(&ictl.bdr_addr, sizeof(ictl.bdr_addr));
609 	}
610 	ictl.ifindex = iface->ifindex;
611 	ictl.state = iface->state;
612 	ictl.mtu = iface->mtu;
613 	ictl.nbr_cnt = 0;
614 	ictl.adj_cnt = 0;
615 	ictl.baudrate = iface->baudrate;
616 	ictl.dead_interval = iface->dead_interval;
617 	ictl.transmit_delay = iface->transmit_delay;
618 	ictl.hello_interval = iface->hello_interval;
619 	ictl.flags = iface->flags;
620 	ictl.metric = iface->metric;
621 	ictl.rxmt_interval = iface->rxmt_interval;
622 	ictl.type = iface->type;
623 	ictl.linkstate = iface->linkstate;
624 	ictl.mediatype = iface->media_type;
625 	ictl.priority = iface->priority;
626 	ictl.passive = iface->passive;
627 	ictl.auth_type = iface->auth_type;
628 	ictl.auth_keyid = iface->auth_keyid;
629 
630 	gettimeofday(&now, NULL);
631 	if (evtimer_pending(&iface->hello_timer, &tv)) {
632 		timersub(&tv, &now, &res);
633 		ictl.hello_timer = res.tv_sec;
634 	} else
635 		ictl.hello_timer = -1;
636 
637 	if (iface->state != IF_STA_DOWN) {
638 		ictl.uptime = now.tv_sec - iface->uptime;
639 	} else
640 		ictl.uptime = 0;
641 
642 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
643 		if (nbr == iface->self)
644 			continue;
645 		ictl.nbr_cnt++;
646 		if (nbr->state & NBR_STA_ADJFORM)
647 			ictl.adj_cnt++;
648 	}
649 
650 	return (&ictl);
651 }
652 
653 /* misc */
654 int
655 if_set_recvif(int fd, int enable)
656 {
657 	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
658 	    sizeof(enable)) < 0) {
659 		log_warn("if_set_recvif: error setting IP_RECVIF");
660 		return (-1);
661 	}
662 	return (0);
663 }
664 
665 void
666 if_set_recvbuf(int fd)
667 {
668 	int	bsize;
669 
670 	bsize = 65535;
671 	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
672 	    sizeof(bsize)) == -1)
673 		bsize /= 2;
674 }
675 
676 /*
677  * only one JOIN or DROP per interface and address is allowed so we need
678  * to keep track of what is added and removed.
679  */
680 struct if_group_count {
681 	LIST_ENTRY(if_group_count)	entry;
682 	struct in_addr			addr;
683 	unsigned int			ifindex;
684 	int				count;
685 };
686 
687 LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
688 
689 int
690 if_join_group(struct iface *iface, struct in_addr *addr)
691 {
692 	struct ip_mreq		 mreq;
693 	struct if_group_count	*ifg;
694 
695 	switch (iface->type) {
696 	case IF_TYPE_POINTOPOINT:
697 	case IF_TYPE_BROADCAST:
698 		LIST_FOREACH(ifg, &ifglist, entry)
699 			if (iface->ifindex == ifg->ifindex &&
700 			    addr->s_addr == ifg->addr.s_addr)
701 				break;
702 		if (ifg == NULL) {
703 			if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
704 				fatal("if_join_group");
705 			ifg->addr.s_addr = addr->s_addr;
706 			ifg->ifindex = iface->ifindex;
707 			LIST_INSERT_HEAD(&ifglist, ifg, entry);
708 		}
709 
710 		if (ifg->count++ != 0)
711 			/* already joined */
712 			return (0);
713 
714 		mreq.imr_multiaddr.s_addr = addr->s_addr;
715 		mreq.imr_interface.s_addr = iface->addr.s_addr;
716 
717 		if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
718 		    (void *)&mreq, sizeof(mreq)) < 0) {
719 			log_warn("if_join_group: error IP_ADD_MEMBERSHIP, "
720 			    "interface %s address %s", iface->name,
721 			    inet_ntoa(*addr));
722 			return (-1);
723 		}
724 		break;
725 	case IF_TYPE_POINTOMULTIPOINT:
726 	case IF_TYPE_VIRTUALLINK:
727 	case IF_TYPE_NBMA:
728 		log_debug("if_join_group: type %s not supported, interface %s",
729 		    if_type_name(iface->type), iface->name);
730 		return (-1);
731 	default:
732 		fatalx("if_join_group: unknown interface type");
733 	}
734 
735 	return (0);
736 }
737 
738 int
739 if_leave_group(struct iface *iface, struct in_addr *addr)
740 {
741 	struct ip_mreq		 mreq;
742 	struct if_group_count	*ifg;
743 
744 	switch (iface->type) {
745 	case IF_TYPE_POINTOPOINT:
746 	case IF_TYPE_BROADCAST:
747 		LIST_FOREACH(ifg, &ifglist, entry)
748 			if (iface->ifindex == ifg->ifindex &&
749 			    addr->s_addr == ifg->addr.s_addr)
750 				break;
751 
752 		/* if interface is not found just try to drop membership */
753 		if (ifg && --ifg->count != 0)
754 			/* others still joined */
755 			return (0);
756 
757 		mreq.imr_multiaddr.s_addr = addr->s_addr;
758 		mreq.imr_interface.s_addr = iface->addr.s_addr;
759 
760 		if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
761 		    (void *)&mreq, sizeof(mreq)) < 0) {
762 			log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, "
763 			    "interface %s address %s", iface->name,
764 			    inet_ntoa(*addr));
765 			return (-1);
766 		}
767 
768 		if (ifg) {
769 			LIST_REMOVE(ifg, entry);
770 			free(ifg);
771 		}
772 		break;
773 	case IF_TYPE_POINTOMULTIPOINT:
774 	case IF_TYPE_VIRTUALLINK:
775 	case IF_TYPE_NBMA:
776 		log_debug("if_leave_group: type %s not supported, interface %s",
777 		    if_type_name(iface->type), iface->name);
778 		return (-1);
779 	default:
780 		fatalx("if_leave_group: unknown interface type");
781 	}
782 
783 	return (0);
784 }
785 
786 int
787 if_set_mcast(struct iface *iface)
788 {
789 	switch (iface->type) {
790 	case IF_TYPE_POINTOPOINT:
791 	case IF_TYPE_BROADCAST:
792 		if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF,
793 		    &iface->addr.s_addr, sizeof(iface->addr.s_addr)) < 0) {
794 			log_debug("if_set_mcast: error setting "
795 			    "IP_MULTICAST_IF, interface %s", iface->name);
796 			return (-1);
797 		}
798 		break;
799 	case IF_TYPE_POINTOMULTIPOINT:
800 	case IF_TYPE_VIRTUALLINK:
801 	case IF_TYPE_NBMA:
802 		log_debug("if_set_mcast: type %s not supported, interface %s",
803 		    if_type_name(iface->type), iface->name);
804 		return (-1);
805 	default:
806 		fatalx("if_set_mcast: unknown interface type");
807 	}
808 
809 	return (0);
810 }
811 
812 int
813 if_set_mcast_loop(int fd)
814 {
815 	u_int8_t	loop = 0;
816 
817 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
818 	    (char *)&loop, sizeof(loop)) < 0) {
819 		log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
820 		return (-1);
821 	}
822 
823 	return (0);
824 }
825 
826 int
827 if_set_ip_hdrincl(int fd)
828 {
829 	int	hincl = 1;
830 
831 	if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) {
832 		log_warn("if_set_ip_hdrincl: error setting IP_HDRINCL");
833 		return (-1);
834 	}
835 
836 	return (0);
837 }
838