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