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