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