xref: /openbsd/usr.sbin/dvmrpd/group.c (revision 14d72cd0)
1*14d72cd0Spyr /*	$OpenBSD: group.c,v 1.3 2009/06/06 07:52:04 pyr 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 <sys/socket.h>
25978e5cffSnorby #include <stdlib.h>
26978e5cffSnorby #include <string.h>
27978e5cffSnorby #include <event.h>
28978e5cffSnorby 
29978e5cffSnorby #include "igmp.h"
30978e5cffSnorby #include "dvmrpd.h"
31978e5cffSnorby #include "dvmrp.h"
32978e5cffSnorby #include "dvmrpe.h"
33978e5cffSnorby #include "log.h"
34978e5cffSnorby #include "control.h"
35978e5cffSnorby 
36978e5cffSnorby void	dead_timer(int, short, void *);
37978e5cffSnorby int	start_dead_timer(struct group *);
38978e5cffSnorby int	start_dead_timer_all(struct group *);
39978e5cffSnorby int	stop_dead_timer(struct group *);
40978e5cffSnorby 
41978e5cffSnorby void	v1_host_timer(int, short, void *);
42978e5cffSnorby int	start_v1_host_timer(struct group *);
43978e5cffSnorby int	stop_v1_host_timer(struct group *);
44978e5cffSnorby 
45978e5cffSnorby void	retrans_timer(int, short, void *);
46978e5cffSnorby int	start_retrans_timer(struct group *);
47978e5cffSnorby int	stop_retrans_timer(struct group *);
48978e5cffSnorby 
49978e5cffSnorby extern struct dvmrpd_conf	*deconf;
50978e5cffSnorby 
51978e5cffSnorby #define MAX_ACTIONS	4
52978e5cffSnorby 
53978e5cffSnorby struct {
54978e5cffSnorby 	int			state;
55978e5cffSnorby 	enum group_event	event;
56978e5cffSnorby 	enum group_action	action[MAX_ACTIONS];
57978e5cffSnorby 	int			new_state;
58978e5cffSnorby } grp_fsm[] = {
59978e5cffSnorby     /* current state		event that happened	action(s) to take		resulting state */
60978e5cffSnorby     /* querier fsm */
6163b350beSmichele     {GRP_STA_NO_MEMB_PRSNT,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_ADD_GROUP,
62978e5cffSnorby 							 GRP_ACT_START_TMR,
63978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
6463b350beSmichele     {GRP_STA_NO_MEMB_PRSNT,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_ADD_GROUP,
65978e5cffSnorby 							 GRP_ACT_START_TMR,
66978e5cffSnorby 							 GRP_ACT_START_V1_HOST_TMR,
67978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
68978e5cffSnorby 
6963b350beSmichele     {GRP_STA_MEMB_PRSNT,	GRP_EVT_TMR_EXPIRED,	{GRP_ACT_DEL_GROUP,
70978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_NO_MEMB_PRSNT},
71978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_START_TMR,
72978e5cffSnorby 							 GRP_ACT_END},			0},
73978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_START_TMR,
74978e5cffSnorby 							 GRP_ACT_START_V1_HOST_TMR,
75978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_V1_MEMB_PRSNT},
76978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_LEAVE_RCVD,	{GRP_ACT_START_TMR_ALL,
77978e5cffSnorby 							 GRP_ACT_START_RETRANS_TMR,
78978e5cffSnorby 							 GRP_ACT_SEND_GRP_QUERY,
79978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_CHECK_MEMB},
80978e5cffSnorby 
81978e5cffSnorby     {GRP_STA_CHECK_MEMB,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_START_TMR,
82978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
8363b350beSmichele     {GRP_STA_CHECK_MEMB,	GRP_EVT_TMR_EXPIRED,	{GRP_ACT_DEL_GROUP,
84978e5cffSnorby 							 GRP_ACT_CLR_RETRANS_TMR,
85978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_NO_MEMB_PRSNT},
86978e5cffSnorby     {GRP_STA_CHECK_MEMB,	GRP_EVT_RETRANS_TMR_EXP,{GRP_ACT_SEND_GRP_QUERY,
87978e5cffSnorby 							 GRP_ACT_START_RETRANS_TMR,
88978e5cffSnorby 							 GRP_ACT_END},			0},
89978e5cffSnorby     {GRP_STA_CHECK_MEMB,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_START_TMR,
90978e5cffSnorby 							 GRP_ACT_START_V1_HOST_TMR,
91978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_V1_MEMB_PRSNT},
92978e5cffSnorby 
93978e5cffSnorby     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_V1_HOST_TMR_EXP,{GRP_ACT_NOTHING,
94978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
95978e5cffSnorby     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_START_TMR,
96978e5cffSnorby 							 GRP_ACT_START_V1_HOST_TMR,
97978e5cffSnorby 							 GRP_ACT_END},			0},
98978e5cffSnorby     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_START_TMR,
99978e5cffSnorby 							 GRP_ACT_END},			0},
10063b350beSmichele     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_TMR_EXPIRED,	{GRP_ACT_DEL_GROUP,
101978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_NO_MEMB_PRSNT},
102978e5cffSnorby 
103978e5cffSnorby     /* non querier fsm */
10463b350beSmichele     {GRP_STA_NO_MEMB_PRSNT,	GRP_EVT_REPORT_RCVD,	{GRP_ACT_ADD_GROUP,
105978e5cffSnorby 							 GRP_ACT_START_TMR,
106978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
107978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_REPORT_RCVD,	{GRP_ACT_START_TMR,
108978e5cffSnorby 							 GRP_ACT_END},			0},
109978e5cffSnorby     {GRP_STA_MEMB_PRSNT,	GRP_EVT_QUERY_RCVD,	{GRP_ACT_START_TMR_ALL,
110978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_CHECK_MEMB},
111978e5cffSnorby 
112978e5cffSnorby     {GRP_STA_CHECK_MEMB,	GRP_EVT_REPORT_RCVD,	{GRP_ACT_START_TMR,
113978e5cffSnorby 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
114978e5cffSnorby     {-1,			GRP_EVT_NOTHING,	{GRP_ACT_NOTHING,
115978e5cffSnorby 							 GRP_ACT_END},			0},
116978e5cffSnorby };
117978e5cffSnorby 
118978e5cffSnorby const char * const group_action_names[] = {
119978e5cffSnorby 	"END MARKER",
120978e5cffSnorby 	"START TIMER",
121978e5cffSnorby 	"START ALL TIMER",
122978e5cffSnorby 	"START RETRANSMISSION TIMER",
123978e5cffSnorby 	"START V1 HOST TIMER",
124978e5cffSnorby 	"SEND GROUP QUERY",
12563b350beSmichele 	"ADD GROUP",
12663b350beSmichele 	"DEL GROUP",
127978e5cffSnorby 	"CLEAR RETRANSMISSION TIMER",
128978e5cffSnorby 	"NOTHING"
129978e5cffSnorby };
130978e5cffSnorby 
131978e5cffSnorby static const char * const group_event_names[] = {
132978e5cffSnorby 	"V2 REPORT RCVD",
133978e5cffSnorby 	"V1 REPORT RCVD",
134978e5cffSnorby 	"LEAVE RCVD",
135978e5cffSnorby 	"TIMER EXPIRED",
136978e5cffSnorby 	"RETRANS TIMER EXPIRED",
137978e5cffSnorby 	"V1 HOST TIMER EXPIRED",
138978e5cffSnorby 	"REPORT RCVD",
139978e5cffSnorby 	"QUERY RCVD",
140978e5cffSnorby 	"NOTHING"
141978e5cffSnorby };
142978e5cffSnorby 
143978e5cffSnorby int
144978e5cffSnorby group_fsm(struct group *group, enum group_event event)
145978e5cffSnorby {
146978e5cffSnorby 	struct mfc	mfc;
147978e5cffSnorby 	int		old_state;
148978e5cffSnorby 	int		new_state = 0;
149978e5cffSnorby 	int		i, j, ret = 0;
150978e5cffSnorby 
151978e5cffSnorby 	old_state = group->state;
152978e5cffSnorby 
153978e5cffSnorby 	for (i = 0; grp_fsm[i].state != -1; i++)
154978e5cffSnorby 		if ((grp_fsm[i].state & old_state) &&
155978e5cffSnorby 		    (grp_fsm[i].event == event)) {
156978e5cffSnorby 			new_state = grp_fsm[i].new_state;
157978e5cffSnorby 			break;
158978e5cffSnorby 		}
159978e5cffSnorby 
160978e5cffSnorby 	if (grp_fsm[i].state == -1) {
161978e5cffSnorby 		/* XXX event outside of the defined fsm, ignore it. */
162978e5cffSnorby 		log_debug("group_fsm: group %s, event '%s' not expected in "
163978e5cffSnorby 		    "state '%s'", inet_ntoa(group->addr),
164978e5cffSnorby 		    group_event_name(event), group_state_name(old_state));
165978e5cffSnorby 		return (0);
166978e5cffSnorby 	}
167978e5cffSnorby 
168978e5cffSnorby 	for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++) {
169978e5cffSnorby 		switch (grp_fsm[i].action[j]) {
170978e5cffSnorby 		case GRP_ACT_START_TMR:
171978e5cffSnorby 			ret = start_dead_timer(group);
172978e5cffSnorby 			break;
173978e5cffSnorby 		case GRP_ACT_START_TMR_ALL:
174978e5cffSnorby 			ret = start_dead_timer_all(group);
175978e5cffSnorby 			break;
176978e5cffSnorby 		case GRP_ACT_START_RETRANS_TMR:
177978e5cffSnorby 			ret = start_retrans_timer(group);
178978e5cffSnorby 			break;
179978e5cffSnorby 		case GRP_ACT_START_V1_HOST_TMR:
180978e5cffSnorby 			ret = start_v1_host_timer(group);
181978e5cffSnorby 			break;
182978e5cffSnorby 		case GRP_ACT_SEND_GRP_QUERY:
183978e5cffSnorby 			ret = send_igmp_query(group->iface, group);
184978e5cffSnorby 			break;
18563b350beSmichele 		case GRP_ACT_ADD_GROUP:
186978e5cffSnorby 			mfc.origin.s_addr = 0;
187978e5cffSnorby 			mfc.group = group->addr;
18863b350beSmichele 			mfc.ifindex = group->iface->ifindex;
18963b350beSmichele 			dvmrpe_imsg_compose_rde(IMSG_GROUP_ADD, 0, 0, &mfc,
190978e5cffSnorby 			    sizeof(mfc));
191978e5cffSnorby 			break;
19263b350beSmichele 		case GRP_ACT_DEL_GROUP:
193978e5cffSnorby 			mfc.origin.s_addr = 0;
194978e5cffSnorby 			mfc.group = group->addr;
19563b350beSmichele 			mfc.ifindex = group->iface->ifindex;
19663b350beSmichele 			dvmrpe_imsg_compose_rde(IMSG_GROUP_DEL, 0, 0, &mfc,
197978e5cffSnorby 			    sizeof(mfc));
198978e5cffSnorby 			break;
199978e5cffSnorby 		case GRP_ACT_CLR_RETRANS_TMR:
200978e5cffSnorby 			ret = stop_retrans_timer(group);
201978e5cffSnorby 			break;
202978e5cffSnorby 		case GRP_ACT_NOTHING:
203978e5cffSnorby 		case GRP_ACT_END:
204978e5cffSnorby 			/* do nothing */
205978e5cffSnorby 			break;
206978e5cffSnorby 		}
207978e5cffSnorby 
208978e5cffSnorby 		if (ret) {
209978e5cffSnorby 			log_debug("group_fsm: error changing state for "
210978e5cffSnorby 			    "group %s, event '%s', state '%s'",
211978e5cffSnorby 			    inet_ntoa(group->addr), group_event_name(event),
212978e5cffSnorby 			    group_state_name(old_state));
213978e5cffSnorby 			return (-1);
214978e5cffSnorby 		}
215978e5cffSnorby 	}
216978e5cffSnorby 
217978e5cffSnorby 	if (new_state != 0)
218978e5cffSnorby 		group->state = new_state;
219978e5cffSnorby 
220978e5cffSnorby 	for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++)
221978e5cffSnorby 		log_debug("group_fsm: event '%s' resulted in action '%s' and "
222978e5cffSnorby 		    "changing state for group %s from '%s' to '%s'",
223978e5cffSnorby 		    group_event_name(event),
224978e5cffSnorby 		    group_action_name(grp_fsm[i].action[j]),
225978e5cffSnorby 		    inet_ntoa(group->addr), group_state_name(old_state),
226978e5cffSnorby 		    group_state_name(group->state));
227978e5cffSnorby 
228978e5cffSnorby 	return (ret);
229978e5cffSnorby }
230978e5cffSnorby 
231978e5cffSnorby /* timers */
232978e5cffSnorby void
233978e5cffSnorby dead_timer(int fd, short event, void *arg)
234978e5cffSnorby {
235978e5cffSnorby 	struct group *group = arg;
236978e5cffSnorby 
237978e5cffSnorby 	log_debug("dead_timer: %s", inet_ntoa(group->addr));
238978e5cffSnorby 
239978e5cffSnorby 	group_fsm(group, GRP_EVT_TMR_EXPIRED);
240978e5cffSnorby }
241978e5cffSnorby 
242978e5cffSnorby int
243978e5cffSnorby start_dead_timer(struct group *group)
244978e5cffSnorby {
245978e5cffSnorby 	struct timeval	tv;
246978e5cffSnorby 
247978e5cffSnorby 	log_debug("start_dead_timer: %s", inet_ntoa(group->addr));
248978e5cffSnorby 
249978e5cffSnorby 	/* Group Membership Interval */
250978e5cffSnorby 	timerclear(&tv);
251978e5cffSnorby 	tv.tv_sec = group->iface->robustness * group->iface->query_interval +
252978e5cffSnorby 	    (group->iface->query_resp_interval / 2);
253978e5cffSnorby 
254978e5cffSnorby 	return (evtimer_add(&group->dead_timer, &tv));
255978e5cffSnorby }
256978e5cffSnorby 
257978e5cffSnorby int
258978e5cffSnorby start_dead_timer_all(struct group *group)
259978e5cffSnorby {
260978e5cffSnorby 	struct timeval	tv;
261978e5cffSnorby 
262978e5cffSnorby 	log_debug("start_dead_timer_all: %s", inet_ntoa(group->addr));
263978e5cffSnorby 
264978e5cffSnorby 	timerclear(&tv);
265978e5cffSnorby 	if (group->iface->state == IF_STA_QUERIER) {
266978e5cffSnorby 		/* querier */
267978e5cffSnorby 		tv.tv_sec = group->iface->last_member_query_interval *
268978e5cffSnorby 		    group->iface->last_member_query_cnt;
269978e5cffSnorby 	} else {
270978e5cffSnorby 		/* non querier */
271978e5cffSnorby 		/* XXX max response time received in packet */
272978e5cffSnorby 		tv.tv_sec = group->iface->recv_query_resp_interval *
273978e5cffSnorby 		    group->iface->last_member_query_cnt;
274978e5cffSnorby 	}
275978e5cffSnorby 
276978e5cffSnorby 	return (evtimer_add(&group->dead_timer, &tv));
277978e5cffSnorby }
278978e5cffSnorby 
279978e5cffSnorby int
280978e5cffSnorby stop_dead_timer(struct group *group)
281978e5cffSnorby {
282978e5cffSnorby 	log_debug("stop_dead_timer: %s", inet_ntoa(group->addr));
283978e5cffSnorby 
284978e5cffSnorby 	return (evtimer_del(&group->dead_timer));
285978e5cffSnorby }
286978e5cffSnorby 
287978e5cffSnorby void
288978e5cffSnorby v1_host_timer(int fd, short event, void *arg)
289978e5cffSnorby {
290978e5cffSnorby 	struct group *group = arg;
291978e5cffSnorby 
292978e5cffSnorby 	log_debug("v1_host_timer: %s", inet_ntoa(group->addr));
293978e5cffSnorby 
294978e5cffSnorby 	group_fsm(group, GRP_EVT_V1_HOST_TMR_EXP);
295978e5cffSnorby }
296978e5cffSnorby 
297978e5cffSnorby int
298978e5cffSnorby start_v1_host_timer(struct group *group)
299978e5cffSnorby {
300978e5cffSnorby 	struct timeval	tv;
301978e5cffSnorby 
302978e5cffSnorby 	log_debug("start_v1_host_timer: %s", inet_ntoa(group->addr));
303978e5cffSnorby 
304978e5cffSnorby 	/* Group Membership Interval */
305978e5cffSnorby 	timerclear(&tv);
306978e5cffSnorby 	tv.tv_sec = group->iface->robustness * group->iface->query_interval +
307978e5cffSnorby 	    (group->iface->query_resp_interval / 2);
308978e5cffSnorby 
309978e5cffSnorby 	return (evtimer_add(&group->v1_host_timer, &tv));
310978e5cffSnorby }
311978e5cffSnorby 
312978e5cffSnorby int
313978e5cffSnorby stop_v1_host_timer(struct group *group)
314978e5cffSnorby {
315978e5cffSnorby 	log_debug("stop_v1_host_timer: %s", inet_ntoa(group->addr));
316978e5cffSnorby 
317978e5cffSnorby 	return (evtimer_del(&group->v1_host_timer));
318978e5cffSnorby }
319978e5cffSnorby 
320978e5cffSnorby void
321978e5cffSnorby retrans_timer(int fd, short event, void *arg)
322978e5cffSnorby {
323978e5cffSnorby 	struct group *group = arg;
324978e5cffSnorby 	struct timeval tv;
325978e5cffSnorby 
326978e5cffSnorby 	log_debug("retrans_timer: %s", inet_ntoa(group->addr));
327978e5cffSnorby 
328978e5cffSnorby 	send_igmp_query(group->iface, group);
329978e5cffSnorby 
330978e5cffSnorby 	/* reschedule retrans_timer */
331978e5cffSnorby 	if (group->state == GRP_STA_CHECK_MEMB) {
332978e5cffSnorby 		timerclear(&tv);
333978e5cffSnorby 		tv.tv_sec = group->iface->last_member_query_interval;
334978e5cffSnorby 		evtimer_add(&group->retrans_timer, &tv);
335978e5cffSnorby 	}
336978e5cffSnorby }
337978e5cffSnorby 
338978e5cffSnorby int
339978e5cffSnorby start_retrans_timer(struct group *group)
340978e5cffSnorby {
341978e5cffSnorby 	struct timeval	tv;
342978e5cffSnorby 
343978e5cffSnorby 	log_debug("start_retrans_timer: %s", inet_ntoa(group->addr));
344978e5cffSnorby 
345978e5cffSnorby 	timerclear(&tv);
346978e5cffSnorby 	tv.tv_sec = group->iface->last_member_query_interval;
347978e5cffSnorby 
348978e5cffSnorby 	return (evtimer_add(&group->retrans_timer, &tv));
349978e5cffSnorby }
350978e5cffSnorby 
351978e5cffSnorby int
352978e5cffSnorby stop_retrans_timer(struct group *group)
353978e5cffSnorby {
354978e5cffSnorby 	log_debug("stop_retrans_timer: %s", inet_ntoa(group->addr));
355978e5cffSnorby 
356978e5cffSnorby 	return (evtimer_del(&group->retrans_timer));
357978e5cffSnorby }
358978e5cffSnorby 
359978e5cffSnorby /* group list */
360978e5cffSnorby struct group *
361978e5cffSnorby group_list_add(struct iface *iface, u_int32_t group)
362978e5cffSnorby {
363978e5cffSnorby 	struct group	*ge;
364978e5cffSnorby 	struct timeval	 now;
365978e5cffSnorby 
366978e5cffSnorby 	/* validate group id */
367978e5cffSnorby 	if (!IN_MULTICAST(htonl(group)))
368978e5cffSnorby 		fatalx("group_list_add: invalid group");
369978e5cffSnorby 
370978e5cffSnorby 	if ((ge = group_list_find(iface, group)) != NULL) {
371978e5cffSnorby 		return (ge);
372978e5cffSnorby 	}
373978e5cffSnorby 
374978e5cffSnorby 	if ((ge = calloc(1, sizeof(*ge))) == NULL)
375978e5cffSnorby 		fatal("group_list_add");
376978e5cffSnorby 
377978e5cffSnorby 	ge->addr.s_addr = group;
378978e5cffSnorby 	ge->state = GRP_STA_NO_MEMB_PRSNT;
379978e5cffSnorby 	evtimer_set(&ge->dead_timer, dead_timer, ge);
380978e5cffSnorby 	evtimer_set(&ge->v1_host_timer, v1_host_timer, ge);
381978e5cffSnorby 	evtimer_set(&ge->retrans_timer, retrans_timer, ge);
382978e5cffSnorby 
383978e5cffSnorby 	gettimeofday(&now, NULL);
384978e5cffSnorby 	ge->uptime = now.tv_sec;
385978e5cffSnorby 
386978e5cffSnorby 	TAILQ_INSERT_TAIL(&iface->group_list, ge, entry);
387978e5cffSnorby 	iface->group_cnt++;
388978e5cffSnorby 
389978e5cffSnorby 	ge->iface = iface;
390978e5cffSnorby 
391978e5cffSnorby 	log_debug("group_list_add: interface %s, group %s", iface->name,
392978e5cffSnorby 	    inet_ntoa(ge->addr));
393978e5cffSnorby 
394978e5cffSnorby 	return (ge);
395978e5cffSnorby }
396978e5cffSnorby 
397978e5cffSnorby void
398978e5cffSnorby group_list_remove(struct iface *iface, struct group *group)
399978e5cffSnorby {
400978e5cffSnorby 	log_debug("group_list_remove: interface %s, group %s", iface->name,
401978e5cffSnorby 	    inet_ntoa(group->addr));
402978e5cffSnorby 
403978e5cffSnorby 	/* stop timers */
404978e5cffSnorby 	stop_dead_timer(group);
405978e5cffSnorby 	start_v1_host_timer(group);
406978e5cffSnorby 	stop_retrans_timer(group);
407978e5cffSnorby 
408978e5cffSnorby 	TAILQ_REMOVE(&iface->group_list, group, entry);
409978e5cffSnorby 	free(group);
410978e5cffSnorby 	iface->group_cnt--;
411978e5cffSnorby }
412978e5cffSnorby 
413978e5cffSnorby struct group *
414978e5cffSnorby group_list_find(struct iface *iface, u_int32_t group)
415978e5cffSnorby {
416978e5cffSnorby 	struct group	*ge = NULL;
417978e5cffSnorby 
418978e5cffSnorby 	/* validate group id */
419978e5cffSnorby 	if (!IN_MULTICAST(htonl(group)))
420978e5cffSnorby 		fatalx("group_list_find: invalid group");
421978e5cffSnorby 
422978e5cffSnorby 	TAILQ_FOREACH(ge, &iface->group_list, entry) {
423978e5cffSnorby 		if (ge->addr.s_addr == group)
424978e5cffSnorby 			return (ge);
425978e5cffSnorby 	}
426978e5cffSnorby 
427978e5cffSnorby 	return (ge);
428978e5cffSnorby }
429978e5cffSnorby 
430978e5cffSnorby void
431978e5cffSnorby group_list_clr(struct iface *iface)
432978e5cffSnorby {
433978e5cffSnorby 	struct group	*ge;
434978e5cffSnorby 
435978e5cffSnorby 	while ((ge = TAILQ_FIRST(&iface->group_list)) != NULL) {
436978e5cffSnorby 		TAILQ_REMOVE(&iface->group_list, ge, entry);
437978e5cffSnorby 		free(ge);
438978e5cffSnorby 	}
439978e5cffSnorby 	iface->group_cnt = 0;
440978e5cffSnorby }
441978e5cffSnorby 
442978e5cffSnorby int
443978e5cffSnorby group_list_empty(struct iface *iface)
444978e5cffSnorby {
445978e5cffSnorby 	return (TAILQ_EMPTY(&iface->group_list));
446978e5cffSnorby }
447978e5cffSnorby 
448978e5cffSnorby void
449978e5cffSnorby group_list_dump(struct iface *iface, struct ctl_conn *c)
450978e5cffSnorby {
451978e5cffSnorby 	struct group		*ge;
452978e5cffSnorby 	struct ctl_group	*gctl;
453978e5cffSnorby 
454978e5cffSnorby 	TAILQ_FOREACH(ge, &iface->group_list, entry) {
455978e5cffSnorby 		gctl = group_to_ctl(ge);
456*14d72cd0Spyr 		imsg_compose_event(&c->iev, IMSG_CTL_SHOW_IGMP, 0, 0,
457*14d72cd0Spyr 		    -1, gctl, sizeof(struct ctl_group));
458978e5cffSnorby 	}
459978e5cffSnorby }
460978e5cffSnorby 
461978e5cffSnorby /* names */
462978e5cffSnorby const char *
463978e5cffSnorby group_event_name(int event)
464978e5cffSnorby {
465978e5cffSnorby 	return (group_event_names[event]);
466978e5cffSnorby }
467978e5cffSnorby 
468978e5cffSnorby const char *
469978e5cffSnorby group_action_name(int action)
470978e5cffSnorby {
471978e5cffSnorby 	return (group_action_names[action]);
472978e5cffSnorby }
473978e5cffSnorby 
474978e5cffSnorby struct ctl_group *
475978e5cffSnorby group_to_ctl(struct group *group)
476978e5cffSnorby {
477978e5cffSnorby 	static struct ctl_group	 gctl;
478978e5cffSnorby 	struct timeval		 tv, now, res;
479978e5cffSnorby 
480978e5cffSnorby 	memcpy(&gctl.addr, &group->addr, sizeof(gctl.addr));
481978e5cffSnorby 
482978e5cffSnorby 	gctl.state = group->state;
483978e5cffSnorby 
484978e5cffSnorby 	gettimeofday(&now, NULL);
485978e5cffSnorby 	if (evtimer_pending(&group->dead_timer, &tv)) {
486978e5cffSnorby 		timersub(&tv, &now, &res);
487978e5cffSnorby 		gctl.dead_timer = res.tv_sec;
488978e5cffSnorby 	} else
489978e5cffSnorby 		gctl.dead_timer = 0;
490978e5cffSnorby 
491978e5cffSnorby 	if (group->state != GRP_STA_NO_MEMB_PRSNT) {
492978e5cffSnorby 		gctl.uptime = now.tv_sec - group->uptime;
493978e5cffSnorby 	} else
494978e5cffSnorby 		gctl.uptime = 0;
495978e5cffSnorby 
496978e5cffSnorby 	return (&gctl);
497978e5cffSnorby }
498