xref: /openbsd-src/usr.sbin/dvmrpd/interface.c (revision 2471dd62a768823e91c9f0fa6ba98a94d7370bc9)
1*2471dd62Sflorian /*	$OpenBSD: interface.c,v 1.13 2024/08/21 09:18:47 florian Exp $ */
2978e5cffSnorby 
3978e5cffSnorby /*
4978e5cffSnorby  * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org>
5978e5cffSnorby  * Copyright (c) 2004, 2005, 2006 Esben Norby <norby@openbsd.org>
6978e5cffSnorby  *
7978e5cffSnorby  * Permission to use, copy, modify, and distribute this software for any
8978e5cffSnorby  * purpose with or without fee is hereby granted, provided that the above
9978e5cffSnorby  * copyright notice and this permission notice appear in all copies.
10978e5cffSnorby  *
11978e5cffSnorby  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12978e5cffSnorby  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13978e5cffSnorby  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14978e5cffSnorby  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15978e5cffSnorby  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16978e5cffSnorby  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17978e5cffSnorby  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18978e5cffSnorby  */
19978e5cffSnorby 
20978e5cffSnorby #include <sys/types.h>
21978e5cffSnorby #include <sys/ioctl.h>
22978e5cffSnorby #include <sys/time.h>
23978e5cffSnorby #include <sys/socket.h>
24978e5cffSnorby 
25978e5cffSnorby #include <netinet/in.h>
26978e5cffSnorby #include <netinet/ip_mroute.h>
27978e5cffSnorby #include <arpa/inet.h>
28978e5cffSnorby #include <net/if.h>
29007f2c42Smichele #include <net/if_types.h>
30978e5cffSnorby 
31978e5cffSnorby #include <ctype.h>
32978e5cffSnorby #include <err.h>
33978e5cffSnorby #include <stdio.h>
34978e5cffSnorby #include <stdlib.h>
35978e5cffSnorby #include <unistd.h>
36978e5cffSnorby #include <string.h>
37978e5cffSnorby #include <event.h>
38978e5cffSnorby 
39978e5cffSnorby #include "igmp.h"
40978e5cffSnorby #include "dvmrpd.h"
41978e5cffSnorby #include "dvmrp.h"
42978e5cffSnorby #include "log.h"
43978e5cffSnorby #include "dvmrpe.h"
44978e5cffSnorby 
45a2ed222dSmichele extern struct dvmrpd_conf	*conf;
46a2ed222dSmichele 
47978e5cffSnorby void	 if_probe_timer(int, short, void *);
48978e5cffSnorby int	 if_start_probe_timer(struct iface *);
49978e5cffSnorby int	 if_stop_probe_timer(struct iface *);
50978e5cffSnorby void	 if_query_timer(int, short, void *);
51978e5cffSnorby int	 if_start_query_timer(struct iface *);
52978e5cffSnorby int	 if_stop_query_timer(struct iface *);
53978e5cffSnorby void	 if_querier_present_timer(int, short, void *);
54978e5cffSnorby int	 if_start_querier_present_timer(struct iface *);
55978e5cffSnorby int	 if_stop_querier_present_timer(struct iface *);
56978e5cffSnorby int	 if_reset_querier_present_timer(struct iface *);
57978e5cffSnorby int	 if_act_start(struct iface *);
58978e5cffSnorby int	 if_act_query_seen(struct iface *);
59978e5cffSnorby int	 if_act_reset(struct iface *);
60978e5cffSnorby 
61978e5cffSnorby struct {
62978e5cffSnorby 	int			state;
63978e5cffSnorby 	enum iface_event	event;
64978e5cffSnorby 	enum iface_action	action;
65978e5cffSnorby 	int			new_state;
66978e5cffSnorby } iface_fsm[] = {
67978e5cffSnorby     /* current state	event that happened	action to take	resulting state */
68978e5cffSnorby     {IF_STA_DOWN,	IF_EVT_UP,		IF_ACT_STRT,	0},
69978e5cffSnorby     {IF_STA_ACTIVE,	IF_EVT_QRECVD,		IF_ACT_QPRSNT,	0},
70978e5cffSnorby     {IF_STA_NONQUERIER, IF_EVT_QPRSNTTMOUT,	IF_ACT_STRT,	0},
71978e5cffSnorby     {IF_STA_ANY,	IF_EVT_DOWN,		IF_ACT_RST,	IF_STA_DOWN},
72978e5cffSnorby     {-1,		IF_EVT_NOTHING,		IF_ACT_NOTHING,	0},
73978e5cffSnorby };
74978e5cffSnorby 
75978e5cffSnorby const char * const if_action_names[] = {
76978e5cffSnorby 	"NOTHING",
77978e5cffSnorby 	"START",
78978e5cffSnorby 	"QPRSNT",
79978e5cffSnorby 	"RESET"
80978e5cffSnorby };
81978e5cffSnorby 
82978e5cffSnorby static const char * const if_event_names[] = {
83978e5cffSnorby 	"NOTHING",
84978e5cffSnorby 	"UP",
85978e5cffSnorby 	"QTMOUT",
86978e5cffSnorby 	"QRECVD",
87978e5cffSnorby 	"QPRSNTTMOUT",
88978e5cffSnorby 	"DOWN"
89978e5cffSnorby };
90978e5cffSnorby 
91978e5cffSnorby int
92978e5cffSnorby if_fsm(struct iface *iface, enum iface_event event)
93978e5cffSnorby {
94978e5cffSnorby 	int	old_state;
95978e5cffSnorby 	int	new_state = 0;
96978e5cffSnorby 	int	i, ret = 0;
97978e5cffSnorby 
98978e5cffSnorby 	old_state = iface->state;
99978e5cffSnorby 
100978e5cffSnorby 	for (i = 0; iface_fsm[i].state != -1; i++)
101978e5cffSnorby 		if ((iface_fsm[i].state & old_state) &&
102978e5cffSnorby 		    (iface_fsm[i].event == event)) {
103978e5cffSnorby 			new_state = iface_fsm[i].new_state;
104978e5cffSnorby 			break;
105978e5cffSnorby 		}
106978e5cffSnorby 
107978e5cffSnorby 	if (iface_fsm[i].state == -1) {
108978e5cffSnorby 		/* XXX event outside of the defined fsm, ignore it. */
109978e5cffSnorby 		log_debug("fsm_if: interface %s, "
110978e5cffSnorby 		    "event '%s' not expected in state '%s'", iface->name,
111978e5cffSnorby 		    if_event_name(event), if_state_name(old_state));
112978e5cffSnorby 		return (0);
113978e5cffSnorby 	}
114978e5cffSnorby 
115978e5cffSnorby 	switch (iface_fsm[i].action) {
116978e5cffSnorby 	case IF_ACT_STRT:
117978e5cffSnorby 		ret = if_act_start(iface);
118978e5cffSnorby 		break;
119978e5cffSnorby 	case IF_ACT_QPRSNT:
120978e5cffSnorby 		ret = if_act_query_seen(iface);
121978e5cffSnorby 		break;
122978e5cffSnorby 	case IF_ACT_RST:
123978e5cffSnorby 		ret = if_act_reset(iface);
124978e5cffSnorby 		break;
125978e5cffSnorby 	case IF_ACT_NOTHING:
126978e5cffSnorby 		/* do nothing */
127978e5cffSnorby 		break;
128978e5cffSnorby 	}
129978e5cffSnorby 
130978e5cffSnorby 	if (ret) {
131978e5cffSnorby 		log_debug("fsm_if: error changing state for interface %s, "
132978e5cffSnorby 		    "event '%s', state '%s'", iface->name, if_event_name(event),
133978e5cffSnorby 		    if_state_name(old_state));
134978e5cffSnorby 		return (-1);
135978e5cffSnorby 	}
136978e5cffSnorby 
137978e5cffSnorby 	if (new_state != 0)
138978e5cffSnorby 		iface->state = new_state;
139978e5cffSnorby 
140978e5cffSnorby 	log_debug("fsm_if: event '%s' resulted in action '%s' and changing "
141978e5cffSnorby 	    "state for interface %s from '%s' to '%s'",
142978e5cffSnorby 	    if_event_name(event), if_action_name(iface_fsm[i].action),
143978e5cffSnorby 	    iface->name, if_state_name(old_state), if_state_name(iface->state));
144978e5cffSnorby 
145978e5cffSnorby 	return (ret);
146978e5cffSnorby }
147978e5cffSnorby 
148978e5cffSnorby struct iface *
149a2ed222dSmichele if_find_index(u_short ifindex)
150a2ed222dSmichele {
151a2ed222dSmichele 	struct iface	*iface;
152a2ed222dSmichele 
153a2ed222dSmichele 	LIST_FOREACH(iface, &conf->iface_list, entry) {
154a2ed222dSmichele 		if (iface->ifindex == ifindex)
155a2ed222dSmichele 			return (iface);
156a2ed222dSmichele 	}
157a2ed222dSmichele 
158a2ed222dSmichele 	return (NULL);
159a2ed222dSmichele }
160a2ed222dSmichele 
161a2ed222dSmichele struct iface *
162978e5cffSnorby if_new(struct kif *kif)
163978e5cffSnorby {
164978e5cffSnorby 	struct sockaddr_in	*sain;
165978e5cffSnorby 	struct iface		*iface;
166978e5cffSnorby 	struct ifreq		*ifr;
167978e5cffSnorby 	int			 s;
168978e5cffSnorby 
169978e5cffSnorby 	if ((iface = calloc(1, sizeof(*iface))) == NULL)
170978e5cffSnorby 		err(1, "if_new: calloc");
171978e5cffSnorby 
172978e5cffSnorby 	iface->state = IF_STA_DOWN;
173978e5cffSnorby 	iface->passive = 1;
174978e5cffSnorby 
175978e5cffSnorby 	LIST_INIT(&iface->nbr_list);
176978e5cffSnorby 	TAILQ_INIT(&iface->group_list);
17763b350beSmichele 	TAILQ_INIT(&iface->rde_group_list);
178978e5cffSnorby 	strlcpy(iface->name, kif->ifname, sizeof(iface->name));
179978e5cffSnorby 
180978e5cffSnorby 	if ((ifr = calloc(1, sizeof(*ifr))) == NULL)
181978e5cffSnorby 		err(1, "if_new: calloc");
182978e5cffSnorby 
183978e5cffSnorby 	/* set up ifreq */
184978e5cffSnorby 	strlcpy(ifr->ifr_name, kif->ifname, sizeof(ifr->ifr_name));
185df69c215Sderaadt 	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
186978e5cffSnorby 		err(1, "if_new: socket");
187978e5cffSnorby 
188978e5cffSnorby 	/* get type */
189978e5cffSnorby 	if ((kif->flags & IFF_POINTOPOINT))
190978e5cffSnorby 		iface->type = IF_TYPE_POINTOPOINT;
191978e5cffSnorby 	if ((kif->flags & IFF_BROADCAST) &&
192978e5cffSnorby 	    (kif->flags & IFF_MULTICAST))
193978e5cffSnorby 		iface->type = IF_TYPE_BROADCAST;
194978e5cffSnorby 
195978e5cffSnorby 	/* get mtu, index and flags */
196978e5cffSnorby 	iface->mtu = kif->mtu;
197978e5cffSnorby 	iface->ifindex = kif->ifindex;
198978e5cffSnorby 	iface->flags = kif->flags;
199978e5cffSnorby 	iface->linkstate = kif->link_state;
2009f69273fSstsp 	iface->if_type = kif->if_type;
201ff83d30fSclaudio 	iface->baudrate = kif->baudrate;
202978e5cffSnorby 
203978e5cffSnorby 	/* get address */
204df69c215Sderaadt 	if (ioctl(s, SIOCGIFADDR, (caddr_t)ifr) == -1)
205978e5cffSnorby 		err(1, "if_new: cannot get address");
206978e5cffSnorby 	sain = (struct sockaddr_in *) &ifr->ifr_addr;
207978e5cffSnorby 	iface->addr = sain->sin_addr;
208978e5cffSnorby 
209978e5cffSnorby 	/* get mask */
210df69c215Sderaadt 	if (ioctl(s, SIOCGIFNETMASK, (caddr_t)ifr) == -1)
211978e5cffSnorby 		err(1, "if_new: cannot get mask");
212978e5cffSnorby 	sain = (struct sockaddr_in *) &ifr->ifr_addr;
213978e5cffSnorby 	iface->mask = sain->sin_addr;
214978e5cffSnorby 
215978e5cffSnorby 	/* get p2p dst address */
216978e5cffSnorby 	if (iface->type == IF_TYPE_POINTOPOINT) {
217df69c215Sderaadt 		if (ioctl(s, SIOCGIFDSTADDR, (caddr_t)ifr) == -1)
218978e5cffSnorby 			err(1, "if_new: cannot get dst addr");
219978e5cffSnorby 		sain = (struct sockaddr_in *) &ifr->ifr_addr;
220978e5cffSnorby 		iface->dst = sain->sin_addr;
221978e5cffSnorby 	}
222978e5cffSnorby 
223978e5cffSnorby 	free(ifr);
224978e5cffSnorby 	close(s);
225978e5cffSnorby 
226978e5cffSnorby 	return (iface);
227978e5cffSnorby }
228978e5cffSnorby 
229978e5cffSnorby void
230978e5cffSnorby if_init(struct dvmrpd_conf *xconf, struct iface *iface)
231978e5cffSnorby {
232978e5cffSnorby 	/* set event handlers for interface */
233978e5cffSnorby 	evtimer_set(&iface->probe_timer, if_probe_timer, iface);
234978e5cffSnorby 	evtimer_set(&iface->query_timer, if_query_timer, iface);
235978e5cffSnorby 	evtimer_set(&iface->querier_present_timer, if_querier_present_timer,
236978e5cffSnorby 	    iface);
237978e5cffSnorby 
238978e5cffSnorby 	TAILQ_INIT(&iface->rr_list);
239978e5cffSnorby 
240978e5cffSnorby 	iface->fd = xconf->dvmrp_socket;
241978e5cffSnorby 	iface->gen_id = xconf->gen_id;
242978e5cffSnorby }
243978e5cffSnorby 
244978e5cffSnorby int
245978e5cffSnorby if_del(struct iface *iface)
246978e5cffSnorby {
247978e5cffSnorby 	struct nbr	*nbr = NULL;
248978e5cffSnorby 
249978e5cffSnorby 	/* clear lists etc */
250978e5cffSnorby 	while ((nbr = LIST_FIRST(&iface->nbr_list)) != NULL) {
251978e5cffSnorby 		LIST_REMOVE(nbr, entry);
252978e5cffSnorby 		nbr_del(nbr);
253978e5cffSnorby 	}
254978e5cffSnorby 	group_list_clr(iface);
255978e5cffSnorby 
256978e5cffSnorby 	return (-1);
257978e5cffSnorby }
258978e5cffSnorby 
259978e5cffSnorby int
260978e5cffSnorby if_nbr_list_empty(struct iface *iface)
261978e5cffSnorby {
262978e5cffSnorby 	return (LIST_EMPTY(&iface->nbr_list));
263978e5cffSnorby }
264978e5cffSnorby 
265978e5cffSnorby /* timers */
266978e5cffSnorby void
267978e5cffSnorby if_probe_timer(int fd, short event, void *arg)
268978e5cffSnorby {
269978e5cffSnorby 	struct iface *iface = arg;
270978e5cffSnorby 	struct timeval tv;
271978e5cffSnorby 
272978e5cffSnorby 	send_probe(iface);
273978e5cffSnorby 
274978e5cffSnorby 	/* reschedule probe_timer */
275978e5cffSnorby 	if (!iface->passive) {
276978e5cffSnorby 		timerclear(&tv);
277978e5cffSnorby 		tv.tv_sec = iface->probe_interval;
278978e5cffSnorby 		evtimer_add(&iface->probe_timer, &tv);
279978e5cffSnorby 	}
280978e5cffSnorby }
281978e5cffSnorby 
282978e5cffSnorby int
283978e5cffSnorby if_start_probe_timer(struct iface *iface)
284978e5cffSnorby {
285978e5cffSnorby 	struct timeval tv;
286978e5cffSnorby 
287978e5cffSnorby 	timerclear(&tv);
288978e5cffSnorby 	return (evtimer_add(&iface->probe_timer, &tv));
289978e5cffSnorby }
290978e5cffSnorby 
291978e5cffSnorby int
292978e5cffSnorby if_stop_probe_timer(struct iface *iface)
293978e5cffSnorby {
294978e5cffSnorby 	return (evtimer_del(&iface->probe_timer));
295978e5cffSnorby }
296978e5cffSnorby 
297978e5cffSnorby void
298978e5cffSnorby if_query_timer(int fd, short event, void *arg)
299978e5cffSnorby {
300978e5cffSnorby 	struct iface *iface = arg;
301978e5cffSnorby 	struct timeval tv;
302978e5cffSnorby 
303978e5cffSnorby 	/* send a general query */
304978e5cffSnorby 	send_igmp_query(iface, NULL);
305978e5cffSnorby 
306978e5cffSnorby 	/* reschedule query_timer */
307978e5cffSnorby 	if (!iface->passive) {
308978e5cffSnorby 		timerclear(&tv);
309978e5cffSnorby 		if (iface->startup_query_counter != 0) {
310978e5cffSnorby 			tv.tv_sec = iface->startup_query_interval;
311978e5cffSnorby 			iface->startup_query_counter--;
312978e5cffSnorby 		} else
313978e5cffSnorby 			tv.tv_sec = iface->query_interval;
314978e5cffSnorby 
315978e5cffSnorby 		evtimer_add(&iface->query_timer, &tv);
316978e5cffSnorby 	}
317978e5cffSnorby }
318978e5cffSnorby 
319978e5cffSnorby int
320978e5cffSnorby if_start_query_timer(struct iface *iface)
321978e5cffSnorby {
322978e5cffSnorby 	struct timeval tv;
323978e5cffSnorby 
324978e5cffSnorby 	timerclear(&tv);
325978e5cffSnorby 	return (evtimer_add(&iface->query_timer, &tv));
326978e5cffSnorby }
327978e5cffSnorby 
328978e5cffSnorby int
329978e5cffSnorby if_stop_query_timer(struct iface *iface)
330978e5cffSnorby {
331978e5cffSnorby 	return (evtimer_del(&iface->query_timer));
332978e5cffSnorby }
333978e5cffSnorby 
334978e5cffSnorby void
335978e5cffSnorby if_querier_present_timer(int fd, short event, void *arg)
336978e5cffSnorby {
337978e5cffSnorby 	struct iface *iface = arg;
338978e5cffSnorby 
339978e5cffSnorby 	if_fsm(iface, IF_EVT_QPRSNTTMOUT);
340978e5cffSnorby }
341978e5cffSnorby 
342978e5cffSnorby int
343978e5cffSnorby if_start_querier_present_timer(struct iface *iface)
344978e5cffSnorby {
345978e5cffSnorby 	struct timeval tv;
346978e5cffSnorby 
347978e5cffSnorby 	/* Other Querier Present Interval */
348978e5cffSnorby 	timerclear(&tv);
349978e5cffSnorby 	tv.tv_sec = iface->robustness * iface->query_interval +
350978e5cffSnorby 	    (iface->query_resp_interval / 2);
351978e5cffSnorby 
352978e5cffSnorby 	return (evtimer_add(&iface->querier_present_timer, &tv));
353978e5cffSnorby }
354978e5cffSnorby 
355978e5cffSnorby int
356978e5cffSnorby if_stop_querier_present_timer(struct iface *iface)
357978e5cffSnorby {
358978e5cffSnorby 	return (evtimer_del(&iface->querier_present_timer));
359978e5cffSnorby }
360978e5cffSnorby 
361978e5cffSnorby int
362978e5cffSnorby if_reset_querier_present_timer(struct iface *iface)
363978e5cffSnorby {
364978e5cffSnorby 	struct timeval	tv;
365978e5cffSnorby 
366978e5cffSnorby 	/* Other Querier Present Interval */
367978e5cffSnorby 	timerclear(&tv);
368978e5cffSnorby 	tv.tv_sec = iface->robustness * iface->query_interval +
369978e5cffSnorby 	    (iface->query_resp_interval / 2);
370978e5cffSnorby 
371978e5cffSnorby 	return (evtimer_add(&iface->querier_present_timer, &tv));
372978e5cffSnorby }
373978e5cffSnorby 
374978e5cffSnorby /* actions */
375978e5cffSnorby int
376978e5cffSnorby if_act_start(struct iface *iface)
377978e5cffSnorby {
378978e5cffSnorby 	struct in_addr	 addr;
379978e5cffSnorby 	struct timeval	 now;
380978e5cffSnorby 
381978e5cffSnorby 	if (iface->passive) {
382978e5cffSnorby 		log_debug("if_act_start: cannot start passive interface %s",
383978e5cffSnorby 		    iface->name);
384978e5cffSnorby 		return (-1);
385978e5cffSnorby 	}
386978e5cffSnorby 
3879a2e0324Sclaudio 	if (!((iface->flags & IFF_UP) && LINK_STATE_IS_UP(iface->linkstate))) {
388978e5cffSnorby 		log_debug("if_act_start: interface %s link down",
389978e5cffSnorby 		    iface->name);
390978e5cffSnorby 		return (0);
391978e5cffSnorby 	}
392978e5cffSnorby 
393978e5cffSnorby 	gettimeofday(&now, NULL);
394978e5cffSnorby 	iface->uptime = now.tv_sec;
395978e5cffSnorby 
396978e5cffSnorby 	switch (iface->type) {
397978e5cffSnorby 	case IF_TYPE_POINTOPOINT:
398978e5cffSnorby 	case IF_TYPE_BROADCAST:
399*2471dd62Sflorian 		inet_pton(AF_INET, AllSystems, &addr);
400978e5cffSnorby 		if (if_join_group(iface, &addr)) {
401978e5cffSnorby 			log_warnx("if_act_start: error joining group %s, "
402978e5cffSnorby 			    "interface %s", inet_ntoa(addr), iface->name);
403978e5cffSnorby 			return (-1);
404978e5cffSnorby 		}
405*2471dd62Sflorian 		inet_pton(AF_INET, AllRouters, &addr);
406978e5cffSnorby 		if (if_join_group(iface, &addr)) {
407978e5cffSnorby 			log_warnx("if_act_start: error joining group %s, "
408978e5cffSnorby 			    "interface %s", inet_ntoa(addr), iface->name);
409978e5cffSnorby 			return (-1);
410978e5cffSnorby 		}
411*2471dd62Sflorian 		inet_pton(AF_INET, AllDVMRPRouters, &addr);
412978e5cffSnorby 		if (if_join_group(iface, &addr)) {
413978e5cffSnorby 			log_warnx("if_act_start: error joining group %s, "
414978e5cffSnorby 			    "interface %s", inet_ntoa(addr), iface->name);
415978e5cffSnorby 			return (-1);
416978e5cffSnorby 		}
417978e5cffSnorby 
418978e5cffSnorby 		iface->state = IF_STA_QUERIER;
419978e5cffSnorby 		if_start_query_timer(iface);
420978e5cffSnorby 		if_start_probe_timer(iface);
421978e5cffSnorby 		iface->startup_query_counter = iface->startup_query_cnt;
422978e5cffSnorby 		break;
423978e5cffSnorby 	default:
424978e5cffSnorby 		fatalx("if_act_start: unknown type");
425978e5cffSnorby 	}
426978e5cffSnorby 
427978e5cffSnorby 	return (0);
428978e5cffSnorby }
429978e5cffSnorby 
430978e5cffSnorby int
431978e5cffSnorby if_act_query_seen(struct iface *iface)
432978e5cffSnorby {
433978e5cffSnorby 	log_debug("if_act_query_seen: interface %s", iface->name);
434978e5cffSnorby 
435978e5cffSnorby 	switch (iface->type) {
436978e5cffSnorby 	case IF_TYPE_POINTOPOINT:
437978e5cffSnorby 	case IF_TYPE_BROADCAST:
438978e5cffSnorby 		iface->state = IF_STA_NONQUERIER;
439978e5cffSnorby 		if_stop_query_timer(iface);
440978e5cffSnorby 		if_reset_querier_present_timer(iface);
441978e5cffSnorby 		break;
442978e5cffSnorby 	default:
443978e5cffSnorby 		fatalx("if_act_querier_seen: unknown type");
444978e5cffSnorby 	}
445978e5cffSnorby 
446978e5cffSnorby 	return (0);
447978e5cffSnorby }
448978e5cffSnorby 
449978e5cffSnorby int
450978e5cffSnorby if_act_reset(struct iface *iface)
451978e5cffSnorby {
452978e5cffSnorby 	struct in_addr	 addr;
453978e5cffSnorby 	struct nbr	*nbr;
454978e5cffSnorby 
455978e5cffSnorby 	switch (iface->type) {
456978e5cffSnorby 	case IF_TYPE_POINTOPOINT:
457978e5cffSnorby 	case IF_TYPE_BROADCAST:
458*2471dd62Sflorian 		inet_pton(AF_INET, AllSystems, &addr);
459978e5cffSnorby 		if (if_leave_group(iface, &addr)) {
460978e5cffSnorby 			log_warnx("if_act_reset: error leaving group %s, "
461978e5cffSnorby 			    "interface %s", inet_ntoa(addr), iface->name);
462978e5cffSnorby 			return (-1);
463978e5cffSnorby 		}
464*2471dd62Sflorian 		inet_pton(AF_INET, AllRouters, &addr);
465978e5cffSnorby 		if (if_leave_group(iface, &addr)) {
466978e5cffSnorby 			log_warnx("if_act_reset: error leaving group %s, "
467978e5cffSnorby 			    "interface %s", inet_ntoa(addr), iface->name);
468978e5cffSnorby 			return (-1);
469978e5cffSnorby 		}
470*2471dd62Sflorian 		inet_pton(AF_INET, AllDVMRPRouters, &addr);
471978e5cffSnorby 		if (if_leave_group(iface, &addr)) {
472978e5cffSnorby 			log_warnx("if_act_reset: error leaving group %s, "
473978e5cffSnorby 			    "interface %s", inet_ntoa(addr), iface->name);
474978e5cffSnorby 			return (-1);
475978e5cffSnorby 		}
476978e5cffSnorby 
477978e5cffSnorby 		iface->state = IF_STA_DOWN;
478978e5cffSnorby 		iface->gen_id++;
479978e5cffSnorby 		if_stop_query_timer(iface);
480978e5cffSnorby 		if_stop_querier_present_timer(iface);
481978e5cffSnorby 		/* XXX clear nbr list? */
482978e5cffSnorby 		break;
483978e5cffSnorby 	default:
484978e5cffSnorby 		fatalx("if_act_reset: unknown type");
485978e5cffSnorby 	}
486978e5cffSnorby 
487978e5cffSnorby 	LIST_FOREACH(nbr, &iface->nbr_list, entry) {
488978e5cffSnorby 		if (nbr_fsm(nbr, NBR_EVT_KILL_NBR)) {
489978e5cffSnorby 			log_debug("if_act_reset: error killing neighbor %s",
490978e5cffSnorby 			    inet_ntoa(nbr->id));
491978e5cffSnorby 		}
492978e5cffSnorby 	}
493978e5cffSnorby 
494978e5cffSnorby 	group_list_clr(iface);	/* XXX clear group list? */
495978e5cffSnorby 
496978e5cffSnorby 	return (0);
497978e5cffSnorby }
498978e5cffSnorby 
499978e5cffSnorby const char *
500978e5cffSnorby if_event_name(int event)
501978e5cffSnorby {
502978e5cffSnorby 	return (if_event_names[event]);
503978e5cffSnorby }
504978e5cffSnorby 
505978e5cffSnorby const char *
506978e5cffSnorby if_action_name(int action)
507978e5cffSnorby {
508978e5cffSnorby 	return (if_action_names[action]);
509978e5cffSnorby }
510978e5cffSnorby 
511978e5cffSnorby /* misc */
512978e5cffSnorby int
513978e5cffSnorby if_set_mcast_ttl(int fd, u_int8_t ttl)
514978e5cffSnorby {
515978e5cffSnorby 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
516df69c215Sderaadt 	    (char *)&ttl, sizeof(ttl)) == -1) {
517978e5cffSnorby 		log_warn("if_set_mcast_ttl: error setting "
518978e5cffSnorby 		    "IP_MULTICAST_TTL to %d", ttl);
519978e5cffSnorby 		return (-1);
520978e5cffSnorby 	}
521978e5cffSnorby 
522978e5cffSnorby 	return (0);
523978e5cffSnorby }
524978e5cffSnorby 
525978e5cffSnorby int
526978e5cffSnorby if_set_tos(int fd, int tos)
527978e5cffSnorby {
528978e5cffSnorby 	if (setsockopt(fd, IPPROTO_IP, IP_TOS,
529df69c215Sderaadt 	    (int *)&tos, sizeof(tos)) == -1) {
530978e5cffSnorby 		log_warn("if_set_tos: error setting IP_TOS to 0x%x", tos);
531978e5cffSnorby 		return (-1);
532978e5cffSnorby 	}
533978e5cffSnorby 
534978e5cffSnorby 	return (0);
535978e5cffSnorby }
536978e5cffSnorby 
537978e5cffSnorby void
538978e5cffSnorby if_set_recvbuf(int fd)
539978e5cffSnorby {
540978e5cffSnorby 	int	bsize;
541978e5cffSnorby 
542978e5cffSnorby 	bsize = 65535;
543978e5cffSnorby 	while (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bsize,
544978e5cffSnorby 	    sizeof(bsize)) == -1)
545978e5cffSnorby 		bsize /= 2;
546978e5cffSnorby }
547978e5cffSnorby 
548978e5cffSnorby int
549978e5cffSnorby if_join_group(struct iface *iface, struct in_addr *addr)
550978e5cffSnorby {
551978e5cffSnorby 	struct ip_mreq	 mreq;
552978e5cffSnorby 
553978e5cffSnorby 	switch (iface->type) {
554978e5cffSnorby 	case IF_TYPE_POINTOPOINT:
555978e5cffSnorby 	case IF_TYPE_BROADCAST:
556978e5cffSnorby 		mreq.imr_multiaddr.s_addr = addr->s_addr;
557978e5cffSnorby 		mreq.imr_interface.s_addr = iface->addr.s_addr;
558978e5cffSnorby 
559978e5cffSnorby 		if (setsockopt(iface->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
560df69c215Sderaadt 		    (void *)&mreq, sizeof(mreq)) == -1) {
561978e5cffSnorby 			log_debug("if_join_group: error IP_ADD_MEMBERSHIP, "
562978e5cffSnorby 			    "interface %s", iface->name);
563978e5cffSnorby 			return (-1);
564978e5cffSnorby 		}
565978e5cffSnorby 		break;
566978e5cffSnorby 	default:
567978e5cffSnorby 		fatalx("if_join_group: unknown interface type");
568978e5cffSnorby 	}
569978e5cffSnorby 
570978e5cffSnorby 	return (0);
571978e5cffSnorby }
572978e5cffSnorby 
573978e5cffSnorby int
574978e5cffSnorby if_leave_group(struct iface *iface, struct in_addr *addr)
575978e5cffSnorby {
576978e5cffSnorby 	struct ip_mreq	 mreq;
577978e5cffSnorby 
578978e5cffSnorby 	switch (iface->type) {
579978e5cffSnorby 	case IF_TYPE_POINTOPOINT:
580978e5cffSnorby 	case IF_TYPE_BROADCAST:
581978e5cffSnorby 		mreq.imr_multiaddr.s_addr = addr->s_addr;
582978e5cffSnorby 		mreq.imr_interface.s_addr = iface->addr.s_addr;
583978e5cffSnorby 
584978e5cffSnorby 		if (setsockopt(iface->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
585df69c215Sderaadt 		    (void *)&mreq, sizeof(mreq)) == -1) {
586978e5cffSnorby 			log_debug("if_leave_group: error IP_DROP_MEMBERSHIP, "
587978e5cffSnorby 			    "interface %s", iface->name);
588978e5cffSnorby 			return (-1);
589978e5cffSnorby 		}
590978e5cffSnorby 		break;
591978e5cffSnorby 	default:
592978e5cffSnorby 		fatalx("if_leave_group: unknown interface type");
593978e5cffSnorby 	}
594978e5cffSnorby 
595978e5cffSnorby 	return (0);
596978e5cffSnorby }
597978e5cffSnorby 
598978e5cffSnorby int
599978e5cffSnorby if_set_mcast(struct iface *iface)
600978e5cffSnorby {
601978e5cffSnorby 	switch (iface->type) {
602978e5cffSnorby 	case IF_TYPE_POINTOPOINT:
603978e5cffSnorby 	case IF_TYPE_BROADCAST:
604978e5cffSnorby 		if (setsockopt(iface->fd, IPPROTO_IP, IP_MULTICAST_IF,
605978e5cffSnorby 		    (char *)&iface->addr.s_addr,
606df69c215Sderaadt 		    sizeof(iface->addr.s_addr)) == -1) {
607978e5cffSnorby 			log_debug("if_set_mcast: error setting "
608978e5cffSnorby 			    "IP_MULTICAST_IF, interface %s", iface->name);
609978e5cffSnorby 			return (-1);
610978e5cffSnorby 		}
611978e5cffSnorby 		break;
612978e5cffSnorby 	default:
613978e5cffSnorby 		fatalx("if_set_mcast: unknown interface type");
614978e5cffSnorby 	}
615978e5cffSnorby 
616978e5cffSnorby 	return (0);
617978e5cffSnorby }
618978e5cffSnorby 
619978e5cffSnorby int
620978e5cffSnorby if_set_mcast_loop(int fd)
621978e5cffSnorby {
622978e5cffSnorby 	u_int8_t	loop = 0;
623978e5cffSnorby 
624978e5cffSnorby 	if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
625df69c215Sderaadt 	    (char *)&loop, sizeof(loop)) == -1) {
626978e5cffSnorby 		log_warn("if_set_mcast_loop: error setting IP_MULTICAST_LOOP");
627978e5cffSnorby 		return (-1);
628978e5cffSnorby 	}
629978e5cffSnorby 
630978e5cffSnorby 	return (0);
631978e5cffSnorby }
632978e5cffSnorby 
633978e5cffSnorby struct ctl_iface *
634978e5cffSnorby if_to_ctl(struct iface *iface)
635978e5cffSnorby {
636978e5cffSnorby 	static struct ctl_iface	 ictl;
637978e5cffSnorby 	struct timeval		 tv, now, res;
638978e5cffSnorby 
639978e5cffSnorby 	memcpy(ictl.name, iface->name, sizeof(ictl.name));
640978e5cffSnorby 	memcpy(&ictl.addr, &iface->addr, sizeof(ictl.addr));
641978e5cffSnorby 	memcpy(&ictl.mask, &iface->mask, sizeof(ictl.mask));
642978e5cffSnorby 	memcpy(&ictl.querier, &iface->querier, sizeof(ictl.querier));
643978e5cffSnorby 
644978e5cffSnorby 	ictl.ifindex = iface->ifindex;
645978e5cffSnorby 	ictl.state = iface->state;
646978e5cffSnorby 	ictl.mtu = iface->mtu;
6478464310fStodd 	ictl.nbr_cnt = iface->nbr_cnt;
648d0cc1e6eSnorby 	ictl.adj_cnt = iface->adj_cnt;
649978e5cffSnorby 
650978e5cffSnorby 	ictl.gen_id = iface->gen_id;
651978e5cffSnorby 	ictl.group_cnt = iface->group_cnt;
652978e5cffSnorby 	ictl.probe_interval = iface->probe_interval;
653978e5cffSnorby 	ictl.query_interval = iface->query_interval;
654978e5cffSnorby 	ictl.query_resp_interval = iface->query_resp_interval;
655978e5cffSnorby 	ictl.recv_query_resp_interval = iface->recv_query_resp_interval;
656978e5cffSnorby 	ictl.group_member_interval = iface->group_member_interval;
657978e5cffSnorby 	ictl.querier_present_interval = iface->querier_present_interval;
658978e5cffSnorby 	ictl.startup_query_interval = iface->startup_query_interval;
659978e5cffSnorby 	ictl.startup_query_cnt = iface->startup_query_cnt;
660978e5cffSnorby 	ictl.last_member_query_interval = iface->last_member_query_interval;
661978e5cffSnorby 	ictl.last_member_query_cnt = iface->last_member_query_cnt;
662978e5cffSnorby 	ictl.last_member_query_time = iface->last_member_query_time;
663978e5cffSnorby 	ictl.v1_querier_present_tmout = iface->v1_querier_present_tmout;
664978e5cffSnorby 	ictl.v1_host_present_interval = iface->v1_host_present_interval;
665978e5cffSnorby 	ictl.dead_interval = iface->dead_interval;
666978e5cffSnorby 
667978e5cffSnorby 	ictl.baudrate = iface->baudrate;
668978e5cffSnorby 	ictl.flags = iface->flags;
669978e5cffSnorby 	ictl.metric = iface->metric;
670978e5cffSnorby 	ictl.type = iface->type;
671978e5cffSnorby 	ictl.robustness = iface->robustness;
672978e5cffSnorby 	ictl.linkstate = iface->linkstate;
673978e5cffSnorby 	ictl.passive = iface->passive;
674978e5cffSnorby 	ictl.igmp_version = iface->igmp_version;
6759f69273fSstsp 	ictl.if_type = iface->if_type;
676978e5cffSnorby 
677978e5cffSnorby 	gettimeofday(&now, NULL);
678978e5cffSnorby 	if (evtimer_pending(&iface->probe_timer, &tv)) {
679978e5cffSnorby 		timersub(&tv, &now, &res);
680978e5cffSnorby 		ictl.probe_timer = res.tv_sec;
681978e5cffSnorby 	} else
682978e5cffSnorby 		ictl.probe_timer = -1;
683978e5cffSnorby 
684978e5cffSnorby 	if (evtimer_pending(&iface->query_timer, &tv)) {
685978e5cffSnorby 		timersub(&tv, &now, &res);
686978e5cffSnorby 		ictl.query_timer = res.tv_sec;
687978e5cffSnorby 	} else
688978e5cffSnorby 		ictl.query_timer = -1;
689978e5cffSnorby 
690978e5cffSnorby 	if (evtimer_pending(&iface->querier_present_timer, &tv)) {
691978e5cffSnorby 		timersub(&tv, &now, &res);
692978e5cffSnorby 		ictl.querier_present_timer = res.tv_sec;
693978e5cffSnorby 	} else
694978e5cffSnorby 		ictl.querier_present_timer = -1;
695978e5cffSnorby 
696978e5cffSnorby 	if (iface->state != IF_STA_DOWN) {
697978e5cffSnorby 		ictl.uptime = now.tv_sec - iface->uptime;
698978e5cffSnorby 	} else
699978e5cffSnorby 		ictl.uptime = 0;
700978e5cffSnorby 
701978e5cffSnorby 	return (&ictl);
702978e5cffSnorby }
703