xref: /openbsd-src/usr.sbin/eigrpd/interface.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: interface.c,v 1.23 2016/09/02 16:44:33 renato Exp $ */
2 
3 /*
4  * Copyright (c) 2015 Renato Westphal <renato@openbsd.org>
5  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
6  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/ioctl.h>
24 #include <arpa/inet.h>
25 
26 #include <ctype.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "eigrpd.h"
32 #include "eigrpe.h"
33 #include "log.h"
34 
35 static __inline int	 iface_id_compare(struct eigrp_iface *,
36 			    struct eigrp_iface *);
37 static struct iface	*if_new(struct eigrpd_conf *, struct kif *);
38 static void		 if_del(struct iface *);
39 static struct if_addr	*if_addr_lookup(struct if_addr_head *, struct kaddr *);
40 static void		 eigrp_if_start(struct eigrp_iface *);
41 static void		 eigrp_if_reset(struct eigrp_iface *);
42 static void		 eigrp_if_hello_timer(int, short, void *);
43 static void		 eigrp_if_start_hello_timer(struct eigrp_iface *);
44 static void		 eigrp_if_stop_hello_timer(struct eigrp_iface *);
45 static int		 if_join_ipv4_group(struct iface *, struct in_addr *);
46 static int		 if_leave_ipv4_group(struct iface *, struct in_addr *);
47 static int		 if_join_ipv6_group(struct iface *, struct in6_addr *);
48 static int		 if_leave_ipv6_group(struct iface *, struct in6_addr *);
49 
50 RB_GENERATE(iface_id_head, eigrp_iface, id_tree, iface_id_compare)
51 
52 struct iface_id_head ifaces_by_id = RB_INITIALIZER(&ifaces_by_id);
53 
54 static __inline int
55 iface_id_compare(struct eigrp_iface *a, struct eigrp_iface *b)
56 {
57 	return (a->ifaceid - b->ifaceid);
58 }
59 
60 static struct iface *
61 if_new(struct eigrpd_conf *xconf, struct kif *kif)
62 {
63 	struct iface		*iface;
64 
65 	if ((iface = calloc(1, sizeof(*iface))) == NULL)
66 		fatal("if_new: calloc");
67 
68 	TAILQ_INIT(&iface->ei_list);
69 	TAILQ_INIT(&iface->addr_list);
70 
71 	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
72 
73 	/* get type */
74 	if (kif->flags & IFF_POINTOPOINT)
75 		iface->type = IF_TYPE_POINTOPOINT;
76 	if (kif->flags & IFF_BROADCAST &&
77 	    kif->flags & IFF_MULTICAST)
78 		iface->type = IF_TYPE_BROADCAST;
79 	if (kif->flags & IFF_LOOPBACK)
80 		iface->type = IF_TYPE_POINTOPOINT;
81 
82 	/* get index and flags */
83 	iface->mtu = kif->mtu;
84 	iface->ifindex = kif->ifindex;
85 	iface->flags = kif->flags;
86 	iface->linkstate = kif->link_state;
87 	iface->if_type = kif->if_type;
88 	iface->baudrate = kif->baudrate;
89 
90 	TAILQ_INSERT_TAIL(&xconf->iface_list, iface, entry);
91 
92 	return (iface);
93 }
94 
95 static void
96 if_del(struct iface *iface)
97 {
98 	struct if_addr		*if_addr;
99 
100 	log_debug("%s: interface %s", __func__, iface->name);
101 
102 	while ((if_addr = TAILQ_FIRST(&iface->addr_list)) != NULL) {
103 		TAILQ_REMOVE(&iface->addr_list, if_addr, entry);
104 		free(if_addr);
105 	}
106 
107 	TAILQ_REMOVE(&econf->iface_list, iface, entry);
108 	free(iface);
109 }
110 
111 struct iface *
112 if_lookup(struct eigrpd_conf *xconf, unsigned int ifindex)
113 {
114 	struct iface	*iface;
115 
116 	TAILQ_FOREACH(iface, &xconf->iface_list, entry)
117 		if (iface->ifindex == ifindex)
118 			return (iface);
119 
120 	return (NULL);
121 }
122 
123 void
124 if_init(struct eigrpd_conf *xconf, struct iface *iface)
125 {
126 	struct ifreq		 ifr;
127 	unsigned int		 rdomain;
128 
129 	/* set rdomain */
130 	strlcpy(ifr.ifr_name, iface->name, sizeof(ifr.ifr_name));
131 	if (ioctl(global.eigrp_socket_v4, SIOCGIFRDOMAIN, (caddr_t)&ifr) == -1)
132 		rdomain = 0;
133 	else {
134 		rdomain = ifr.ifr_rdomainid;
135 		if (setsockopt(global.eigrp_socket_v4, SOL_SOCKET, SO_RTABLE,
136 		    &rdomain, sizeof(rdomain)) == -1)
137 			fatal("failed to set rdomain");
138 	}
139 	if (rdomain != xconf->rdomain)
140 		fatalx("interface rdomain mismatch");
141 }
142 
143 void
144 if_addr_new(struct iface *iface, struct kaddr *ka)
145 {
146 	struct if_addr		*if_addr;
147 	struct eigrp_iface	*ei;
148 
149 	if (ka->af == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&ka->addr.v6)) {
150 		iface->linklocal = ka->addr.v6;
151 		if_update(iface, AF_INET6);
152 		return;
153 	}
154 
155 	if (if_addr_lookup(&iface->addr_list, ka) != NULL)
156 		return;
157 
158 	if ((if_addr = calloc(1, sizeof(*if_addr))) == NULL)
159 		fatal("if_addr_new: calloc");
160 	if_addr->af = ka->af;
161 	if_addr->addr = ka->addr;
162 	if_addr->prefixlen = ka->prefixlen;
163 	if_addr->dstbrd = ka->dstbrd;
164 	TAILQ_INSERT_TAIL(&iface->addr_list, if_addr, entry);
165 
166 	TAILQ_FOREACH(ei, &iface->ei_list, i_entry)
167 		if (ei->state == IF_STA_ACTIVE && ei->eigrp->af == if_addr->af)
168 			eigrpe_orig_local_route(ei, if_addr, 0);
169 
170 	if (if_addr->af == AF_INET)
171 		if_update(iface, AF_INET);
172 }
173 
174 void
175 if_addr_del(struct iface *iface, struct kaddr *ka)
176 {
177 	struct if_addr		*if_addr;
178 	struct eigrp_iface	*ei;
179 	int			 af = ka->af;
180 
181 	if (ka->af == AF_INET6 &&
182 	    IN6_ARE_ADDR_EQUAL(&iface->linklocal, &ka->addr.v6)) {
183 		memset(&iface->linklocal, 0, sizeof(iface->linklocal));
184 		if_update(iface, AF_INET6);
185 		return;
186 	}
187 
188 	if_addr = if_addr_lookup(&iface->addr_list, ka);
189 	if (if_addr == NULL)
190 		return;
191 
192 	TAILQ_FOREACH(ei, &iface->ei_list, i_entry)
193 		if (ei->state == IF_STA_ACTIVE && ei->eigrp->af == if_addr->af)
194 			eigrpe_orig_local_route(ei, if_addr, 1);
195 
196 	TAILQ_REMOVE(&iface->addr_list, if_addr, entry);
197 	free(if_addr);
198 
199 	if (af == AF_INET)
200 		if_update(iface, AF_INET);
201 }
202 
203 static struct if_addr *
204 if_addr_lookup(struct if_addr_head *addr_list, struct kaddr *ka)
205 {
206 	struct if_addr	*if_addr;
207 	int		 af = ka->af;
208 
209 	TAILQ_FOREACH(if_addr, addr_list, entry)
210 		if (!eigrp_addrcmp(af, &if_addr->addr, &ka->addr) &&
211 		    if_addr->prefixlen == ka->prefixlen &&
212 		    !eigrp_addrcmp(af, &if_addr->dstbrd, &ka->dstbrd))
213 			return (if_addr);
214 
215 	return (NULL);
216 }
217 
218 in_addr_t
219 if_primary_addr(struct iface *iface)
220 {
221 	struct if_addr	*if_addr;
222 
223 	TAILQ_FOREACH(if_addr, &iface->addr_list, entry)
224 		if (if_addr->af == AF_INET)
225 			return (if_addr->addr.v4.s_addr);
226 
227 	return (INADDR_ANY);
228 }
229 
230 uint8_t
231 if_primary_addr_prefixlen(struct iface *iface)
232 {
233 	struct if_addr	*if_addr;
234 
235 	TAILQ_FOREACH(if_addr, &iface->addr_list, entry)
236 		if (if_addr->af == AF_INET)
237 			return (if_addr->prefixlen);
238 
239 	return (0);
240 }
241 
242 /* up/down events */
243 void
244 if_update(struct iface *iface, int af)
245 {
246 	struct eigrp_iface	*ei;
247 	int			 link_ok;
248 	int			 addr_ok, addr4_ok = 0, addr6_ok = 0;
249 	struct if_addr		*if_addr;
250 
251 	link_ok = (iface->flags & IFF_UP) &&
252 	    LINK_STATE_IS_UP(iface->linkstate);
253 
254 	/*
255 	 * NOTE: for EIGRPv4, each interface should have at least one valid
256 	 * IP address otherwise they can not be enabled in the routing domain.
257 	 */
258 	TAILQ_FOREACH(if_addr, &iface->addr_list, entry) {
259 		if (if_addr->af == AF_INET) {
260 			addr4_ok = 1;
261 			break;
262 		}
263 	}
264 	/* for IPv6 the link-local address is enough. */
265 	if (IN6_IS_ADDR_LINKLOCAL(&iface->linklocal))
266 		addr6_ok = 1;
267 
268 	TAILQ_FOREACH(ei, &iface->ei_list, i_entry) {
269 		if (af != AF_UNSPEC && ei->eigrp->af != af)
270 			continue;
271 
272 		switch (ei->eigrp->af) {
273 		case AF_INET:
274 			addr_ok = addr4_ok;
275 			break;
276 		case AF_INET6:
277 			addr_ok = addr6_ok;
278 			break;
279 		default:
280 			fatalx("if_update: unknown af");
281 		}
282 
283 		if (ei->state == IF_STA_DOWN) {
284 			if (!link_ok || !addr_ok)
285 				continue;
286 			ei->state = IF_STA_ACTIVE;
287 			eigrp_if_start(ei);
288 		} else if (ei->state == IF_STA_ACTIVE) {
289 			if (link_ok && addr_ok)
290 				continue;
291 			ei->state = IF_STA_DOWN;
292 			eigrp_if_reset(ei);
293 		}
294 	}
295 }
296 
297 struct eigrp_iface *
298 eigrp_if_new(struct eigrpd_conf *xconf, struct eigrp *eigrp, struct kif *kif)
299 {
300 	struct iface		*iface;
301 	struct eigrp_iface	*ei;
302 	static uint32_t		 ifacecnt = 1;
303 
304 	iface = if_lookup(xconf, kif->ifindex);
305 	if (iface == NULL)
306 		iface = if_new(xconf, kif);
307 
308 	if ((ei = calloc(1, sizeof(*ei))) == NULL)
309 		fatal("eigrp_if_new: calloc");
310 
311 	ei->state = IF_STA_DOWN;
312 	/* get next unused ifaceid */
313 	while (eigrp_if_lookup_id(ifacecnt++))
314 		;
315 	ei->ifaceid = ifacecnt;
316 	ei->eigrp = eigrp;
317 	ei->iface = iface;
318 	if (ei->iface->flags & IFF_LOOPBACK)
319 		ei->passive = 1;
320 
321 	TAILQ_INIT(&ei->nbr_list);
322 	TAILQ_INIT(&ei->update_list);
323 	TAILQ_INIT(&ei->query_list);
324 	TAILQ_INIT(&ei->summary_list);
325 	TAILQ_INSERT_TAIL(&iface->ei_list, ei, i_entry);
326 	TAILQ_INSERT_TAIL(&eigrp->ei_list, ei, e_entry);
327 	if (RB_INSERT(iface_id_head, &ifaces_by_id, ei) != NULL)
328 		fatalx("eigrp_if_new: RB_INSERT(ifaces_by_id) failed");
329 
330 	return (ei);
331 }
332 
333 void
334 eigrp_if_del(struct eigrp_iface *ei)
335 {
336 	struct summary_addr	*summary;
337 
338 	RB_REMOVE(iface_id_head, &ifaces_by_id, ei);
339 	TAILQ_REMOVE(&ei->eigrp->ei_list, ei, e_entry);
340 	TAILQ_REMOVE(&ei->iface->ei_list, ei, i_entry);
341 	while ((summary = TAILQ_FIRST(&ei->summary_list)) != NULL) {
342 		TAILQ_REMOVE(&ei->summary_list, summary, entry);
343 		free(summary);
344 	}
345 	message_list_clr(&ei->query_list);
346 	message_list_clr(&ei->update_list);
347 
348 	if (ei->state == IF_STA_ACTIVE)
349 		eigrp_if_reset(ei);
350 
351 	if (TAILQ_EMPTY(&ei->iface->ei_list))
352 		if_del(ei->iface);
353 
354 	free(ei);
355 }
356 
357 struct eigrp_iface *
358 eigrp_if_lookup(struct iface *iface, int af, uint16_t as)
359 {
360 	struct eigrp_iface	*ei;
361 
362 	TAILQ_FOREACH(ei, &iface->ei_list, i_entry)
363 		if (ei->eigrp->af == af &&
364 		    ei->eigrp->as == as)
365 			return (ei);
366 
367 	return (NULL);
368 }
369 
370 struct eigrp_iface *
371 eigrp_if_lookup_id(uint32_t ifaceid)
372 {
373 	struct eigrp_iface	 e;
374 	e.ifaceid = ifaceid;
375 	return (RB_FIND(iface_id_head, &ifaces_by_id, &e));
376 }
377 
378 static void
379 eigrp_if_start(struct eigrp_iface *ei)
380 {
381 	struct eigrp		*eigrp = ei->eigrp;
382 	struct timeval		 now;
383 	struct if_addr		*if_addr;
384 	union eigrpd_addr	 addr;
385 
386 	log_debug("%s: %s as %u family %s", __func__, ei->iface->name,
387 	    eigrp->as, af_name(eigrp->af));
388 
389 	gettimeofday(&now, NULL);
390 	ei->uptime = now.tv_sec;
391 
392 	/* init the dummy self neighbor */
393 	memset(&addr, 0, sizeof(addr));
394 	ei->self = nbr_new(ei, &addr, 0, 1);
395 	nbr_init(ei->self);
396 
397 	TAILQ_FOREACH(if_addr, &ei->iface->addr_list, entry) {
398 		if (if_addr->af != eigrp->af)
399 			continue;
400 
401 		eigrpe_orig_local_route(ei, if_addr, 0);
402 	}
403 
404 	if (ei->passive)
405 		return;
406 
407 	switch (eigrp->af) {
408 	case AF_INET:
409 		if (if_join_ipv4_group(ei->iface, &global.mcast_addr_v4))
410 			return;
411 		break;
412 	case AF_INET6:
413 		if (if_join_ipv6_group(ei->iface, &global.mcast_addr_v6))
414 			return;
415 		break;
416 	default:
417 		fatalx("eigrp_if_start: unknown af");
418 	}
419 
420 	evtimer_set(&ei->hello_timer, eigrp_if_hello_timer, ei);
421 	eigrp_if_start_hello_timer(ei);
422 }
423 
424 static void
425 eigrp_if_reset(struct eigrp_iface *ei)
426 {
427 	struct eigrp		*eigrp = ei->eigrp;
428 	struct nbr		*nbr;
429 
430 	log_debug("%s: %s as %u family %s", __func__, ei->iface->name,
431 	    eigrp->as, af_name(eigrp->af));
432 
433 	/* the rde will withdraw the connected route for us */
434 
435 	while ((nbr = TAILQ_FIRST(&ei->nbr_list)) != NULL)
436 		nbr_del(nbr);
437 
438 	if (ei->passive)
439 		return;
440 
441 	/* try to cleanup */
442 	switch (eigrp->af) {
443 	case AF_INET:
444 		if_leave_ipv4_group(ei->iface, &global.mcast_addr_v4);
445 		break;
446 	case AF_INET6:
447 		if_leave_ipv6_group(ei->iface, &global.mcast_addr_v6);
448 		break;
449 	default:
450 		fatalx("eigrp_if_reset: unknown af");
451 	}
452 
453 	eigrp_if_stop_hello_timer(ei);
454 }
455 
456 /* timers */
457 /* ARGSUSED */
458 static void
459 eigrp_if_hello_timer(int fd, short event, void *arg)
460 {
461 	struct eigrp_iface	*ei = arg;
462 	struct timeval		 tv;
463 
464 	send_hello(ei, NULL, 0);
465 
466 	/* reschedule hello_timer */
467 	timerclear(&tv);
468 	tv.tv_sec = ei->hello_interval;
469 	if (evtimer_add(&ei->hello_timer, &tv) == -1)
470 		fatal("eigrp_if_hello_timer");
471 }
472 
473 static void
474 eigrp_if_start_hello_timer(struct eigrp_iface *ei)
475 {
476 	struct timeval		 tv;
477 
478 	timerclear(&tv);
479 	tv.tv_sec = ei->hello_interval;
480 	if (evtimer_add(&ei->hello_timer, &tv) == -1)
481 		fatal("eigrp_if_start_hello_timer");
482 }
483 
484 static void
485 eigrp_if_stop_hello_timer(struct eigrp_iface *ei)
486 {
487 	if (evtimer_pending(&ei->hello_timer, NULL) &&
488 	    evtimer_del(&ei->hello_timer) == -1)
489 		fatal("eigrp_if_stop_hello_timer");
490 }
491 
492 struct ctl_iface *
493 if_to_ctl(struct eigrp_iface *ei)
494 {
495 	static struct ctl_iface	 ictl;
496 	struct timeval		 now;
497 	struct nbr		*nbr;
498 
499 	ictl.af = ei->eigrp->af;
500 	ictl.as = ei->eigrp->as;
501 	memcpy(ictl.name, ei->iface->name, sizeof(ictl.name));
502 	ictl.ifindex = ei->iface->ifindex;
503 	switch (ei->eigrp->af) {
504 	case AF_INET:
505 		ictl.addr.v4.s_addr = if_primary_addr(ei->iface);
506 		ictl.prefixlen = if_primary_addr_prefixlen(ei->iface);
507 		break;
508 	case AF_INET6:
509 		ictl.addr.v6 = ei->iface->linklocal;
510 		if (!IN6_IS_ADDR_UNSPECIFIED(&ei->iface->linklocal))
511 			ictl.prefixlen = 64;
512 		else
513 			ictl.prefixlen = 0;
514 		break;
515 	default:
516 		fatalx("if_to_ctl: unknown af");
517 	}
518 	ictl.flags = ei->iface->flags;
519 	ictl.linkstate = ei->iface->linkstate;
520 	ictl.mtu = ei->iface->mtu;
521 	ictl.type = ei->iface->type;
522 	ictl.if_type = ei->iface->if_type;
523 	ictl.baudrate = ei->iface->baudrate;
524 	ictl.delay = ei->delay;
525 	ictl.bandwidth = ei->bandwidth;
526 	ictl.hello_holdtime = ei->hello_holdtime;
527 	ictl.hello_interval = ei->hello_interval;
528 	ictl.splithorizon = ei->splithorizon;
529 	ictl.passive = ei->passive;
530 	ictl.nbr_cnt = 0;
531 
532 	gettimeofday(&now, NULL);
533 	if (ei->state != IF_STA_DOWN && ei->uptime != 0)
534 		ictl.uptime = now.tv_sec - ei->uptime;
535 	else
536 		ictl.uptime = 0;
537 
538 	TAILQ_FOREACH(nbr, &ei->nbr_list, entry)
539 		if (!(nbr->flags & (F_EIGRP_NBR_PENDING|F_EIGRP_NBR_SELF)))
540 			ictl.nbr_cnt++;
541 
542 	return (&ictl);
543 }
544 
545 /* misc */
546 void
547 if_set_sockbuf(int fd)
548 {
549 	int	bsize;
550 
551 	bsize = 65535;
552 	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
553 	    sizeof(bsize)) == -1)
554 		bsize /= 2;
555 
556 	if (bsize != 65535)
557 		log_warnx("%s: recvbuf size only %d", __func__, bsize);
558 
559 	bsize = 65535;
560 	while (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bsize,
561 	    sizeof(bsize)) == -1)
562 		bsize /= 2;
563 
564 	if (bsize != 65535)
565 		log_warnx("%s: sendbuf size only %d", __func__, bsize);
566 }
567 
568 static int
569 if_join_ipv4_group(struct iface *iface, struct in_addr *addr)
570 {
571 	struct ip_mreq		 mreq;
572 
573 	if (iface->group_count_v4++ != 0)
574 		/* already joined */
575 		return (0);
576 
577 	log_debug("%s: interface %s addr %s", __func__, iface->name,
578 	    inet_ntoa(*addr));
579 
580 	mreq.imr_multiaddr = *addr;
581 	mreq.imr_interface.s_addr = if_primary_addr(iface);
582 
583 	if (setsockopt(global.eigrp_socket_v4, IPPROTO_IP, IP_ADD_MEMBERSHIP,
584 	    (void *)&mreq, sizeof(mreq)) < 0) {
585 		log_warn("%s: error IP_ADD_MEMBERSHIP, interface %s address %s",
586 		    __func__, iface->name, inet_ntoa(*addr));
587 		return (-1);
588 	}
589 
590 	return (0);
591 }
592 
593 static int
594 if_leave_ipv4_group(struct iface *iface, struct in_addr *addr)
595 {
596 	struct ip_mreq		 mreq;
597 
598 	if (--iface->group_count_v4 != 0)
599 		/* others still joined */
600 		return (0);
601 
602 	log_debug("%s: interface %s addr %s", __func__, iface->name,
603 	    inet_ntoa(*addr));
604 
605 	mreq.imr_multiaddr = *addr;
606 	mreq.imr_interface.s_addr = if_primary_addr(iface);
607 
608 	if (setsockopt(global.eigrp_socket_v4, IPPROTO_IP, IP_DROP_MEMBERSHIP,
609 	    (void *)&mreq, sizeof(mreq)) < 0) {
610 		log_warn("%s: error IP_DROP_MEMBERSHIP, interface %s "
611 		    "address %s", iface->name, __func__, inet_ntoa(*addr));
612 		return (-1);
613 	}
614 
615 	return (0);
616 }
617 
618 int
619 if_set_ipv4_mcast_ttl(int fd, uint8_t ttl)
620 {
621 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
622 	    (char *)&ttl, sizeof(ttl)) < 0) {
623 		log_warn("%s: error setting IP_MULTICAST_TTL to %d",
624 		    __func__, ttl);
625 		return (-1);
626 	}
627 
628 	return (0);
629 }
630 
631 int
632 if_set_ipv4_mcast(struct iface *iface)
633 {
634 	in_addr_t	 addr;
635 
636 	addr = if_primary_addr(iface);
637 
638 	if (setsockopt(global.eigrp_socket_v4, IPPROTO_IP, IP_MULTICAST_IF,
639 	    &addr, sizeof(addr)) < 0) {
640 		log_warn("%s: error setting IP_MULTICAST_IF, interface %s",
641 		    __func__, iface->name);
642 		return (-1);
643 	}
644 
645 	return (0);
646 }
647 
648 int
649 if_set_ipv4_mcast_loop(int fd)
650 {
651 	uint8_t	loop = 0;
652 
653 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
654 	    (char *)&loop, sizeof(loop)) < 0) {
655 		log_warn("%s: error setting IP_MULTICAST_LOOP", __func__);
656 		return (-1);
657 	}
658 
659 	return (0);
660 }
661 
662 int
663 if_set_ipv4_recvif(int fd, int enable)
664 {
665 	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
666 	    sizeof(enable)) < 0) {
667 		log_warn("%s: error setting IP_RECVIF", __func__);
668 		return (-1);
669 	}
670 	return (0);
671 }
672 
673 int
674 if_set_ipv4_hdrincl(int fd)
675 {
676 	int	hincl = 1;
677 
678 	if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) < 0) {
679 		log_warn("%s: error setting IP_HDRINCL", __func__);
680 		return (-1);
681 	}
682 
683 	return (0);
684 }
685 
686 static int
687 if_join_ipv6_group(struct iface *iface, struct in6_addr *addr)
688 {
689 	struct ipv6_mreq	 mreq;
690 
691 	if (iface->group_count_v6++ != 0)
692 		/* already joined */
693 		return (0);
694 
695 	log_debug("%s: interface %s addr %s", __func__, iface->name,
696 	    log_in6addr(addr));
697 
698 	mreq.ipv6mr_multiaddr = *addr;
699 	mreq.ipv6mr_interface = iface->ifindex;
700 
701 	if (setsockopt(global.eigrp_socket_v6, IPPROTO_IPV6, IPV6_JOIN_GROUP,
702 	    &mreq, sizeof(mreq)) < 0) {
703 		log_warn("%s: error IPV6_JOIN_GROUP, interface %s address %s",
704 		    __func__, iface->name, log_in6addr(addr));
705 		return (-1);
706 	}
707 
708 	return (0);
709 }
710 
711 static int
712 if_leave_ipv6_group(struct iface *iface, struct in6_addr *addr)
713 {
714 	struct ipv6_mreq	 mreq;
715 
716 	if (--iface->group_count_v6 != 0)
717 		/* others still joined */
718 		return (0);
719 
720 	log_debug("%s: interface %s addr %s", __func__, iface->name,
721 	    log_in6addr(addr));
722 
723 	mreq.ipv6mr_multiaddr = *addr;
724 	mreq.ipv6mr_interface = iface->ifindex;
725 
726 	if (setsockopt(global.eigrp_socket_v6, IPPROTO_IPV6, IPV6_LEAVE_GROUP,
727 	    (void *)&mreq, sizeof(mreq)) < 0) {
728 		log_warn("%s: error IPV6_LEAVE_GROUP, interface %s address %s",
729 		    __func__, iface->name, log_in6addr(addr));
730 		return (-1);
731 	}
732 
733 	return (0);
734 }
735 
736 int
737 if_set_ipv6_mcast(struct iface *iface)
738 {
739 	if (setsockopt(global.eigrp_socket_v6, IPPROTO_IPV6, IPV6_MULTICAST_IF,
740 	    &iface->ifindex, sizeof(iface->ifindex)) < 0) {
741 		log_warn("%s: error setting IPV6_MULTICAST_IF, interface %s",
742 		    __func__, iface->name);
743 		return (-1);
744 	}
745 
746 	return (0);
747 }
748 
749 int
750 if_set_ipv6_mcast_loop(int fd)
751 {
752 	unsigned int	loop = 0;
753 
754 	if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
755 	    (unsigned int *)&loop, sizeof(loop)) < 0) {
756 		log_warn("%s: error setting IPV6_MULTICAST_LOOP", __func__);
757 		return (-1);
758 	}
759 
760 	return (0);
761 }
762 
763 int
764 if_set_ipv6_pktinfo(int fd, int enable)
765 {
766 	if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &enable,
767 	    sizeof(enable)) < 0) {
768 		log_warn("%s: error setting IPV6_RECVPKTINFO", __func__);
769 		return (-1);
770 	}
771 
772 	return (0);
773 }
774 
775 int
776 if_set_ipv6_dscp(int fd, int dscp)
777 {
778 	if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, &dscp,
779 	    sizeof(dscp)) < 0) {
780 		log_warn("%s: error setting IPV6_TCLASS", __func__);
781 		return (-1);
782 	}
783 
784 	return (0);
785 }
786