xref: /openbsd-src/usr.sbin/ospfd/interface.c (revision cd1eb269cafb12c415be1749cd4a4b5422710415)
1 /*	$OpenBSD: interface.c,v 1.68 2010/02/16 18:20:37 claudio 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 	struct ifreq	ifr;
242 	u_int		rdomain;
243 
244 	/* init the dummy local neighbor */
245 	iface->self = nbr_new(ospfe_router_id(), iface, 1);
246 
247 	/* set event handlers for interface */
248 	evtimer_set(&iface->lsack_tx_timer, ls_ack_tx_timer, iface);
249 	evtimer_set(&iface->hello_timer, if_hello_timer, iface);
250 	evtimer_set(&iface->wait_timer, if_wait_timer, iface);
251 
252 	iface->fd = xconf->ospf_socket;
253 
254 	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
255 	if (ioctl(iface->fd, SIOCGIFRTABLEID, (caddr_t)&ifr) == -1)
256 		rdomain = 0;
257 	else {
258 		rdomain = ifr.ifr_rdomainid;
259 		if (setsockopt(iface->fd, IPPROTO_IP, SO_RDOMAIN,
260 		    &rdomain, sizeof(rdomain)) == -1)
261 			fatal("failed to set rdomain");
262 	}
263 	if (rdomain != xconf->rdomain)
264 		fatalx("interface rdomain mismatch");
265 
266 	ospfe_demote_iface(iface, 0);
267 }
268 
269 /* timers */
270 /* ARGSUSED */
271 void
272 if_hello_timer(int fd, short event, void *arg)
273 {
274 	struct iface *iface = arg;
275 	struct timeval tv;
276 
277 	send_hello(iface);
278 
279 	/* reschedule hello_timer */
280 	timerclear(&tv);
281 	if (iface->dead_interval == FAST_RTR_DEAD_TIME)
282 		tv.tv_usec = iface->fast_hello_interval * 1000;
283 	else
284 		tv.tv_sec = iface->hello_interval;
285 	if (evtimer_add(&iface->hello_timer, &tv) == -1)
286 		fatal("if_hello_timer");
287 }
288 
289 void
290 if_start_hello_timer(struct iface *iface)
291 {
292 	struct timeval tv;
293 
294 	timerclear(&tv);
295 	if (evtimer_add(&iface->hello_timer, &tv) == -1)
296 		fatal("if_start_hello_timer");
297 }
298 
299 void
300 if_stop_hello_timer(struct iface *iface)
301 {
302 	if (evtimer_del(&iface->hello_timer) == -1)
303 		fatal("if_stop_hello_timer");
304 }
305 
306 /* ARGSUSED */
307 void
308 if_wait_timer(int fd, short event, void *arg)
309 {
310 	struct iface *iface = arg;
311 
312 	if_fsm(iface, IF_EVT_WTIMER);
313 }
314 
315 void
316 if_start_wait_timer(struct iface *iface)
317 {
318 	struct timeval	tv;
319 
320 	timerclear(&tv);
321 	tv.tv_sec = iface->dead_interval;
322 	if (evtimer_add(&iface->wait_timer, &tv) == -1)
323 		fatal("if_start_wait_timer");
324 }
325 
326 void
327 if_stop_wait_timer(struct iface *iface)
328 {
329 	if (evtimer_del(&iface->wait_timer) == -1)
330 		fatal("if_stop_wait_timer");
331 }
332 
333 /* actions */
334 int
335 if_act_start(struct iface *iface)
336 {
337 	struct in_addr		 addr;
338 	struct timeval		 now;
339 
340 	if (!((iface->flags & IFF_UP) &&
341 	    (LINK_STATE_IS_UP(iface->linkstate) ||
342 	    (iface->linkstate == LINK_STATE_UNKNOWN &&
343 	    iface->media_type != IFT_CARP))))
344 		return (0);
345 
346 	if (iface->media_type == IFT_CARP && iface->passive == 0) {
347 		/* force passive mode on carp interfaces */
348 		log_warnx("if_act_start: forcing interface %s to passive",
349 		    iface->name);
350 		iface->passive = 1;
351 	}
352 
353 	if (iface->passive) {
354 		/* for an update of stub network entries */
355 		orig_rtr_lsa(iface->area);
356 		return (0);
357 	}
358 
359 	gettimeofday(&now, NULL);
360 	iface->uptime = now.tv_sec;
361 
362 	switch (iface->type) {
363 	case IF_TYPE_POINTOPOINT:
364 		inet_aton(AllSPFRouters, &addr);
365 		if (if_join_group(iface, &addr))
366 			return (-1);
367 		iface->state = IF_STA_POINTTOPOINT;
368 		break;
369 	case IF_TYPE_VIRTUALLINK:
370 		iface->state = IF_STA_POINTTOPOINT;
371 		break;
372 	case IF_TYPE_POINTOMULTIPOINT:
373 	case IF_TYPE_NBMA:
374 		log_debug("if_act_start: type %s not supported, interface %s",
375 		    if_type_name(iface->type), iface->name);
376 		return (-1);
377 	case IF_TYPE_BROADCAST:
378 		inet_aton(AllSPFRouters, &addr);
379 		if (if_join_group(iface, &addr))
380 			return (-1);
381 		if (iface->priority == 0) {
382 			iface->state = IF_STA_DROTHER;
383 		} else {
384 			iface->state = IF_STA_WAITING;
385 			if_start_wait_timer(iface);
386 		}
387 		break;
388 	default:
389 		fatalx("if_act_start: unknown interface type");
390 	}
391 
392 	/* hello timer needs to be started in any case */
393 	if_start_hello_timer(iface);
394 	return (0);
395 }
396 
397 struct nbr *
398 if_elect(struct nbr *a, struct nbr *b)
399 {
400 	if (a->priority > b->priority)
401 		return (a);
402 	if (a->priority < b->priority)
403 		return (b);
404 	if (ntohl(a->id.s_addr) > ntohl(b->id.s_addr))
405 		return (a);
406 	return (b);
407 }
408 
409 int
410 if_act_elect(struct iface *iface)
411 {
412 	struct in_addr	 addr;
413 	struct nbr	*nbr, *bdr = NULL, *dr = NULL;
414 	int		 round = 0;
415 	int		 changed = 0;
416 	int		 old_state;
417 	char		 b1[16], b2[16], b3[16], b4[16];
418 
419 start:
420 	/* elect backup designated router */
421 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
422 		if (nbr->priority == 0 || nbr == dr ||	/* not electable */
423 		    nbr->state & NBR_STA_PRELIM ||	/* not available */
424 		    nbr->dr.s_addr == nbr->addr.s_addr)	/* don't elect DR */
425 			continue;
426 		if (bdr != NULL) {
427 			/*
428 			 * routers announcing themselves as BDR have higher
429 			 * precedence over those routers announcing a
430 			 * different BDR.
431 			 */
432 			if (nbr->bdr.s_addr == nbr->addr.s_addr) {
433 				if (bdr->bdr.s_addr == bdr->addr.s_addr)
434 					bdr = if_elect(bdr, nbr);
435 				else
436 					bdr = nbr;
437 			} else if (bdr->bdr.s_addr != bdr->addr.s_addr)
438 					bdr = if_elect(bdr, nbr);
439 		} else
440 			bdr = nbr;
441 	}
442 
443 	/* elect designated router */
444 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
445 		if (nbr->priority == 0 || nbr->state & NBR_STA_PRELIM ||
446 		    (nbr != dr && nbr->dr.s_addr != nbr->addr.s_addr))
447 			/* only DR may be elected check priority too */
448 			continue;
449 		if (dr == NULL)
450 			dr = nbr;
451 		else
452 			dr = if_elect(dr, nbr);
453 	}
454 
455 	if (dr == NULL) {
456 		/* no designated router found use backup DR */
457 		dr = bdr;
458 		bdr = NULL;
459 	}
460 
461 	/*
462 	 * if we are involved in the election (e.g. new DR or no
463 	 * longer BDR) redo the election
464 	 */
465 	if (round == 0 &&
466 	    ((iface->self == dr && iface->self != iface->dr) ||
467 	    (iface->self != dr && iface->self == iface->dr) ||
468 	    (iface->self == bdr && iface->self != iface->bdr) ||
469 	    (iface->self != bdr && iface->self == iface->bdr))) {
470 		/*
471 		 * Reset announced DR/BDR to calculated one, so
472 		 * that we may get elected in the second round.
473 		 * This is needed to drop from a DR to a BDR.
474 		 */
475 		iface->self->dr.s_addr = dr->addr.s_addr;
476 		if (bdr)
477 			iface->self->bdr.s_addr = bdr->addr.s_addr;
478 		round = 1;
479 		goto start;
480 	}
481 
482 	log_debug("if_act_elect: interface %s old dr %s new dr %s, "
483 	    "old bdr %s new bdr %s", iface->name,
484 	    iface->dr ? inet_ntop(AF_INET, &iface->dr->addr, b1, sizeof(b1)) :
485 	    "none", dr ? inet_ntop(AF_INET, &dr->addr, b2, sizeof(b2)) : "none",
486 	    iface->bdr ? inet_ntop(AF_INET, &iface->bdr->addr, b3, sizeof(b3)) :
487 	    "none", bdr ? inet_ntop(AF_INET, &bdr->addr, b4, sizeof(b4)) :
488 	    "none");
489 
490 	/*
491 	 * After the second round still DR or BDR change state to DR or BDR,
492 	 * etc.
493 	 */
494 	old_state = iface->state;
495 	if (dr == iface->self)
496 		iface->state = IF_STA_DR;
497 	else if (bdr == iface->self)
498 		iface->state = IF_STA_BACKUP;
499 	else
500 		iface->state = IF_STA_DROTHER;
501 
502 	/* TODO if iface is NBMA send all non eligible neighbors event Start */
503 
504 	/*
505 	 * if DR or BDR changed issue a AdjOK? event for all neighbors > 2-Way
506 	 */
507 	if (iface->dr != dr || iface->bdr != bdr)
508 		changed = 1;
509 
510 	iface->dr = dr;
511 	iface->bdr = bdr;
512 
513 	if (changed) {
514 		inet_aton(AllDRouters, &addr);
515 		if (old_state & IF_STA_DRORBDR &&
516 		    (iface->state & IF_STA_DRORBDR) == 0) {
517 			if (if_leave_group(iface, &addr))
518 				return (-1);
519 		} else if ((old_state & IF_STA_DRORBDR) == 0 &&
520 		    iface->state & IF_STA_DRORBDR) {
521 			if (if_join_group(iface, &addr))
522 				return (-1);
523 		}
524 
525 		LIST_FOREACH(nbr, &iface->nbr_list, entry) {
526 			if (nbr->state & NBR_STA_BIDIR)
527 				nbr_fsm(nbr, NBR_EVT_ADJ_OK);
528 		}
529 
530 		orig_rtr_lsa(iface->area);
531 		if (iface->state & IF_STA_DR || old_state & IF_STA_DR)
532 			orig_net_lsa(iface);
533 	}
534 
535 	if_start_hello_timer(iface);
536 	return (0);
537 }
538 
539 int
540 if_act_reset(struct iface *iface)
541 {
542 	struct nbr		*nbr = NULL;
543 	struct in_addr		 addr;
544 
545 	if (iface->passive) {
546 		/* for an update of stub network entries */
547 		orig_rtr_lsa(iface->area);
548 		return (0);
549 	}
550 
551 	switch (iface->type) {
552 	case IF_TYPE_POINTOPOINT:
553 	case IF_TYPE_BROADCAST:
554 		/* try to cleanup */
555 		inet_aton(AllSPFRouters, &addr);
556 		if_leave_group(iface, &addr);
557 		if (iface->state & IF_STA_DRORBDR) {
558 			inet_aton(AllDRouters, &addr);
559 			if_leave_group(iface, &addr);
560 		}
561 		break;
562 	case IF_TYPE_VIRTUALLINK:
563 		/* nothing */
564 		break;
565 	case IF_TYPE_NBMA:
566 	case IF_TYPE_POINTOMULTIPOINT:
567 		log_debug("if_act_reset: type %s not supported, interface %s",
568 		    if_type_name(iface->type), iface->name);
569 		return (-1);
570 	default:
571 		fatalx("if_act_reset: unknown interface type");
572 	}
573 
574 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
575 		if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) {
576 			log_debug("if_act_reset: error killing neighbor %s",
577 			    inet_ntoa(nbr->id));
578 		}
579 	}
580 
581 	iface->dr = NULL;
582 	iface->bdr = NULL;
583 
584 	ls_ack_list_clr(iface);
585 	stop_ls_ack_tx_timer(iface);
586 	if_stop_hello_timer(iface);
587 	if_stop_wait_timer(iface);
588 
589 	/* send empty hello to tell everybody that we are going down */
590 	send_hello(iface);
591 
592 	return (0);
593 }
594 
595 struct ctl_iface *
596 if_to_ctl(struct iface *iface)
597 {
598 	static struct ctl_iface	 ictl;
599 	struct timeval		 tv, now, res;
600 	struct nbr		*nbr;
601 
602 	memcpy(ictl.name, iface->name, sizeof(ictl.name));
603 	memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr));
604 	memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask));
605 	ictl.rtr_id.s_addr = ospfe_router_id();
606 	memcpy(&ictl.area, &iface->area->id, sizeof(ictl.area));
607 	if (iface->dr) {
608 		memcpy(&ictl.dr_id, &iface->dr->id, sizeof(ictl.dr_id));
609 		memcpy(&ictl.dr_addr, &iface->dr->addr, sizeof(ictl.dr_addr));
610 	} else {
611 		bzero(&ictl.dr_id, sizeof(ictl.dr_id));
612 		bzero(&ictl.dr_addr, sizeof(ictl.dr_addr));
613 	}
614 	if (iface->bdr) {
615 		memcpy(&ictl.bdr_id, &iface->bdr->id, sizeof(ictl.bdr_id));
616 		memcpy(&ictl.bdr_addr, &iface->bdr->addr,
617 		    sizeof(ictl.bdr_addr));
618 	} else {
619 		bzero(&ictl.bdr_id, sizeof(ictl.bdr_id));
620 		bzero(&ictl.bdr_addr, sizeof(ictl.bdr_addr));
621 	}
622 	ictl.ifindex = iface->ifindex;
623 	ictl.state = iface->state;
624 	ictl.mtu = iface->mtu;
625 	ictl.nbr_cnt = 0;
626 	ictl.adj_cnt = 0;
627 	ictl.baudrate = iface->baudrate;
628 	ictl.dead_interval = iface->dead_interval;
629 	ictl.fast_hello_interval = iface->fast_hello_interval;
630 	ictl.transmit_delay = iface->transmit_delay;
631 	ictl.hello_interval = iface->hello_interval;
632 	ictl.flags = iface->flags;
633 	ictl.metric = iface->metric;
634 	ictl.rxmt_interval = iface->rxmt_interval;
635 	ictl.type = iface->type;
636 	ictl.linkstate = iface->linkstate;
637 	ictl.mediatype = iface->media_type;
638 	ictl.priority = iface->priority;
639 	ictl.passive = iface->passive;
640 	ictl.auth_type = iface->auth_type;
641 	ictl.auth_keyid = iface->auth_keyid;
642 
643 	gettimeofday(&now, NULL);
644 	if (evtimer_pending(&iface->hello_timer, &tv)) {
645 		timersub(&tv, &now, &res);
646 		ictl.hello_timer = res;
647 	} else {
648 		ictl.hello_timer.tv_sec = -1;
649 	}
650 
651 	if (iface->state != IF_STA_DOWN) {
652 		ictl.uptime = now.tv_sec - iface->uptime;
653 	} else
654 		ictl.uptime = 0;
655 
656 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
657 		if (nbr == iface->self)
658 			continue;
659 		ictl.nbr_cnt++;
660 		if (nbr->state & NBR_STA_ADJFORM)
661 			ictl.adj_cnt++;
662 	}
663 
664 	return (&ictl);
665 }
666 
667 /* misc */
668 int
669 if_set_recvif(int fd, int enable)
670 {
671 	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
672 	    sizeof(enable)) < 0) {
673 		log_warn("if_set_recvif: error setting IP_RECVIF");
674 		return (-1);
675 	}
676 	return (0);
677 }
678 
679 void
680 if_set_sockbuf(int fd)
681 {
682 	int	bsize;
683 
684 	bsize = 65535;
685 	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
686 	    sizeof(bsize)) == -1)
687 		bsize /= 2;
688 
689 	if (bsize != 65535)
690 		log_warnx("if_set_sockbuf: recvbuf size only %d", bsize);
691 
692 	bsize = 65535;
693 	while (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bsize,
694 	    sizeof(bsize)) == -1)
695 		bsize /= 2;
696 
697 	if (bsize != 65535)
698 		log_warnx("if_set_sockbuf: sendbuf size only %d", bsize);
699 }
700 
701 /*
702  * only one JOIN or DROP per interface and address is allowed so we need
703  * to keep track of what is added and removed.
704  */
705 struct if_group_count {
706 	LIST_ENTRY(if_group_count)	entry;
707 	struct in_addr			addr;
708 	unsigned int			ifindex;
709 	int				count;
710 };
711 
712 LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
713 
714 int
715 if_join_group(struct iface *iface, struct in_addr *addr)
716 {
717 	struct ip_mreq		 mreq;
718 	struct if_group_count	*ifg;
719 
720 	switch (iface->type) {
721 	case IF_TYPE_POINTOPOINT:
722 	case IF_TYPE_BROADCAST:
723 		LIST_FOREACH(ifg, &ifglist, entry)
724 			if (iface->ifindex == ifg->ifindex &&
725 			    addr->s_addr == ifg->addr.s_addr)
726 				break;
727 		if (ifg == NULL) {
728 			if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
729 				fatal("if_join_group");
730 			ifg->addr.s_addr = addr->s_addr;
731 			ifg->ifindex = iface->ifindex;
732 			LIST_INSERT_HEAD(&ifglist, ifg, entry);
733 		}
734 
735 		if (ifg->count++ != 0)
736 			/* already joined */
737 			return (0);
738 
739 		mreq.imr_multiaddr.s_addr = addr->s_addr;
740 		mreq.imr_interface.s_addr = iface->addr.s_addr;
741 
742 		if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
743 		    (void *)&mreq, sizeof(mreq)) < 0) {
744 			log_warn("if_join_group: error IP_ADD_MEMBERSHIP, "
745 			    "interface %s address %s", iface->name,
746 			    inet_ntoa(*addr));
747 			return (-1);
748 		}
749 		break;
750 	case IF_TYPE_POINTOMULTIPOINT:
751 	case IF_TYPE_VIRTUALLINK:
752 	case IF_TYPE_NBMA:
753 		log_debug("if_join_group: type %s not supported, interface %s",
754 		    if_type_name(iface->type), iface->name);
755 		return (-1);
756 	default:
757 		fatalx("if_join_group: unknown interface type");
758 	}
759 
760 	return (0);
761 }
762 
763 int
764 if_leave_group(struct iface *iface, struct in_addr *addr)
765 {
766 	struct ip_mreq		 mreq;
767 	struct if_group_count	*ifg;
768 
769 	switch (iface->type) {
770 	case IF_TYPE_POINTOPOINT:
771 	case IF_TYPE_BROADCAST:
772 		LIST_FOREACH(ifg, &ifglist, entry)
773 			if (iface->ifindex == ifg->ifindex &&
774 			    addr->s_addr == ifg->addr.s_addr)
775 				break;
776 
777 		/* if interface is not found just try to drop membership */
778 		if (ifg) {
779 			if (--ifg->count != 0)
780 				/* others still joined */
781 				return (0);
782 
783 			LIST_REMOVE(ifg, entry);
784 			free(ifg);
785 		}
786 
787 		mreq.imr_multiaddr.s_addr = addr->s_addr;
788 		mreq.imr_interface.s_addr = iface->addr.s_addr;
789 
790 		if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
791 		    (void *)&mreq, sizeof(mreq)) < 0) {
792 			log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, "
793 			    "interface %s address %s", iface->name,
794 			    inet_ntoa(*addr));
795 			return (-1);
796 		}
797 		break;
798 	case IF_TYPE_POINTOMULTIPOINT:
799 	case IF_TYPE_VIRTUALLINK:
800 	case IF_TYPE_NBMA:
801 		log_debug("if_leave_group: type %s not supported, interface %s",
802 		    if_type_name(iface->type), iface->name);
803 		return (-1);
804 	default:
805 		fatalx("if_leave_group: unknown interface type");
806 	}
807 
808 	return (0);
809 }
810 
811 int
812 if_set_mcast(struct iface *iface)
813 {
814 	switch (iface->type) {
815 	case IF_TYPE_POINTOPOINT:
816 	case IF_TYPE_BROADCAST:
817 		if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF,
818 		    &iface->addr.s_addr, sizeof(iface->addr.s_addr)) < 0) {
819 			log_warn("if_set_mcast: error setting "
820 			    "IP_MULTICAST_IF, interface %s", iface->name);
821 			return (-1);
822 		}
823 		break;
824 	case IF_TYPE_POINTOMULTIPOINT:
825 	case IF_TYPE_VIRTUALLINK:
826 	case IF_TYPE_NBMA:
827 		log_debug("if_set_mcast: type %s not supported, interface %s",
828 		    if_type_name(iface->type), iface->name);
829 		return (-1);
830 	default:
831 		fatalx("if_set_mcast: unknown interface type");
832 	}
833 
834 	return (0);
835 }
836 
837 int
838 if_set_mcast_loop(int fd)
839 {
840 	u_int8_t	loop = 0;
841 
842 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
843 	    (char *)&loop, sizeof(loop)) < 0) {
844 		log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
845 		return (-1);
846 	}
847 
848 	return (0);
849 }
850 
851 int
852 if_set_ip_hdrincl(int fd)
853 {
854 	int	hincl = 1;
855 
856 	if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) {
857 		log_warn("if_set_ip_hdrincl: error setting IP_HDRINCL");
858 		return (-1);
859 	}
860 
861 	return (0);
862 }
863