xref: /openbsd/usr.sbin/dvmrpd/group.c (revision 5376bb5a)
1*5376bb5aSkrw /*	$OpenBSD: group.c,v 1.4 2014/11/18 20:54:28 krw Exp $ */
2978e5cffSnorby 
3978e5cffSnorby /*
4978e5cffSnorby  * Copyright (c) 2006 Esben Norby <norby@openbsd.org>
5978e5cffSnorby  *
6978e5cffSnorby  * Permission to use, copy, modify, and distribute this software for any
7978e5cffSnorby  * purpose with or without fee is hereby granted, provided that the above
8978e5cffSnorby  * copyright notice and this permission notice appear in all copies.
9978e5cffSnorby  *
10978e5cffSnorby  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11978e5cffSnorby  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12978e5cffSnorby  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13978e5cffSnorby  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14978e5cffSnorby  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15978e5cffSnorby  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16978e5cffSnorby  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17978e5cffSnorby  */
18978e5cffSnorby 
19978e5cffSnorby #include <sys/types.h>
20978e5cffSnorby #include <sys/socket.h>
21978e5cffSnorby #include <netinet/in.h>
22978e5cffSnorby #include <arpa/inet.h>
23978e5cffSnorby #include <sys/time.h>
24978e5cffSnorby #include <stdlib.h>
25978e5cffSnorby #include <string.h>
26978e5cffSnorby #include <event.h>
27978e5cffSnorby 
28978e5cffSnorby #include "igmp.h"
29978e5cffSnorby #include "dvmrpd.h"
30978e5cffSnorby #include "dvmrp.h"
31978e5cffSnorby #include "dvmrpe.h"
32978e5cffSnorby #include "log.h"
33978e5cffSnorby #include "control.h"
34978e5cffSnorby 
35978e5cffSnorby void	dead_timer(int, short, void *);
36978e5cffSnorby int	start_dead_timer(struct group *);
37978e5cffSnorby int	start_dead_timer_all(struct group *);
38978e5cffSnorby int	stop_dead_timer(struct group *);
39978e5cffSnorby 
40978e5cffSnorby void	v1_host_timer(int, short, void *);
41978e5cffSnorby int	start_v1_host_timer(struct group *);
42978e5cffSnorby int	stop_v1_host_timer(struct group *);
43978e5cffSnorby 
44978e5cffSnorby void	retrans_timer(int, short, void *);
45978e5cffSnorby int	start_retrans_timer(struct group *);
46978e5cffSnorby int	stop_retrans_timer(struct group *);
47978e5cffSnorby 
48978e5cffSnorby extern struct dvmrpd_conf	*deconf;
49978e5cffSnorby 
50978e5cffSnorby #define MAX_ACTIONS	4
51978e5cffSnorby 
52978e5cffSnorby struct {
53978e5cffSnorby 	int			state;
54978e5cffSnorby 	enum group_event	event;
55978e5cffSnorby 	enum group_action	action[MAX_ACTIONS];
56978e5cffSnorby 	int			new_state;
57978e5cffSnorby } grp_fsm[] = {
58978e5cffSnorby     /* current state		event that happened	action(s) to take		resulting state */
59978e5cffSnorby     /* querier fsm */
6063b350beSmichele     {GRP_STA_NO_MEMB_PRSNT,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_ADD_GROUP,
61978e5cffSnorby 							 GRP_ACT_START_TMR,
62978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
6363b350beSmichele     {GRP_STA_NO_MEMB_PRSNT,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_ADD_GROUP,
64978e5cffSnorby 							 GRP_ACT_START_TMR,
65978e5cffSnorby 							 GRP_ACT_START_V1_HOST_TMR,
66978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
67978e5cffSnorby 
6863b350beSmichele     {GRP_STA_MEMB_PRSNT,	GRP_EVT_TMR_EXPIRED,	{GRP_ACT_DEL_GROUP,
69978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_NO_MEMB_PRSNT},
70978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_START_TMR,
71978e5cffSnorby 							 GRP_ACT_END},			0},
72978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_START_TMR,
73978e5cffSnorby 							 GRP_ACT_START_V1_HOST_TMR,
74978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_V1_MEMB_PRSNT},
75978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_LEAVE_RCVD,	{GRP_ACT_START_TMR_ALL,
76978e5cffSnorby 							 GRP_ACT_START_RETRANS_TMR,
77978e5cffSnorby 							 GRP_ACT_SEND_GRP_QUERY,
78978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_CHECK_MEMB},
79978e5cffSnorby 
80978e5cffSnorby     {GRP_STA_CHECK_MEMB,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_START_TMR,
81978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
8263b350beSmichele     {GRP_STA_CHECK_MEMB,	GRP_EVT_TMR_EXPIRED,	{GRP_ACT_DEL_GROUP,
83978e5cffSnorby 							 GRP_ACT_CLR_RETRANS_TMR,
84978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_NO_MEMB_PRSNT},
85978e5cffSnorby     {GRP_STA_CHECK_MEMB,	GRP_EVT_RETRANS_TMR_EXP,{GRP_ACT_SEND_GRP_QUERY,
86978e5cffSnorby 							 GRP_ACT_START_RETRANS_TMR,
87978e5cffSnorby 							 GRP_ACT_END},			0},
88978e5cffSnorby     {GRP_STA_CHECK_MEMB,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_START_TMR,
89978e5cffSnorby 							 GRP_ACT_START_V1_HOST_TMR,
90978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_V1_MEMB_PRSNT},
91978e5cffSnorby 
92978e5cffSnorby     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_V1_HOST_TMR_EXP,{GRP_ACT_NOTHING,
93978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
94978e5cffSnorby     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_START_TMR,
95978e5cffSnorby 							 GRP_ACT_START_V1_HOST_TMR,
96978e5cffSnorby 							 GRP_ACT_END},			0},
97978e5cffSnorby     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_START_TMR,
98978e5cffSnorby 							 GRP_ACT_END},			0},
9963b350beSmichele     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_TMR_EXPIRED,	{GRP_ACT_DEL_GROUP,
100978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_NO_MEMB_PRSNT},
101978e5cffSnorby 
102978e5cffSnorby     /* non querier fsm */
10363b350beSmichele     {GRP_STA_NO_MEMB_PRSNT,	GRP_EVT_REPORT_RCVD,	{GRP_ACT_ADD_GROUP,
104978e5cffSnorby 							 GRP_ACT_START_TMR,
105978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
106978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_REPORT_RCVD,	{GRP_ACT_START_TMR,
107978e5cffSnorby 							 GRP_ACT_END},			0},
108978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_QUERY_RCVD,	{GRP_ACT_START_TMR_ALL,
109978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_CHECK_MEMB},
110978e5cffSnorby 
111978e5cffSnorby     {GRP_STA_CHECK_MEMB,	GRP_EVT_REPORT_RCVD,	{GRP_ACT_START_TMR,
112978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
113978e5cffSnorby     {-1,			GRP_EVT_NOTHING,	{GRP_ACT_NOTHING,
114978e5cffSnorby 							 GRP_ACT_END},			0},
115978e5cffSnorby };
116978e5cffSnorby 
117978e5cffSnorby const char * const group_action_names[] = {
118978e5cffSnorby 	"END MARKER",
119978e5cffSnorby 	"START TIMER",
120978e5cffSnorby 	"START ALL TIMER",
121978e5cffSnorby 	"START RETRANSMISSION TIMER",
122978e5cffSnorby 	"START V1 HOST TIMER",
123978e5cffSnorby 	"SEND GROUP QUERY",
12463b350beSmichele 	"ADD GROUP",
12563b350beSmichele 	"DEL GROUP",
126978e5cffSnorby 	"CLEAR RETRANSMISSION TIMER",
127978e5cffSnorby 	"NOTHING"
128978e5cffSnorby };
129978e5cffSnorby 
130978e5cffSnorby static const char * const group_event_names[] = {
131978e5cffSnorby 	"V2 REPORT RCVD",
132978e5cffSnorby 	"V1 REPORT RCVD",
133978e5cffSnorby 	"LEAVE RCVD",
134978e5cffSnorby 	"TIMER EXPIRED",
135978e5cffSnorby 	"RETRANS TIMER EXPIRED",
136978e5cffSnorby 	"V1 HOST TIMER EXPIRED",
137978e5cffSnorby 	"REPORT RCVD",
138978e5cffSnorby 	"QUERY RCVD",
139978e5cffSnorby 	"NOTHING"
140978e5cffSnorby };
141978e5cffSnorby 
142978e5cffSnorby int
group_fsm(struct group * group,enum group_event event)143978e5cffSnorby group_fsm(struct group *group, enum group_event event)
144978e5cffSnorby {
145978e5cffSnorby 	struct mfc	mfc;
146978e5cffSnorby 	int		old_state;
147978e5cffSnorby 	int		new_state = 0;
148978e5cffSnorby 	int		i, j, ret = 0;
149978e5cffSnorby 
150978e5cffSnorby 	old_state = group->state;
151978e5cffSnorby 
152978e5cffSnorby 	for (i = 0; grp_fsm[i].state != -1; i++)
153978e5cffSnorby 		if ((grp_fsm[i].state & old_state) &&
154978e5cffSnorby 		    (grp_fsm[i].event == event)) {
155978e5cffSnorby 			new_state = grp_fsm[i].new_state;
156978e5cffSnorby 			break;
157978e5cffSnorby 		}
158978e5cffSnorby 
159978e5cffSnorby 	if (grp_fsm[i].state == -1) {
160978e5cffSnorby 		/* XXX event outside of the defined fsm, ignore it. */
161978e5cffSnorby 		log_debug("group_fsm: group %s, event '%s' not expected in "
162978e5cffSnorby 		    "state '%s'", inet_ntoa(group->addr),
163978e5cffSnorby 		    group_event_name(event), group_state_name(old_state));
164978e5cffSnorby 		return (0);
165978e5cffSnorby 	}
166978e5cffSnorby 
167978e5cffSnorby 	for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++) {
168978e5cffSnorby 		switch (grp_fsm[i].action[j]) {
169978e5cffSnorby 		case GRP_ACT_START_TMR:
170978e5cffSnorby 			ret = start_dead_timer(group);
171978e5cffSnorby 			break;
172978e5cffSnorby 		case GRP_ACT_START_TMR_ALL:
173978e5cffSnorby 			ret = start_dead_timer_all(group);
174978e5cffSnorby 			break;
175978e5cffSnorby 		case GRP_ACT_START_RETRANS_TMR:
176978e5cffSnorby 			ret = start_retrans_timer(group);
177978e5cffSnorby 			break;
178978e5cffSnorby 		case GRP_ACT_START_V1_HOST_TMR:
179978e5cffSnorby 			ret = start_v1_host_timer(group);
180978e5cffSnorby 			break;
181978e5cffSnorby 		case GRP_ACT_SEND_GRP_QUERY:
182978e5cffSnorby 			ret = send_igmp_query(group->iface, group);
183978e5cffSnorby 			break;
18463b350beSmichele 		case GRP_ACT_ADD_GROUP:
185978e5cffSnorby 			mfc.origin.s_addr = 0;
186978e5cffSnorby 			mfc.group = group->addr;
18763b350beSmichele 			mfc.ifindex = group->iface->ifindex;
18863b350beSmichele 			dvmrpe_imsg_compose_rde(IMSG_GROUP_ADD, 0, 0, &mfc,
189978e5cffSnorby 			    sizeof(mfc));
190978e5cffSnorby 			break;
19163b350beSmichele 		case GRP_ACT_DEL_GROUP:
192978e5cffSnorby 			mfc.origin.s_addr = 0;
193978e5cffSnorby 			mfc.group = group->addr;
19463b350beSmichele 			mfc.ifindex = group->iface->ifindex;
19563b350beSmichele 			dvmrpe_imsg_compose_rde(IMSG_GROUP_DEL, 0, 0, &mfc,
196978e5cffSnorby 			    sizeof(mfc));
197978e5cffSnorby 			break;
198978e5cffSnorby 		case GRP_ACT_CLR_RETRANS_TMR:
199978e5cffSnorby 			ret = stop_retrans_timer(group);
200978e5cffSnorby 			break;
201978e5cffSnorby 		case GRP_ACT_NOTHING:
202978e5cffSnorby 		case GRP_ACT_END:
203978e5cffSnorby 			/* do nothing */
204978e5cffSnorby 			break;
205978e5cffSnorby 		}
206978e5cffSnorby 
207978e5cffSnorby 		if (ret) {
208978e5cffSnorby 			log_debug("group_fsm: error changing state for "
209978e5cffSnorby 			    "group %s, event '%s', state '%s'",
210978e5cffSnorby 			    inet_ntoa(group->addr), group_event_name(event),
211978e5cffSnorby 			    group_state_name(old_state));
212978e5cffSnorby 			return (-1);
213978e5cffSnorby 		}
214978e5cffSnorby 	}
215978e5cffSnorby 
216978e5cffSnorby 	if (new_state != 0)
217978e5cffSnorby 		group->state = new_state;
218978e5cffSnorby 
219978e5cffSnorby 	for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++)
220978e5cffSnorby 		log_debug("group_fsm: event '%s' resulted in action '%s' and "
221978e5cffSnorby 		    "changing state for group %s from '%s' to '%s'",
222978e5cffSnorby 		    group_event_name(event),
223978e5cffSnorby 		    group_action_name(grp_fsm[i].action[j]),
224978e5cffSnorby 		    inet_ntoa(group->addr), group_state_name(old_state),
225978e5cffSnorby 		    group_state_name(group->state));
226978e5cffSnorby 
227978e5cffSnorby 	return (ret);
228978e5cffSnorby }
229978e5cffSnorby 
230978e5cffSnorby /* timers */
231978e5cffSnorby void
dead_timer(int fd,short event,void * arg)232978e5cffSnorby dead_timer(int fd, short event, void *arg)
233978e5cffSnorby {
234978e5cffSnorby 	struct group *group = arg;
235978e5cffSnorby 
236978e5cffSnorby 	log_debug("dead_timer: %s", inet_ntoa(group->addr));
237978e5cffSnorby 
238978e5cffSnorby 	group_fsm(group, GRP_EVT_TMR_EXPIRED);
239978e5cffSnorby }
240978e5cffSnorby 
241978e5cffSnorby int
start_dead_timer(struct group * group)242978e5cffSnorby start_dead_timer(struct group *group)
243978e5cffSnorby {
244978e5cffSnorby 	struct timeval	tv;
245978e5cffSnorby 
246978e5cffSnorby 	log_debug("start_dead_timer: %s", inet_ntoa(group->addr));
247978e5cffSnorby 
248978e5cffSnorby 	/* Group Membership Interval */
249978e5cffSnorby 	timerclear(&tv);
250978e5cffSnorby 	tv.tv_sec = group->iface->robustness * group->iface->query_interval +
251978e5cffSnorby 	    (group->iface->query_resp_interval / 2);
252978e5cffSnorby 
253978e5cffSnorby 	return (evtimer_add(&group->dead_timer, &tv));
254978e5cffSnorby }
255978e5cffSnorby 
256978e5cffSnorby int
start_dead_timer_all(struct group * group)257978e5cffSnorby start_dead_timer_all(struct group *group)
258978e5cffSnorby {
259978e5cffSnorby 	struct timeval	tv;
260978e5cffSnorby 
261978e5cffSnorby 	log_debug("start_dead_timer_all: %s", inet_ntoa(group->addr));
262978e5cffSnorby 
263978e5cffSnorby 	timerclear(&tv);
264978e5cffSnorby 	if (group->iface->state == IF_STA_QUERIER) {
265978e5cffSnorby 		/* querier */
266978e5cffSnorby 		tv.tv_sec = group->iface->last_member_query_interval *
267978e5cffSnorby 		    group->iface->last_member_query_cnt;
268978e5cffSnorby 	} else {
269978e5cffSnorby 		/* non querier */
270978e5cffSnorby 		/* XXX max response time received in packet */
271978e5cffSnorby 		tv.tv_sec = group->iface->recv_query_resp_interval *
272978e5cffSnorby 		    group->iface->last_member_query_cnt;
273978e5cffSnorby 	}
274978e5cffSnorby 
275978e5cffSnorby 	return (evtimer_add(&group->dead_timer, &tv));
276978e5cffSnorby }
277978e5cffSnorby 
278978e5cffSnorby int
stop_dead_timer(struct group * group)279978e5cffSnorby stop_dead_timer(struct group *group)
280978e5cffSnorby {
281978e5cffSnorby 	log_debug("stop_dead_timer: %s", inet_ntoa(group->addr));
282978e5cffSnorby 
283978e5cffSnorby 	return (evtimer_del(&group->dead_timer));
284978e5cffSnorby }
285978e5cffSnorby 
286978e5cffSnorby void
v1_host_timer(int fd,short event,void * arg)287978e5cffSnorby v1_host_timer(int fd, short event, void *arg)
288978e5cffSnorby {
289978e5cffSnorby 	struct group *group = arg;
290978e5cffSnorby 
291978e5cffSnorby 	log_debug("v1_host_timer: %s", inet_ntoa(group->addr));
292978e5cffSnorby 
293978e5cffSnorby 	group_fsm(group, GRP_EVT_V1_HOST_TMR_EXP);
294978e5cffSnorby }
295978e5cffSnorby 
296978e5cffSnorby int
start_v1_host_timer(struct group * group)297978e5cffSnorby start_v1_host_timer(struct group *group)
298978e5cffSnorby {
299978e5cffSnorby 	struct timeval	tv;
300978e5cffSnorby 
301978e5cffSnorby 	log_debug("start_v1_host_timer: %s", inet_ntoa(group->addr));
302978e5cffSnorby 
303978e5cffSnorby 	/* Group Membership Interval */
304978e5cffSnorby 	timerclear(&tv);
305978e5cffSnorby 	tv.tv_sec = group->iface->robustness * group->iface->query_interval +
306978e5cffSnorby 	    (group->iface->query_resp_interval / 2);
307978e5cffSnorby 
308978e5cffSnorby 	return (evtimer_add(&group->v1_host_timer, &tv));
309978e5cffSnorby }
310978e5cffSnorby 
311978e5cffSnorby int
stop_v1_host_timer(struct group * group)312978e5cffSnorby stop_v1_host_timer(struct group *group)
313978e5cffSnorby {
314978e5cffSnorby 	log_debug("stop_v1_host_timer: %s", inet_ntoa(group->addr));
315978e5cffSnorby 
316978e5cffSnorby 	return (evtimer_del(&group->v1_host_timer));
317978e5cffSnorby }
318978e5cffSnorby 
319978e5cffSnorby void
retrans_timer(int fd,short event,void * arg)320978e5cffSnorby retrans_timer(int fd, short event, void *arg)
321978e5cffSnorby {
322978e5cffSnorby 	struct group *group = arg;
323978e5cffSnorby 	struct timeval tv;
324978e5cffSnorby 
325978e5cffSnorby 	log_debug("retrans_timer: %s", inet_ntoa(group->addr));
326978e5cffSnorby 
327978e5cffSnorby 	send_igmp_query(group->iface, group);
328978e5cffSnorby 
329978e5cffSnorby 	/* reschedule retrans_timer */
330978e5cffSnorby 	if (group->state == GRP_STA_CHECK_MEMB) {
331978e5cffSnorby 		timerclear(&tv);
332978e5cffSnorby 		tv.tv_sec = group->iface->last_member_query_interval;
333978e5cffSnorby 		evtimer_add(&group->retrans_timer, &tv);
334978e5cffSnorby 	}
335978e5cffSnorby }
336978e5cffSnorby 
337978e5cffSnorby int
start_retrans_timer(struct group * group)338978e5cffSnorby start_retrans_timer(struct group *group)
339978e5cffSnorby {
340978e5cffSnorby 	struct timeval	tv;
341978e5cffSnorby 
342978e5cffSnorby 	log_debug("start_retrans_timer: %s", inet_ntoa(group->addr));
343978e5cffSnorby 
344978e5cffSnorby 	timerclear(&tv);
345978e5cffSnorby 	tv.tv_sec = group->iface->last_member_query_interval;
346978e5cffSnorby 
347978e5cffSnorby 	return (evtimer_add(&group->retrans_timer, &tv));
348978e5cffSnorby }
349978e5cffSnorby 
350978e5cffSnorby int
stop_retrans_timer(struct group * group)351978e5cffSnorby stop_retrans_timer(struct group *group)
352978e5cffSnorby {
353978e5cffSnorby 	log_debug("stop_retrans_timer: %s", inet_ntoa(group->addr));
354978e5cffSnorby 
355978e5cffSnorby 	return (evtimer_del(&group->retrans_timer));
356978e5cffSnorby }
357978e5cffSnorby 
358978e5cffSnorby /* group list */
359978e5cffSnorby struct group *
group_list_add(struct iface * iface,u_int32_t group)360978e5cffSnorby group_list_add(struct iface *iface, u_int32_t group)
361978e5cffSnorby {
362978e5cffSnorby 	struct group	*ge;
363978e5cffSnorby 	struct timeval	 now;
364978e5cffSnorby 
365978e5cffSnorby 	/* validate group id */
366978e5cffSnorby 	if (!IN_MULTICAST(htonl(group)))
367978e5cffSnorby 		fatalx("group_list_add: invalid group");
368978e5cffSnorby 
369978e5cffSnorby 	if ((ge = group_list_find(iface, group)) != NULL) {
370978e5cffSnorby 		return (ge);
371978e5cffSnorby 	}
372978e5cffSnorby 
373978e5cffSnorby 	if ((ge = calloc(1, sizeof(*ge))) == NULL)
374978e5cffSnorby 		fatal("group_list_add");
375978e5cffSnorby 
376978e5cffSnorby 	ge->addr.s_addr = group;
377978e5cffSnorby 	ge->state = GRP_STA_NO_MEMB_PRSNT;
378978e5cffSnorby 	evtimer_set(&ge->dead_timer, dead_timer, ge);
379978e5cffSnorby 	evtimer_set(&ge->v1_host_timer, v1_host_timer, ge);
380978e5cffSnorby 	evtimer_set(&ge->retrans_timer, retrans_timer, ge);
381978e5cffSnorby 
382978e5cffSnorby 	gettimeofday(&now, NULL);
383978e5cffSnorby 	ge->uptime = now.tv_sec;
384978e5cffSnorby 
385978e5cffSnorby 	TAILQ_INSERT_TAIL(&iface->group_list, ge, entry);
386978e5cffSnorby 	iface->group_cnt++;
387978e5cffSnorby 
388978e5cffSnorby 	ge->iface = iface;
389978e5cffSnorby 
390978e5cffSnorby 	log_debug("group_list_add: interface %s, group %s", iface->name,
391978e5cffSnorby 	    inet_ntoa(ge->addr));
392978e5cffSnorby 
393978e5cffSnorby 	return (ge);
394978e5cffSnorby }
395978e5cffSnorby 
396978e5cffSnorby void
group_list_remove(struct iface * iface,struct group * group)397978e5cffSnorby group_list_remove(struct iface *iface, struct group *group)
398978e5cffSnorby {
399978e5cffSnorby 	log_debug("group_list_remove: interface %s, group %s", iface->name,
400978e5cffSnorby 	    inet_ntoa(group->addr));
401978e5cffSnorby 
402978e5cffSnorby 	/* stop timers */
403978e5cffSnorby 	stop_dead_timer(group);
404978e5cffSnorby 	start_v1_host_timer(group);
405978e5cffSnorby 	stop_retrans_timer(group);
406978e5cffSnorby 
407978e5cffSnorby 	TAILQ_REMOVE(&iface->group_list, group, entry);
408978e5cffSnorby 	free(group);
409978e5cffSnorby 	iface->group_cnt--;
410978e5cffSnorby }
411978e5cffSnorby 
412978e5cffSnorby struct group *
group_list_find(struct iface * iface,u_int32_t group)413978e5cffSnorby group_list_find(struct iface *iface, u_int32_t group)
414978e5cffSnorby {
415978e5cffSnorby 	struct group	*ge = NULL;
416978e5cffSnorby 
417978e5cffSnorby 	/* validate group id */
418978e5cffSnorby 	if (!IN_MULTICAST(htonl(group)))
419978e5cffSnorby 		fatalx("group_list_find: invalid group");
420978e5cffSnorby 
421978e5cffSnorby 	TAILQ_FOREACH(ge, &iface->group_list, entry) {
422978e5cffSnorby 		if (ge->addr.s_addr == group)
423978e5cffSnorby 			return (ge);
424978e5cffSnorby 	}
425978e5cffSnorby 
426978e5cffSnorby 	return (ge);
427978e5cffSnorby }
428978e5cffSnorby 
429978e5cffSnorby void
group_list_clr(struct iface * iface)430978e5cffSnorby group_list_clr(struct iface *iface)
431978e5cffSnorby {
432978e5cffSnorby 	struct group	*ge;
433978e5cffSnorby 
434978e5cffSnorby 	while ((ge = TAILQ_FIRST(&iface->group_list)) != NULL) {
435978e5cffSnorby 		TAILQ_REMOVE(&iface->group_list, ge, entry);
436978e5cffSnorby 		free(ge);
437978e5cffSnorby 	}
438978e5cffSnorby 	iface->group_cnt = 0;
439978e5cffSnorby }
440978e5cffSnorby 
441978e5cffSnorby int
group_list_empty(struct iface * iface)442978e5cffSnorby group_list_empty(struct iface *iface)
443978e5cffSnorby {
444978e5cffSnorby 	return (TAILQ_EMPTY(&iface->group_list));
445978e5cffSnorby }
446978e5cffSnorby 
447978e5cffSnorby void
group_list_dump(struct iface * iface,struct ctl_conn * c)448978e5cffSnorby group_list_dump(struct iface *iface, struct ctl_conn *c)
449978e5cffSnorby {
450978e5cffSnorby 	struct group		*ge;
451978e5cffSnorby 	struct ctl_group	*gctl;
452978e5cffSnorby 
453978e5cffSnorby 	TAILQ_FOREACH(ge, &iface->group_list, entry) {
454978e5cffSnorby 		gctl = group_to_ctl(ge);
45514d72cd0Spyr 		imsg_compose_event(&c->iev, IMSG_CTL_SHOW_IGMP, 0, 0,
45614d72cd0Spyr 		    -1, gctl, sizeof(struct ctl_group));
457978e5cffSnorby 	}
458978e5cffSnorby }
459978e5cffSnorby 
460978e5cffSnorby /* names */
461978e5cffSnorby const char *
group_event_name(int event)462978e5cffSnorby group_event_name(int event)
463978e5cffSnorby {
464978e5cffSnorby 	return (group_event_names[event]);
465978e5cffSnorby }
466978e5cffSnorby 
467978e5cffSnorby const char *
group_action_name(int action)468978e5cffSnorby group_action_name(int action)
469978e5cffSnorby {
470978e5cffSnorby 	return (group_action_names[action]);
471978e5cffSnorby }
472978e5cffSnorby 
473978e5cffSnorby struct ctl_group *
group_to_ctl(struct group * group)474978e5cffSnorby group_to_ctl(struct group *group)
475978e5cffSnorby {
476978e5cffSnorby 	static struct ctl_group	 gctl;
477978e5cffSnorby 	struct timeval		 tv, now, res;
478978e5cffSnorby 
479978e5cffSnorby 	memcpy(&gctl.addr, &group->addr, sizeof(gctl.addr));
480978e5cffSnorby 
481978e5cffSnorby 	gctl.state = group->state;
482978e5cffSnorby 
483978e5cffSnorby 	gettimeofday(&now, NULL);
484978e5cffSnorby 	if (evtimer_pending(&group->dead_timer, &tv)) {
485978e5cffSnorby 		timersub(&tv, &now, &res);
486978e5cffSnorby 		gctl.dead_timer = res.tv_sec;
487978e5cffSnorby 	} else
488978e5cffSnorby 		gctl.dead_timer = 0;
489978e5cffSnorby 
490978e5cffSnorby 	if (group->state != GRP_STA_NO_MEMB_PRSNT) {
491978e5cffSnorby 		gctl.uptime = now.tv_sec - group->uptime;
492978e5cffSnorby 	} else
493978e5cffSnorby 		gctl.uptime = 0;
494978e5cffSnorby 
495978e5cffSnorby 	return (&gctl);
496978e5cffSnorby }
497