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