xref: /openbsd-src/usr.sbin/ospfd/interface.c (revision 4f4fe40bc9d06de09f4af934b87299ab1ee3cc66)
1*4f4fe40bSflorian /*	$OpenBSD: interface.c,v 1.88 2024/08/21 15:18:00 florian Exp $ */
2204df0f8Sclaudio 
3204df0f8Sclaudio /*
4204df0f8Sclaudio  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5367f601bSnorby  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6204df0f8Sclaudio  *
7204df0f8Sclaudio  * Permission to use, copy, modify, and distribute this software for any
8204df0f8Sclaudio  * purpose with or without fee is hereby granted, provided that the above
9204df0f8Sclaudio  * copyright notice and this permission notice appear in all copies.
10204df0f8Sclaudio  *
11204df0f8Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12204df0f8Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13204df0f8Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14204df0f8Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15204df0f8Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16204df0f8Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17204df0f8Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18204df0f8Sclaudio  */
19204df0f8Sclaudio 
20204df0f8Sclaudio #include <sys/types.h>
21204df0f8Sclaudio #include <sys/ioctl.h>
22204df0f8Sclaudio #include <sys/time.h>
23204df0f8Sclaudio #include <sys/socket.h>
24204df0f8Sclaudio #include <netinet/in.h>
25204df0f8Sclaudio #include <arpa/inet.h>
26204df0f8Sclaudio #include <net/if.h>
27d7a91d7eSclaudio #include <net/if_types.h>
28204df0f8Sclaudio #include <ctype.h>
29204df0f8Sclaudio #include <err.h>
30204df0f8Sclaudio #include <stdio.h>
31204df0f8Sclaudio #include <stdlib.h>
32204df0f8Sclaudio #include <unistd.h>
33204df0f8Sclaudio #include <string.h>
34204df0f8Sclaudio #include <event.h>
35204df0f8Sclaudio 
36204df0f8Sclaudio #include "ospfd.h"
37204df0f8Sclaudio #include "ospf.h"
38204df0f8Sclaudio #include "log.h"
39204df0f8Sclaudio #include "ospfe.h"
40204df0f8Sclaudio 
41204df0f8Sclaudio void		 if_hello_timer(int, short, void *);
423aede346Sclaudio void		 if_start_hello_timer(struct iface *);
433aede346Sclaudio void		 if_stop_hello_timer(struct iface *);
443aede346Sclaudio void		 if_stop_wait_timer(struct iface *);
45204df0f8Sclaudio void		 if_wait_timer(int, short, void *);
463aede346Sclaudio void		 if_start_wait_timer(struct iface *);
473aede346Sclaudio void		 if_stop_wait_timer(struct iface *);
48204df0f8Sclaudio struct nbr	*if_elect(struct nbr *, struct nbr *);
49204df0f8Sclaudio 
50204df0f8Sclaudio struct {
51204df0f8Sclaudio 	int			state;
52204df0f8Sclaudio 	enum iface_event	event;
53204df0f8Sclaudio 	enum iface_action	action;
54204df0f8Sclaudio 	int			new_state;
55204df0f8Sclaudio } iface_fsm[] = {
56204df0f8Sclaudio     /* current state	event that happened	action to take	resulting state */
57204df0f8Sclaudio     {IF_STA_DOWN,	IF_EVT_UP,		IF_ACT_STRT,	0},
58204df0f8Sclaudio     {IF_STA_WAITING,	IF_EVT_BACKUP_SEEN,	IF_ACT_ELECT,	0},
59204df0f8Sclaudio     {IF_STA_WAITING,	IF_EVT_WTIMER,		IF_ACT_ELECT,	0},
60bcecdf6fSclaudio     {IF_STA_ANY,	IF_EVT_WTIMER,		IF_ACT_NOTHING,	0},
611aaf2c2dSclaudio     {IF_STA_WAITING,	IF_EVT_NBR_CHNG,	IF_ACT_NOTHING,	0},
62204df0f8Sclaudio     {IF_STA_MULTI,	IF_EVT_NBR_CHNG,	IF_ACT_ELECT,	0},
631aaf2c2dSclaudio     {IF_STA_ANY,	IF_EVT_NBR_CHNG,	IF_ACT_NOTHING,	0},
64204df0f8Sclaudio     {IF_STA_ANY,	IF_EVT_DOWN,		IF_ACT_RST,	IF_STA_DOWN},
65204df0f8Sclaudio     {IF_STA_ANY,	IF_EVT_LOOP,		IF_ACT_RST,	IF_STA_LOOPBACK},
661aaf2c2dSclaudio     {IF_STA_LOOPBACK,	IF_EVT_UNLOOP,		IF_ACT_NOTHING,	IF_STA_DOWN},
671aaf2c2dSclaudio     {-1,		IF_EVT_NOTHING,		IF_ACT_NOTHING,	0},
68204df0f8Sclaudio };
69204df0f8Sclaudio 
70ec4d3955Snorby static int vlink_cnt = 0;
71ec4d3955Snorby 
72f769f51dSclaudio const char * const if_event_names[] = {
73f769f51dSclaudio 	"NOTHING",
74f769f51dSclaudio 	"UP",
75f769f51dSclaudio 	"WAITTIMER",
76f769f51dSclaudio 	"BACKUPSEEN",
77f769f51dSclaudio 	"NEIGHBORCHANGE",
78f769f51dSclaudio 	"LOOP",
79f769f51dSclaudio 	"UNLOOP",
80f769f51dSclaudio 	"DOWN"
81f769f51dSclaudio };
82f769f51dSclaudio 
83204df0f8Sclaudio const char * const if_action_names[] = {
841aaf2c2dSclaudio 	"NOTHING",
85204df0f8Sclaudio 	"START",
86204df0f8Sclaudio 	"ELECT",
87204df0f8Sclaudio 	"RESET"
88204df0f8Sclaudio };
89204df0f8Sclaudio 
90204df0f8Sclaudio int
91204df0f8Sclaudio if_fsm(struct iface *iface, enum iface_event event)
92204df0f8Sclaudio {
93204df0f8Sclaudio 	int	old_state;
94204df0f8Sclaudio 	int	new_state = 0;
95204df0f8Sclaudio 	int	i, ret = 0;
96204df0f8Sclaudio 
97204df0f8Sclaudio 	old_state = iface->state;
98204df0f8Sclaudio 
99204df0f8Sclaudio 	for (i = 0; iface_fsm[i].state != -1; i++)
100204df0f8Sclaudio 		if ((iface_fsm[i].state & old_state) &&
101204df0f8Sclaudio 		    (iface_fsm[i].event == event)) {
102204df0f8Sclaudio 			new_state = iface_fsm[i].new_state;
103204df0f8Sclaudio 			break;
104204df0f8Sclaudio 		}
105204df0f8Sclaudio 
106204df0f8Sclaudio 	if (iface_fsm[i].state == -1) {
107aeb9d7c6Sclaudio 		/* event outside of the defined fsm, ignore it. */
1085d021e83Smsf 		log_debug("if_fsm: interface %s, "
109204df0f8Sclaudio 		    "event %s not expected in state %s", iface->name,
110cdd38203Sclaudio 		    if_event_names[event], if_state_name(old_state));
111204df0f8Sclaudio 		return (0);
112204df0f8Sclaudio 	}
113204df0f8Sclaudio 
114204df0f8Sclaudio 	switch (iface_fsm[i].action) {
115204df0f8Sclaudio 	case IF_ACT_STRT:
116204df0f8Sclaudio 		ret = if_act_start(iface);
117204df0f8Sclaudio 		break;
118204df0f8Sclaudio 	case IF_ACT_ELECT:
119204df0f8Sclaudio 		ret = if_act_elect(iface);
120204df0f8Sclaudio 		break;
121204df0f8Sclaudio 	case IF_ACT_RST:
122204df0f8Sclaudio 		ret = if_act_reset(iface);
123204df0f8Sclaudio 		break;
1241aaf2c2dSclaudio 	case IF_ACT_NOTHING:
125204df0f8Sclaudio 		/* do nothing */
126204df0f8Sclaudio 		break;
127204df0f8Sclaudio 	}
128204df0f8Sclaudio 
129204df0f8Sclaudio 	if (ret) {
1305d021e83Smsf 		log_debug("if_fsm: error changing state for interface %s, "
131cdd38203Sclaudio 		    "event %s, state %s", iface->name, if_event_names[event],
132204df0f8Sclaudio 		    if_state_name(old_state));
133204df0f8Sclaudio 		return (-1);
134204df0f8Sclaudio 	}
135204df0f8Sclaudio 
136204df0f8Sclaudio 	if (new_state != 0)
137204df0f8Sclaudio 		iface->state = new_state;
138204df0f8Sclaudio 
13906267228Sclaudio 	if (iface->state != old_state) {
14006267228Sclaudio 		area_track(iface->area);
14191cc1f69Sclaudio 		orig_rtr_lsa(iface->area);
14206267228Sclaudio 	}
14391cc1f69Sclaudio 
144f6f3b5e0Sclaudio 	if (old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT) &&
145f6f3b5e0Sclaudio 	    (iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0)
146fba2d3d0Sclaudio 		ospfe_demote_iface(iface, 0);
147f6f3b5e0Sclaudio 	if ((old_state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0 &&
148f6f3b5e0Sclaudio 	    iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT))
149fba2d3d0Sclaudio 		ospfe_demote_iface(iface, 1);
150fba2d3d0Sclaudio 
1515d021e83Smsf 	log_debug("if_fsm: event %s resulted in action %s and changing "
152204df0f8Sclaudio 	    "state for interface %s from %s to %s",
153cdd38203Sclaudio 	    if_event_names[event], if_action_names[iface_fsm[i].action],
154204df0f8Sclaudio 	    iface->name, if_state_name(old_state), if_state_name(iface->state));
155204df0f8Sclaudio 
156204df0f8Sclaudio 	return (ret);
157204df0f8Sclaudio }
158204df0f8Sclaudio 
159204df0f8Sclaudio struct iface *
16066dd3991Sclaudio if_new(struct kif *kif, struct kif_addr *ka)
161204df0f8Sclaudio {
162e9fa2173Sclaudio 	struct iface		*iface;
163204df0f8Sclaudio 
164204df0f8Sclaudio 	if ((iface = calloc(1, sizeof(*iface))) == NULL)
165e9fa2173Sclaudio 		err(1, "if_new: calloc");
166204df0f8Sclaudio 
167204df0f8Sclaudio 	iface->state = IF_STA_DOWN;
168204df0f8Sclaudio 
169204df0f8Sclaudio 	LIST_INIT(&iface->nbr_list);
170204df0f8Sclaudio 	TAILQ_INIT(&iface->ls_ack_list);
17176b51f83Sclaudio 	TAILQ_INIT(&iface->auth_md_list);
172097ed198Sclaudio 	RB_INIT(&iface->lsa_tree);
17303431b74Snorby 
1742ecbebf2Snorby 	iface->crypt_seq_num = arc4random() & 0x0fffffff;
175204df0f8Sclaudio 
176ec4d3955Snorby 	if (kif == NULL) {
177ec4d3955Snorby 		iface->type = IF_TYPE_VIRTUALLINK;
178ec4d3955Snorby 		snprintf(iface->name, sizeof(iface->name), "vlink%d",
179ec4d3955Snorby 		    vlink_cnt++);
180ec4d3955Snorby 		iface->flags |= IFF_UP;
181ec4d3955Snorby 		iface->mtu = IP_MSS;
182ec4d3955Snorby 		return (iface);
183ec4d3955Snorby 	}
184ec4d3955Snorby 
185e9fa2173Sclaudio 	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
186204df0f8Sclaudio 
187204df0f8Sclaudio 	/* get type */
18818cd8ae7Sclaudio 	if (kif->flags & IFF_POINTOPOINT)
189204df0f8Sclaudio 		iface->type = IF_TYPE_POINTOPOINT;
19018cd8ae7Sclaudio 	if (kif->flags & IFF_BROADCAST &&
19118cd8ae7Sclaudio 	    kif->flags & IFF_MULTICAST)
192204df0f8Sclaudio 		iface->type = IF_TYPE_BROADCAST;
19318cd8ae7Sclaudio 	if (kif->flags & IFF_LOOPBACK) {
19418cd8ae7Sclaudio 		iface->type = IF_TYPE_POINTOPOINT;
195e79881faSsthen 		iface->passive = 1;
19618cd8ae7Sclaudio 	}
197204df0f8Sclaudio 
198e9fa2173Sclaudio 	/* get mtu, index and flags */
199e9fa2173Sclaudio 	iface->mtu = kif->mtu;
200e9fa2173Sclaudio 	iface->ifindex = kif->ifindex;
201358269bbSclaudio 	iface->rdomain = kif->rdomain;
202e9fa2173Sclaudio 	iface->flags = kif->flags;
203e9fa2173Sclaudio 	iface->linkstate = kif->link_state;
20418ffdd94Sstsp 	iface->if_type = kif->if_type;
2052e87294eSclaudio 	iface->baudrate = kif->baudrate;
206204df0f8Sclaudio 
20766dd3991Sclaudio 	/* set address, mask and p2p addr */
20866dd3991Sclaudio 	iface->addr = ka->addr;
20966dd3991Sclaudio 	iface->mask = ka->mask;
21018cd8ae7Sclaudio 	if (kif->flags & IFF_POINTOPOINT) {
21166dd3991Sclaudio 		iface->dst = ka->dstbrd;
212feb9f399Snorby 	}
213feb9f399Snorby 
214204df0f8Sclaudio 	return (iface);
215204df0f8Sclaudio }
216204df0f8Sclaudio 
21735277385Sclaudio void
218204df0f8Sclaudio if_del(struct iface *iface)
219204df0f8Sclaudio {
220204df0f8Sclaudio 	struct nbr	*nbr = NULL;
221204df0f8Sclaudio 
222fba2d3d0Sclaudio 	/* revert the demotion when the interface is deleted */
223fba2d3d0Sclaudio 	if ((iface->state & (IF_STA_MULTI | IF_STA_POINTTOPOINT)) == 0)
224fba2d3d0Sclaudio 		ospfe_demote_iface(iface, 1);
225fba2d3d0Sclaudio 
226204df0f8Sclaudio 	/* clear lists etc */
22735277385Sclaudio 	while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL)
228204df0f8Sclaudio 		nbr_del(nbr);
229204df0f8Sclaudio 
230a302a2ceSclaudio 	if (evtimer_pending(&iface->hello_timer, NULL))
231a302a2ceSclaudio 		evtimer_del(&iface->hello_timer);
232a302a2ceSclaudio 	if (evtimer_pending(&iface->wait_timer, NULL))
233a302a2ceSclaudio 		evtimer_del(&iface->wait_timer);
234a302a2ceSclaudio 	if (evtimer_pending(&iface->lsack_tx_timer, NULL))
235a302a2ceSclaudio 		evtimer_del(&iface->lsack_tx_timer);
236a302a2ceSclaudio 
237204df0f8Sclaudio 	ls_ack_list_clr(iface);
23876b51f83Sclaudio 	md_list_clr(&iface->auth_md_list);
23935277385Sclaudio 	free(iface);
240204df0f8Sclaudio }
241204df0f8Sclaudio 
2421d887742Sclaudio void
2431d887742Sclaudio if_init(struct ospfd_conf *xconf, struct iface *iface)
244204df0f8Sclaudio {
2451d887742Sclaudio 	/* init the dummy local neighbor */
2461d887742Sclaudio 	iface->self = nbr_new(ospfe_router_id(), iface, 1);
247204df0f8Sclaudio 
2481d887742Sclaudio 	/* set event handlers for interface */
2491d887742Sclaudio 	evtimer_set(&iface->lsack_tx_timer, ls_ack_tx_timer, iface);
2501d887742Sclaudio 	evtimer_set(&iface->hello_timer, if_hello_timer, iface);
2511d887742Sclaudio 	evtimer_set(&iface->wait_timer, if_wait_timer, iface);
2521d887742Sclaudio 
253feb9f399Snorby 	iface->fd = xconf->ospf_socket;
254fba2d3d0Sclaudio 
255fba2d3d0Sclaudio 	ospfe_demote_iface(iface, 0);
256204df0f8Sclaudio }
257204df0f8Sclaudio 
258204df0f8Sclaudio /* timers */
259204df0f8Sclaudio void
260204df0f8Sclaudio if_hello_timer(int fd, short event, void *arg)
261204df0f8Sclaudio {
262204df0f8Sclaudio 	struct iface *iface = arg;
263204df0f8Sclaudio 	struct timeval tv;
264204df0f8Sclaudio 
265204df0f8Sclaudio 	send_hello(iface);
266204df0f8Sclaudio 
267204df0f8Sclaudio 	/* reschedule hello_timer */
268204df0f8Sclaudio 	timerclear(&tv);
2697afdfd2dSdlg 	if (iface->dead_interval == FAST_RTR_DEAD_TIME)
2707afdfd2dSdlg 		tv.tv_usec = iface->fast_hello_interval * 1000;
2717afdfd2dSdlg 	else
272204df0f8Sclaudio 		tv.tv_sec = iface->hello_interval;
2733aede346Sclaudio 	if (evtimer_add(&iface->hello_timer, &tv) == -1)
2743aede346Sclaudio 		fatal("if_hello_timer");
275204df0f8Sclaudio }
276204df0f8Sclaudio 
2773aede346Sclaudio void
278204df0f8Sclaudio if_start_hello_timer(struct iface *iface)
279204df0f8Sclaudio {
280204df0f8Sclaudio 	struct timeval tv;
281204df0f8Sclaudio 
282204df0f8Sclaudio 	timerclear(&tv);
2833aede346Sclaudio 	if (evtimer_add(&iface->hello_timer, &tv) == -1)
2843aede346Sclaudio 		fatal("if_start_hello_timer");
285204df0f8Sclaudio }
286204df0f8Sclaudio 
2873aede346Sclaudio void
288204df0f8Sclaudio if_stop_hello_timer(struct iface *iface)
289204df0f8Sclaudio {
2903aede346Sclaudio 	if (evtimer_del(&iface->hello_timer) == -1)
2913aede346Sclaudio 		fatal("if_stop_hello_timer");
292204df0f8Sclaudio }
293204df0f8Sclaudio 
294204df0f8Sclaudio void
295204df0f8Sclaudio if_wait_timer(int fd, short event, void *arg)
296204df0f8Sclaudio {
297204df0f8Sclaudio 	struct iface *iface = arg;
298204df0f8Sclaudio 
299204df0f8Sclaudio 	if_fsm(iface, IF_EVT_WTIMER);
300204df0f8Sclaudio }
301204df0f8Sclaudio 
3023aede346Sclaudio void
303204df0f8Sclaudio if_start_wait_timer(struct iface *iface)
304204df0f8Sclaudio {
305204df0f8Sclaudio 	struct timeval	tv;
306204df0f8Sclaudio 
307204df0f8Sclaudio 	timerclear(&tv);
308204df0f8Sclaudio 	tv.tv_sec = iface->dead_interval;
3093aede346Sclaudio 	if (evtimer_add(&iface->wait_timer, &tv) == -1)
3103aede346Sclaudio 		fatal("if_start_wait_timer");
311204df0f8Sclaudio }
312204df0f8Sclaudio 
3133aede346Sclaudio void
314204df0f8Sclaudio if_stop_wait_timer(struct iface *iface)
315204df0f8Sclaudio {
3163aede346Sclaudio 	if (evtimer_del(&iface->wait_timer) == -1)
3173aede346Sclaudio 		fatal("if_stop_wait_timer");
318204df0f8Sclaudio }
319204df0f8Sclaudio 
320204df0f8Sclaudio /* actions */
321204df0f8Sclaudio int
322204df0f8Sclaudio if_act_start(struct iface *iface)
323204df0f8Sclaudio {
324204df0f8Sclaudio 	struct in_addr		 addr;
3253388a643Snorby 	struct timeval		 now;
326204df0f8Sclaudio 
3273a9b53d2Sbenno 	if (!(iface->flags & IFF_UP) ||
3283a9b53d2Sbenno 	    (!LINK_STATE_IS_UP(iface->linkstate) &&
32918ffdd94Sstsp 	    !(iface->if_type == IFT_CARP &&
3303a9b53d2Sbenno 	    iface->linkstate == LINK_STATE_DOWN)))
331e9fa2173Sclaudio 		return (0);
332e9fa2173Sclaudio 
33318ffdd94Sstsp 	if (iface->if_type == IFT_CARP && iface->passive == 0) {
334d7a91d7eSclaudio 		/* force passive mode on carp interfaces */
335d7a91d7eSclaudio 		log_warnx("if_act_start: forcing interface %s to passive",
336d7a91d7eSclaudio 		    iface->name);
337d7a91d7eSclaudio 		iface->passive = 1;
338d7a91d7eSclaudio 	}
339d7a91d7eSclaudio 
340aa018143Sclaudio 	gettimeofday(&now, NULL);
341aa018143Sclaudio 	iface->uptime = now.tv_sec;
342aa018143Sclaudio 
343aa018143Sclaudio 	/* loopback interfaces have a special state and are passive */
344aa018143Sclaudio 	if (iface->flags & IFF_LOOPBACK)
345aa018143Sclaudio 		iface->state = IF_STA_LOOPBACK;
346aa018143Sclaudio 
347d7a91d7eSclaudio 	if (iface->passive) {
348d7a91d7eSclaudio 		/* for an update of stub network entries */
349d7a91d7eSclaudio 		orig_rtr_lsa(iface->area);
350d7a91d7eSclaudio 		return (0);
351d7a91d7eSclaudio 	}
352d7a91d7eSclaudio 
353204df0f8Sclaudio 	switch (iface->type) {
354204df0f8Sclaudio 	case IF_TYPE_POINTOPOINT:
355*4f4fe40bSflorian 		inet_pton(AF_INET, AllSPFRouters, &addr);
35666dd3991Sclaudio 		if (if_join_group(iface, &addr))
357feb9f399Snorby 			return (-1);
358feb9f399Snorby 		iface->state = IF_STA_POINTTOPOINT;
359feb9f399Snorby 		break;
360204df0f8Sclaudio 	case IF_TYPE_VIRTUALLINK:
361ec4d3955Snorby 		iface->state = IF_STA_POINTTOPOINT;
362ec4d3955Snorby 		break;
363ec4d3955Snorby 	case IF_TYPE_POINTOMULTIPOINT:
3641aaf2c2dSclaudio 	case IF_TYPE_NBMA:
365204df0f8Sclaudio 		log_debug("if_act_start: type %s not supported, interface %s",
366204df0f8Sclaudio 		    if_type_name(iface->type), iface->name);
367204df0f8Sclaudio 		return (-1);
368204df0f8Sclaudio 	case IF_TYPE_BROADCAST:
369*4f4fe40bSflorian 		inet_pton(AF_INET, AllSPFRouters, &addr);
37066dd3991Sclaudio 		if (if_join_group(iface, &addr))
3711aaf2c2dSclaudio 			return (-1);
372204df0f8Sclaudio 		if (iface->priority == 0) {
373204df0f8Sclaudio 			iface->state = IF_STA_DROTHER;
374204df0f8Sclaudio 		} else {
375204df0f8Sclaudio 			iface->state = IF_STA_WAITING;
3763aede346Sclaudio 			if_start_wait_timer(iface);
377204df0f8Sclaudio 		}
378204df0f8Sclaudio 		break;
379204df0f8Sclaudio 	default:
380204df0f8Sclaudio 		fatalx("if_act_start: unknown interface type");
381204df0f8Sclaudio 	}
382204df0f8Sclaudio 
3833aede346Sclaudio 	/* hello timer needs to be started in any case */
3843aede346Sclaudio 	if_start_hello_timer(iface);
385204df0f8Sclaudio 	return (0);
386204df0f8Sclaudio }
387204df0f8Sclaudio 
388204df0f8Sclaudio struct nbr *
389204df0f8Sclaudio if_elect(struct nbr *a, struct nbr *b)
390204df0f8Sclaudio {
391204df0f8Sclaudio 	if (a->priority > b->priority)
392204df0f8Sclaudio 		return (a);
393204df0f8Sclaudio 	if (a->priority < b->priority)
394204df0f8Sclaudio 		return (b);
395204df0f8Sclaudio 	if (ntohl(a->id.s_addr) > ntohl(b->id.s_addr))
396204df0f8Sclaudio 		return (a);
397204df0f8Sclaudio 	return (b);
398204df0f8Sclaudio }
399204df0f8Sclaudio 
400204df0f8Sclaudio int
401204df0f8Sclaudio if_act_elect(struct iface *iface)
402204df0f8Sclaudio {
403ae00ee2bSclaudio 	struct in_addr	 addr;
404204df0f8Sclaudio 	struct nbr	*nbr, *bdr = NULL, *dr = NULL;
405204df0f8Sclaudio 	int		 round = 0;
406204df0f8Sclaudio 	int		 changed = 0;
40791cc1f69Sclaudio 	int		 old_state;
408204df0f8Sclaudio 	char		 b1[16], b2[16], b3[16], b4[16];
409204df0f8Sclaudio 
410204df0f8Sclaudio start:
411204df0f8Sclaudio 	/* elect backup designated router */
412204df0f8Sclaudio 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
413204df0f8Sclaudio 		if (nbr->priority == 0 || nbr == dr ||	/* not electable */
4141d6faa01Sclaudio 		    nbr->state & NBR_STA_PRELIM ||	/* not available */
415204df0f8Sclaudio 		    nbr->dr.s_addr == nbr->addr.s_addr)	/* don't elect DR */
416204df0f8Sclaudio 			continue;
417204df0f8Sclaudio 		if (bdr != NULL) {
418a3aad27bSclaudio 			/*
419611e3786Sclaudio 			 * routers announcing themselves as BDR have higher
420a3aad27bSclaudio 			 * precedence over those routers announcing a
421a3aad27bSclaudio 			 * different BDR.
422a3aad27bSclaudio 			 */
423204df0f8Sclaudio 			if (nbr->bdr.s_addr == nbr->addr.s_addr) {
424204df0f8Sclaudio 				if (bdr->bdr.s_addr == bdr->addr.s_addr)
425204df0f8Sclaudio 					bdr = if_elect(bdr, nbr);
426204df0f8Sclaudio 				else
427204df0f8Sclaudio 					bdr = nbr;
428204df0f8Sclaudio 			} else if (bdr->bdr.s_addr != bdr->addr.s_addr)
429204df0f8Sclaudio 					bdr = if_elect(bdr, nbr);
430204df0f8Sclaudio 		} else
431204df0f8Sclaudio 			bdr = nbr;
432204df0f8Sclaudio 	}
4338852c891Snorby 
434204df0f8Sclaudio 	/* elect designated router */
435204df0f8Sclaudio 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
4361d6faa01Sclaudio 		if (nbr->priority == 0 || nbr->state & NBR_STA_PRELIM ||
437bdd037daSclaudio 		    (nbr != dr && nbr->dr.s_addr != nbr->addr.s_addr))
438204df0f8Sclaudio 			/* only DR may be elected check priority too */
439204df0f8Sclaudio 			continue;
440a3aad27bSclaudio 		if (dr == NULL)
441204df0f8Sclaudio 			dr = nbr;
442204df0f8Sclaudio 		else
443bdd037daSclaudio 			dr = if_elect(dr, nbr);
444204df0f8Sclaudio 	}
4458852c891Snorby 
446204df0f8Sclaudio 	if (dr == NULL) {
447cc63418dSsthen 		/* no designated router found use backup DR */
448204df0f8Sclaudio 		dr = bdr;
449204df0f8Sclaudio 		bdr = NULL;
450204df0f8Sclaudio 	}
451204df0f8Sclaudio 
452204df0f8Sclaudio 	/*
453204df0f8Sclaudio 	 * if we are involved in the election (e.g. new DR or no
454204df0f8Sclaudio 	 * longer BDR) redo the election
455204df0f8Sclaudio 	 */
456204df0f8Sclaudio 	if (round == 0 &&
457204df0f8Sclaudio 	    ((iface->self == dr && iface->self != iface->dr) ||
458204df0f8Sclaudio 	    (iface->self != dr && iface->self == iface->dr) ||
459204df0f8Sclaudio 	    (iface->self == bdr && iface->self != iface->bdr) ||
460204df0f8Sclaudio 	    (iface->self != bdr && iface->self == iface->bdr))) {
461a3aad27bSclaudio 		/*
462611e3786Sclaudio 		 * Reset announced DR/BDR to calculated one, so
463a3aad27bSclaudio 		 * that we may get elected in the second round.
464a3aad27bSclaudio 		 * This is needed to drop from a DR to a BDR.
465a3aad27bSclaudio 		 */
466a3aad27bSclaudio 		iface->self->dr.s_addr = dr->addr.s_addr;
467a3aad27bSclaudio 		if (bdr)
46861741bc7Sclaudio 			iface->self->bdr.s_addr = bdr->addr.s_addr;
469204df0f8Sclaudio 		round = 1;
470204df0f8Sclaudio 		goto start;
471204df0f8Sclaudio 	}
472204df0f8Sclaudio 
473204df0f8Sclaudio 	log_debug("if_act_elect: interface %s old dr %s new dr %s, "
474204df0f8Sclaudio 	    "old bdr %s new bdr %s", iface->name,
475204df0f8Sclaudio 	    iface->dr ? inet_ntop(AF_INET, &iface->dr->addr, b1, sizeof(b1)) :
476204df0f8Sclaudio 	    "none", dr ? inet_ntop(AF_INET, &dr->addr, b2, sizeof(b2)) : "none",
477204df0f8Sclaudio 	    iface->bdr ? inet_ntop(AF_INET, &iface->bdr->addr, b3, sizeof(b3)) :
478204df0f8Sclaudio 	    "none", bdr ? inet_ntop(AF_INET, &bdr->addr, b4, sizeof(b4)) :
479204df0f8Sclaudio 	    "none");
480204df0f8Sclaudio 
481204df0f8Sclaudio 	/*
482204df0f8Sclaudio 	 * After the second round still DR or BDR change state to DR or BDR,
483204df0f8Sclaudio 	 * etc.
484204df0f8Sclaudio 	 */
48591cc1f69Sclaudio 	old_state = iface->state;
486204df0f8Sclaudio 	if (dr == iface->self)
487204df0f8Sclaudio 		iface->state = IF_STA_DR;
488204df0f8Sclaudio 	else if (bdr == iface->self)
489204df0f8Sclaudio 		iface->state = IF_STA_BACKUP;
490204df0f8Sclaudio 	else
491204df0f8Sclaudio 		iface->state = IF_STA_DROTHER;
492204df0f8Sclaudio 
493204df0f8Sclaudio 	/* TODO if iface is NBMA send all non eligible neighbors event Start */
494204df0f8Sclaudio 
495204df0f8Sclaudio 	/*
496204df0f8Sclaudio 	 * if DR or BDR changed issue a AdjOK? event for all neighbors > 2-Way
497204df0f8Sclaudio 	 */
498204df0f8Sclaudio 	if (iface->dr != dr || iface->bdr != bdr)
499204df0f8Sclaudio 		changed = 1;
500204df0f8Sclaudio 
501204df0f8Sclaudio 	iface->dr = dr;
502204df0f8Sclaudio 	iface->bdr = bdr;
503204df0f8Sclaudio 
50491cc1f69Sclaudio 	if (changed) {
505*4f4fe40bSflorian 		inet_pton(AF_INET, AllDRouters, &addr);
506ae00ee2bSclaudio 		if (old_state & IF_STA_DRORBDR &&
507ae00ee2bSclaudio 		    (iface->state & IF_STA_DRORBDR) == 0) {
50866dd3991Sclaudio 			if (if_leave_group(iface, &addr))
509ae00ee2bSclaudio 				return (-1);
510ae00ee2bSclaudio 		} else if ((old_state & IF_STA_DRORBDR) == 0 &&
511ae00ee2bSclaudio 		    iface->state & IF_STA_DRORBDR) {
51266dd3991Sclaudio 			if (if_join_group(iface, &addr))
513ae00ee2bSclaudio 				return (-1);
514ae00ee2bSclaudio 		}
515ae00ee2bSclaudio 
516204df0f8Sclaudio 		LIST_FOREACH(nbr, &iface->nbr_list, entry) {
517204df0f8Sclaudio 			if (nbr->state & NBR_STA_BIDIR)
518204df0f8Sclaudio 				nbr_fsm(nbr, NBR_EVT_ADJ_OK);
519204df0f8Sclaudio 		}
520ae00ee2bSclaudio 
52191cc1f69Sclaudio 		orig_rtr_lsa(iface->area);
52291cc1f69Sclaudio 		if (iface->state & IF_STA_DR || old_state & IF_STA_DR)
52391cc1f69Sclaudio 			orig_net_lsa(iface);
52491cc1f69Sclaudio 	}
525204df0f8Sclaudio 
5263aede346Sclaudio 	if_start_hello_timer(iface);
527204df0f8Sclaudio 	return (0);
528204df0f8Sclaudio }
529204df0f8Sclaudio 
530204df0f8Sclaudio int
531204df0f8Sclaudio if_act_reset(struct iface *iface)
532204df0f8Sclaudio {
533204df0f8Sclaudio 	struct nbr		*nbr = NULL;
534204df0f8Sclaudio 	struct in_addr		 addr;
535204df0f8Sclaudio 
536d7a91d7eSclaudio 	if (iface->passive) {
537d7a91d7eSclaudio 		/* for an update of stub network entries */
538d7a91d7eSclaudio 		orig_rtr_lsa(iface->area);
539d7a91d7eSclaudio 		return (0);
540d7a91d7eSclaudio 	}
541d7a91d7eSclaudio 
542204df0f8Sclaudio 	switch (iface->type) {
543204df0f8Sclaudio 	case IF_TYPE_POINTOPOINT:
544204df0f8Sclaudio 	case IF_TYPE_BROADCAST:
54545daa23aSclaudio 		/* try to cleanup */
546*4f4fe40bSflorian 		inet_pton(AF_INET, AllSPFRouters, &addr);
54745daa23aSclaudio 		if_leave_group(iface, &addr);
5484804db9fSclaudio 		if (iface->state & IF_STA_DRORBDR) {
549*4f4fe40bSflorian 			inet_pton(AF_INET, AllDRouters, &addr);
55045daa23aSclaudio 			if_leave_group(iface, &addr);
5514804db9fSclaudio 		}
552204df0f8Sclaudio 		break;
553ec4d3955Snorby 	case IF_TYPE_VIRTUALLINK:
554ec4d3955Snorby 		/* nothing */
555ec4d3955Snorby 		break;
556204df0f8Sclaudio 	case IF_TYPE_NBMA:
557204df0f8Sclaudio 	case IF_TYPE_POINTOMULTIPOINT:
558204df0f8Sclaudio 		log_debug("if_act_reset: type %s not supported, interface %s",
559204df0f8Sclaudio 		    if_type_name(iface->type), iface->name);
560204df0f8Sclaudio 		return (-1);
561204df0f8Sclaudio 	default:
562204df0f8Sclaudio 		fatalx("if_act_reset: unknown interface type");
563204df0f8Sclaudio 	}
564204df0f8Sclaudio 
565204df0f8Sclaudio 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
566204df0f8Sclaudio 		if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) {
567204df0f8Sclaudio 			log_debug("if_act_reset: error killing neighbor %s",
568204df0f8Sclaudio 			    inet_ntoa(nbr->id));
569204df0f8Sclaudio 		}
570204df0f8Sclaudio 	}
571204df0f8Sclaudio 
572204df0f8Sclaudio 	iface->dr = NULL;
573204df0f8Sclaudio 	iface->bdr = NULL;
574204df0f8Sclaudio 
575a7d8ed20Sclaudio 	ls_ack_list_clr(iface);
5763aede346Sclaudio 	stop_ls_ack_tx_timer(iface);
5773aede346Sclaudio 	if_stop_hello_timer(iface);
5783aede346Sclaudio 	if_stop_wait_timer(iface);
579a7d8ed20Sclaudio 
5804d12edf6Sclaudio 	/* send empty hello to tell everybody that we are going down */
5814d12edf6Sclaudio 	send_hello(iface);
5824d12edf6Sclaudio 
583204df0f8Sclaudio 	return (0);
584204df0f8Sclaudio }
585204df0f8Sclaudio 
586204df0f8Sclaudio struct ctl_iface *
587204df0f8Sclaudio if_to_ctl(struct iface *iface)
588204df0f8Sclaudio {
589204df0f8Sclaudio 	static struct ctl_iface	 ictl;
590204df0f8Sclaudio 	struct timeval		 tv, now, res;
591204df0f8Sclaudio 	struct nbr		*nbr;
592204df0f8Sclaudio 
593204df0f8Sclaudio 	memcpy(ictl.name, iface->name, sizeof(ictl.name));
594204df0f8Sclaudio 	memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr));
595204df0f8Sclaudio 	memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask));
596f12637e5Smsf 	ictl.rtr_id.s_addr = ospfe_router_id();
597204df0f8Sclaudio 	memcpy(&ictl.area, &iface->area->id, sizeof(ictl.area));
598204df0f8Sclaudio 	if (iface->dr) {
599204df0f8Sclaudio 		memcpy(&ictl.dr_id, &iface->dr->id, sizeof(ictl.dr_id));
600204df0f8Sclaudio 		memcpy(&ictl.dr_addr, &iface->dr->addr, sizeof(ictl.dr_addr));
601204df0f8Sclaudio 	} else {
602204df0f8Sclaudio 		bzero(&ictl.dr_id, sizeof(ictl.dr_id));
603204df0f8Sclaudio 		bzero(&ictl.dr_addr, sizeof(ictl.dr_addr));
604204df0f8Sclaudio 	}
605204df0f8Sclaudio 	if (iface->bdr) {
606204df0f8Sclaudio 		memcpy(&ictl.bdr_id, &iface->bdr->id, sizeof(ictl.bdr_id));
607204df0f8Sclaudio 		memcpy(&ictl.bdr_addr, &iface->bdr->addr,
608204df0f8Sclaudio 		    sizeof(ictl.bdr_addr));
609204df0f8Sclaudio 	} else {
610204df0f8Sclaudio 		bzero(&ictl.bdr_id, sizeof(ictl.bdr_id));
611204df0f8Sclaudio 		bzero(&ictl.bdr_addr, sizeof(ictl.bdr_addr));
612204df0f8Sclaudio 	}
613204df0f8Sclaudio 	ictl.ifindex = iface->ifindex;
614204df0f8Sclaudio 	ictl.state = iface->state;
615204df0f8Sclaudio 	ictl.mtu = iface->mtu;
616204df0f8Sclaudio 	ictl.nbr_cnt = 0;
617204df0f8Sclaudio 	ictl.adj_cnt = 0;
618204df0f8Sclaudio 	ictl.baudrate = iface->baudrate;
619204df0f8Sclaudio 	ictl.dead_interval = iface->dead_interval;
6207afdfd2dSdlg 	ictl.fast_hello_interval = iface->fast_hello_interval;
6211d3a1c7dSnorby 	ictl.transmit_delay = iface->transmit_delay;
622204df0f8Sclaudio 	ictl.hello_interval = iface->hello_interval;
623204df0f8Sclaudio 	ictl.flags = iface->flags;
624204df0f8Sclaudio 	ictl.metric = iface->metric;
625204df0f8Sclaudio 	ictl.rxmt_interval = iface->rxmt_interval;
626204df0f8Sclaudio 	ictl.type = iface->type;
627204df0f8Sclaudio 	ictl.linkstate = iface->linkstate;
62818ffdd94Sstsp 	ictl.if_type = iface->if_type;
629204df0f8Sclaudio 	ictl.priority = iface->priority;
630204df0f8Sclaudio 	ictl.passive = iface->passive;
631a4aef966Smsf 	ictl.auth_type = iface->auth_type;
632a4aef966Smsf 	ictl.auth_keyid = iface->auth_keyid;
633204df0f8Sclaudio 
63427198fecSdlg 	memcpy(ictl.dependon, iface->dependon, sizeof(ictl.dependon));
63527198fecSdlg 	ictl.depend_ok = iface->depend_ok;
63627198fecSdlg 
637204df0f8Sclaudio 	gettimeofday(&now, NULL);
638204df0f8Sclaudio 	if (evtimer_pending(&iface->hello_timer, &tv)) {
639204df0f8Sclaudio 		timersub(&tv, &now, &res);
6407afdfd2dSdlg 		ictl.hello_timer = res;
6417afdfd2dSdlg 	} else {
6427afdfd2dSdlg 		ictl.hello_timer.tv_sec = -1;
6437afdfd2dSdlg 	}
644204df0f8Sclaudio 
645df38c63bSsthen 	if (iface->state != IF_STA_DOWN &&
646df38c63bSsthen 	    iface->uptime != 0) {
6473388a643Snorby 		ictl.uptime = now.tv_sec - iface->uptime;
6483388a643Snorby 	} else
6493388a643Snorby 		ictl.uptime = 0;
6503388a643Snorby 
651204df0f8Sclaudio 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
652204df0f8Sclaudio 		if (nbr == iface->self)
653204df0f8Sclaudio 			continue;
654204df0f8Sclaudio 		ictl.nbr_cnt++;
655204df0f8Sclaudio 		if (nbr->state & NBR_STA_ADJFORM)
656204df0f8Sclaudio 			ictl.adj_cnt++;
657204df0f8Sclaudio 	}
658204df0f8Sclaudio 
659204df0f8Sclaudio 	return (&ictl);
660204df0f8Sclaudio }
661204df0f8Sclaudio 
662204df0f8Sclaudio /* misc */
663204df0f8Sclaudio int
664e29a452cSclaudio if_set_recvif(int fd, int enable)
665e29a452cSclaudio {
666e29a452cSclaudio 	if (setsockopt(fd, IPPROTO_IP, IP_RECVIF, &enable,
667df69c215Sderaadt 	    sizeof(enable)) == -1) {
668e29a452cSclaudio 		log_warn("if_set_recvif: error setting IP_RECVIF");
669e29a452cSclaudio 		return (-1);
670e29a452cSclaudio 	}
671e29a452cSclaudio 	return (0);
672e29a452cSclaudio }
673e29a452cSclaudio 
674cfb36fa8Snorby void
675b07e4035Sclaudio if_set_sockbuf(int fd)
676cfb36fa8Snorby {
677cfb36fa8Snorby 	int	bsize;
678cfb36fa8Snorby 
6798e4254b8Sclaudio 	bsize = 256 * 1024;
680cfb36fa8Snorby 	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
681cfb36fa8Snorby 	    sizeof(bsize)) == -1)
682cfb36fa8Snorby 		bsize /= 2;
683b07e4035Sclaudio 
6848e4254b8Sclaudio 	if (bsize != 256 * 1024)
685b07e4035Sclaudio 		log_warnx("if_set_sockbuf: recvbuf size only %d", bsize);
686b07e4035Sclaudio 
6878e4254b8Sclaudio 	bsize = 64 * 1024;
688b07e4035Sclaudio 	while (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bsize,
689b07e4035Sclaudio 	    sizeof(bsize)) == -1)
690b07e4035Sclaudio 		bsize /= 2;
691b07e4035Sclaudio 
6928e4254b8Sclaudio 	if (bsize != 64 * 1024)
693b07e4035Sclaudio 		log_warnx("if_set_sockbuf: sendbuf size only %d", bsize);
694cfb36fa8Snorby }
695cfb36fa8Snorby 
69666dd3991Sclaudio /*
69766dd3991Sclaudio  * only one JOIN or DROP per interface and address is allowed so we need
69866dd3991Sclaudio  * to keep track of what is added and removed.
69966dd3991Sclaudio  */
70066dd3991Sclaudio struct if_group_count {
70166dd3991Sclaudio 	LIST_ENTRY(if_group_count)	entry;
70266dd3991Sclaudio 	struct in_addr			addr;
70366dd3991Sclaudio 	unsigned int			ifindex;
70466dd3991Sclaudio 	int				count;
70566dd3991Sclaudio };
70666dd3991Sclaudio 
70766dd3991Sclaudio LIST_HEAD(,if_group_count) ifglist = LIST_HEAD_INITIALIZER(ifglist);
70866dd3991Sclaudio 
709204df0f8Sclaudio int
710204df0f8Sclaudio if_join_group(struct iface *iface, struct in_addr *addr)
711204df0f8Sclaudio {
712b3a8ee7dSclaudio 	struct ip_mreqn		 mreq;
71366dd3991Sclaudio 	struct if_group_count	*ifg;
714204df0f8Sclaudio 
715204df0f8Sclaudio 	switch (iface->type) {
716feb9f399Snorby 	case IF_TYPE_POINTOPOINT:
717204df0f8Sclaudio 	case IF_TYPE_BROADCAST:
71866dd3991Sclaudio 		LIST_FOREACH(ifg, &ifglist, entry)
71966dd3991Sclaudio 			if (iface->ifindex == ifg->ifindex &&
72066dd3991Sclaudio 			    addr->s_addr == ifg->addr.s_addr)
72166dd3991Sclaudio 				break;
72266dd3991Sclaudio 		if (ifg == NULL) {
72366dd3991Sclaudio 			if ((ifg = calloc(1, sizeof(*ifg))) == NULL)
72466dd3991Sclaudio 				fatal("if_join_group");
72566dd3991Sclaudio 			ifg->addr.s_addr = addr->s_addr;
72666dd3991Sclaudio 			ifg->ifindex = iface->ifindex;
72766dd3991Sclaudio 			LIST_INSERT_HEAD(&ifglist, ifg, entry);
72866dd3991Sclaudio 		}
72966dd3991Sclaudio 
73066dd3991Sclaudio 		if (ifg->count++ != 0)
73166dd3991Sclaudio 			/* already joined */
73266dd3991Sclaudio 			return (0);
73366dd3991Sclaudio 
734263652a1Sclaudio 		memset(&mreq, 0, sizeof(mreq));
735204df0f8Sclaudio 		mreq.imr_multiaddr.s_addr = addr->s_addr;
736b3a8ee7dSclaudio 		mreq.imr_ifindex = iface->ifindex;
737204df0f8Sclaudio 
738204df0f8Sclaudio 		if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
739df69c215Sderaadt 		    (void *)&mreq, sizeof(mreq)) == -1) {
74066dd3991Sclaudio 			log_warn("if_join_group: error IP_ADD_MEMBERSHIP, "
74166dd3991Sclaudio 			    "interface %s address %s", iface->name,
74266dd3991Sclaudio 			    inet_ntoa(*addr));
743204df0f8Sclaudio 			return (-1);
744204df0f8Sclaudio 		}
745204df0f8Sclaudio 		break;
746204df0f8Sclaudio 	case IF_TYPE_POINTOMULTIPOINT:
747204df0f8Sclaudio 	case IF_TYPE_VIRTUALLINK:
748204df0f8Sclaudio 	case IF_TYPE_NBMA:
749204df0f8Sclaudio 		log_debug("if_join_group: type %s not supported, interface %s",
750204df0f8Sclaudio 		    if_type_name(iface->type), iface->name);
751204df0f8Sclaudio 		return (-1);
752204df0f8Sclaudio 	default:
753204df0f8Sclaudio 		fatalx("if_join_group: unknown interface type");
754204df0f8Sclaudio 	}
755204df0f8Sclaudio 
756204df0f8Sclaudio 	return (0);
757204df0f8Sclaudio }
758204df0f8Sclaudio 
759204df0f8Sclaudio int
760204df0f8Sclaudio if_leave_group(struct iface *iface, struct in_addr *addr)
761204df0f8Sclaudio {
762b3a8ee7dSclaudio 	struct ip_mreqn		 mreq;
76366dd3991Sclaudio 	struct if_group_count	*ifg;
764204df0f8Sclaudio 
765204df0f8Sclaudio 	switch (iface->type) {
766feb9f399Snorby 	case IF_TYPE_POINTOPOINT:
767204df0f8Sclaudio 	case IF_TYPE_BROADCAST:
76866dd3991Sclaudio 		LIST_FOREACH(ifg, &ifglist, entry)
76966dd3991Sclaudio 			if (iface->ifindex == ifg->ifindex &&
77066dd3991Sclaudio 			    addr->s_addr == ifg->addr.s_addr)
77166dd3991Sclaudio 				break;
77266dd3991Sclaudio 
77366dd3991Sclaudio 		/* if interface is not found just try to drop membership */
77445daa23aSclaudio 		if (ifg) {
77545daa23aSclaudio 			if (--ifg->count != 0)
77666dd3991Sclaudio 				/* others still joined */
77766dd3991Sclaudio 				return (0);
77866dd3991Sclaudio 
77945daa23aSclaudio 			LIST_REMOVE(ifg, entry);
78045daa23aSclaudio 			free(ifg);
78145daa23aSclaudio 		}
78245daa23aSclaudio 
783263652a1Sclaudio 		memset(&mreq, 0, sizeof(mreq));
784204df0f8Sclaudio 		mreq.imr_multiaddr.s_addr = addr->s_addr;
785b3a8ee7dSclaudio 		mreq.imr_ifindex = iface->ifindex;
786204df0f8Sclaudio 
787204df0f8Sclaudio 		if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
788df69c215Sderaadt 		    (void *)&mreq, sizeof(mreq)) == -1) {
78966dd3991Sclaudio 			log_warn("if_leave_group: error IP_DROP_MEMBERSHIP, "
79066dd3991Sclaudio 			    "interface %s address %s", iface->name,
79166dd3991Sclaudio 			    inet_ntoa(*addr));
792204df0f8Sclaudio 			return (-1);
793204df0f8Sclaudio 		}
794204df0f8Sclaudio 		break;
795204df0f8Sclaudio 	case IF_TYPE_POINTOMULTIPOINT:
796204df0f8Sclaudio 	case IF_TYPE_VIRTUALLINK:
797204df0f8Sclaudio 	case IF_TYPE_NBMA:
798204df0f8Sclaudio 		log_debug("if_leave_group: type %s not supported, interface %s",
799204df0f8Sclaudio 		    if_type_name(iface->type), iface->name);
800204df0f8Sclaudio 		return (-1);
801204df0f8Sclaudio 	default:
802204df0f8Sclaudio 		fatalx("if_leave_group: unknown interface type");
803204df0f8Sclaudio 	}
804204df0f8Sclaudio 
805204df0f8Sclaudio 	return (0);
806204df0f8Sclaudio }
807204df0f8Sclaudio 
808204df0f8Sclaudio int
809204df0f8Sclaudio if_set_mcast(struct iface *iface)
810204df0f8Sclaudio {
811263652a1Sclaudio 	struct ip_mreqn		 mreq;
812263652a1Sclaudio 
813204df0f8Sclaudio 	switch (iface->type) {
814feb9f399Snorby 	case IF_TYPE_POINTOPOINT:
815204df0f8Sclaudio 	case IF_TYPE_BROADCAST:
816263652a1Sclaudio 		memset(&mreq, 0, sizeof(mreq));
817263652a1Sclaudio 		mreq.imr_ifindex = iface->ifindex;
818204df0f8Sclaudio 		if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF,
819263652a1Sclaudio 		    &mreq, sizeof(mreq)) == -1) {
82045daa23aSclaudio 			log_warn("if_set_mcast: error setting "
821204df0f8Sclaudio 			    "IP_MULTICAST_IF, interface %s", iface->name);
822204df0f8Sclaudio 			return (-1);
823204df0f8Sclaudio 		}
824204df0f8Sclaudio 		break;
825204df0f8Sclaudio 	case IF_TYPE_POINTOMULTIPOINT:
826204df0f8Sclaudio 	case IF_TYPE_VIRTUALLINK:
827204df0f8Sclaudio 	case IF_TYPE_NBMA:
828204df0f8Sclaudio 		log_debug("if_set_mcast: type %s not supported, interface %s",
829204df0f8Sclaudio 		    if_type_name(iface->type), iface->name);
830204df0f8Sclaudio 		return (-1);
831204df0f8Sclaudio 	default:
832204df0f8Sclaudio 		fatalx("if_set_mcast: unknown interface type");
833204df0f8Sclaudio 	}
834204df0f8Sclaudio 
835204df0f8Sclaudio 	return (0);
836204df0f8Sclaudio }
837204df0f8Sclaudio 
838204df0f8Sclaudio int
839204df0f8Sclaudio if_set_mcast_loop(int fd)
840204df0f8Sclaudio {
841204df0f8Sclaudio 	u_int8_t	loop = 0;
842204df0f8Sclaudio 
843204df0f8Sclaudio 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
844df69c215Sderaadt 	    (char *)&loop, sizeof(loop)) == -1) {
845204df0f8Sclaudio 		log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
846204df0f8Sclaudio 		return (-1);
847204df0f8Sclaudio 	}
848204df0f8Sclaudio 
849204df0f8Sclaudio 	return (0);
850204df0f8Sclaudio }
85166dd3991Sclaudio 
85266dd3991Sclaudio int
85366dd3991Sclaudio if_set_ip_hdrincl(int fd)
85466dd3991Sclaudio {
85566dd3991Sclaudio 	int	hincl = 1;
85666dd3991Sclaudio 
857df69c215Sderaadt 	if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &hincl, sizeof(hincl)) == -1) {
85866dd3991Sclaudio 		log_warn("if_set_ip_hdrincl: error setting IP_HDRINCL");
85966dd3991Sclaudio 		return (-1);
86066dd3991Sclaudio 	}
86166dd3991Sclaudio 
86266dd3991Sclaudio 	return (0);
86366dd3991Sclaudio }
864