xref: /openbsd/usr.sbin/dvmrpd/group.c (revision 5376bb5a)
1 /*	$OpenBSD: group.c,v 1.4 2014/11/18 20:54:28 krw Exp $ */
2 
3 /*
4  * Copyright (c) 2006 Esben Norby <norby@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <arpa/inet.h>
23 #include <sys/time.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <event.h>
27 
28 #include "igmp.h"
29 #include "dvmrpd.h"
30 #include "dvmrp.h"
31 #include "dvmrpe.h"
32 #include "log.h"
33 #include "control.h"
34 
35 void	dead_timer(int, short, void *);
36 int	start_dead_timer(struct group *);
37 int	start_dead_timer_all(struct group *);
38 int	stop_dead_timer(struct group *);
39 
40 void	v1_host_timer(int, short, void *);
41 int	start_v1_host_timer(struct group *);
42 int	stop_v1_host_timer(struct group *);
43 
44 void	retrans_timer(int, short, void *);
45 int	start_retrans_timer(struct group *);
46 int	stop_retrans_timer(struct group *);
47 
48 extern struct dvmrpd_conf	*deconf;
49 
50 #define MAX_ACTIONS	4
51 
52 struct {
53 	int			state;
54 	enum group_event	event;
55 	enum group_action	action[MAX_ACTIONS];
56 	int			new_state;
57 } grp_fsm[] = {
58     /* current state		event that happened	action(s) to take		resulting state */
59     /* querier fsm */
60     {GRP_STA_NO_MEMB_PRSNT,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_ADD_GROUP,
61 							 GRP_ACT_START_TMR,
62 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
63     {GRP_STA_NO_MEMB_PRSNT,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_ADD_GROUP,
64 							 GRP_ACT_START_TMR,
65 							 GRP_ACT_START_V1_HOST_TMR,
66 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
67 
68     {GRP_STA_MEMB_PRSNT,	GRP_EVT_TMR_EXPIRED,	{GRP_ACT_DEL_GROUP,
69 							 GRP_ACT_END},			GRP_STA_NO_MEMB_PRSNT},
70     {GRP_STA_MEMB_PRSNT,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_START_TMR,
71 							 GRP_ACT_END},			0},
72     {GRP_STA_MEMB_PRSNT,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_START_TMR,
73 							 GRP_ACT_START_V1_HOST_TMR,
74 							 GRP_ACT_END},			GRP_STA_V1_MEMB_PRSNT},
75     {GRP_STA_MEMB_PRSNT,	GRP_EVT_LEAVE_RCVD,	{GRP_ACT_START_TMR_ALL,
76 							 GRP_ACT_START_RETRANS_TMR,
77 							 GRP_ACT_SEND_GRP_QUERY,
78 							 GRP_ACT_END},			GRP_STA_CHECK_MEMB},
79 
80     {GRP_STA_CHECK_MEMB,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_START_TMR,
81 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
82     {GRP_STA_CHECK_MEMB,	GRP_EVT_TMR_EXPIRED,	{GRP_ACT_DEL_GROUP,
83 							 GRP_ACT_CLR_RETRANS_TMR,
84 							 GRP_ACT_END},			GRP_STA_NO_MEMB_PRSNT},
85     {GRP_STA_CHECK_MEMB,	GRP_EVT_RETRANS_TMR_EXP,{GRP_ACT_SEND_GRP_QUERY,
86 							 GRP_ACT_START_RETRANS_TMR,
87 							 GRP_ACT_END},			0},
88     {GRP_STA_CHECK_MEMB,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_START_TMR,
89 							 GRP_ACT_START_V1_HOST_TMR,
90 							 GRP_ACT_END},			GRP_STA_V1_MEMB_PRSNT},
91 
92     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_V1_HOST_TMR_EXP,{GRP_ACT_NOTHING,
93 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
94     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_V1_REPORT_RCVD,	{GRP_ACT_START_TMR,
95 							 GRP_ACT_START_V1_HOST_TMR,
96 							 GRP_ACT_END},			0},
97     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_V2_REPORT_RCVD,	{GRP_ACT_START_TMR,
98 							 GRP_ACT_END},			0},
99     {GRP_STA_V1_MEMB_PRSNT,	GRP_EVT_TMR_EXPIRED,	{GRP_ACT_DEL_GROUP,
100 							 GRP_ACT_END},			GRP_STA_NO_MEMB_PRSNT},
101 
102     /* non querier fsm */
103     {GRP_STA_NO_MEMB_PRSNT,	GRP_EVT_REPORT_RCVD,	{GRP_ACT_ADD_GROUP,
104 							 GRP_ACT_START_TMR,
105 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
106     {GRP_STA_MEMB_PRSNT,	GRP_EVT_REPORT_RCVD,	{GRP_ACT_START_TMR,
107 							 GRP_ACT_END},			0},
108     {GRP_STA_MEMB_PRSNT,	GRP_EVT_QUERY_RCVD,	{GRP_ACT_START_TMR_ALL,
109 							 GRP_ACT_END},			GRP_STA_CHECK_MEMB},
110 
111     {GRP_STA_CHECK_MEMB,	GRP_EVT_REPORT_RCVD,	{GRP_ACT_START_TMR,
112 							 GRP_ACT_END},			GRP_STA_MEMB_PRSNT},
113     {-1,			GRP_EVT_NOTHING,	{GRP_ACT_NOTHING,
114 							 GRP_ACT_END},			0},
115 };
116 
117 const char * const group_action_names[] = {
118 	"END MARKER",
119 	"START TIMER",
120 	"START ALL TIMER",
121 	"START RETRANSMISSION TIMER",
122 	"START V1 HOST TIMER",
123 	"SEND GROUP QUERY",
124 	"ADD GROUP",
125 	"DEL GROUP",
126 	"CLEAR RETRANSMISSION TIMER",
127 	"NOTHING"
128 };
129 
130 static const char * const group_event_names[] = {
131 	"V2 REPORT RCVD",
132 	"V1 REPORT RCVD",
133 	"LEAVE RCVD",
134 	"TIMER EXPIRED",
135 	"RETRANS TIMER EXPIRED",
136 	"V1 HOST TIMER EXPIRED",
137 	"REPORT RCVD",
138 	"QUERY RCVD",
139 	"NOTHING"
140 };
141 
142 int
group_fsm(struct group * group,enum group_event event)143 group_fsm(struct group *group, enum group_event event)
144 {
145 	struct mfc	mfc;
146 	int		old_state;
147 	int		new_state = 0;
148 	int		i, j, ret = 0;
149 
150 	old_state = group->state;
151 
152 	for (i = 0; grp_fsm[i].state != -1; i++)
153 		if ((grp_fsm[i].state & old_state) &&
154 		    (grp_fsm[i].event == event)) {
155 			new_state = grp_fsm[i].new_state;
156 			break;
157 		}
158 
159 	if (grp_fsm[i].state == -1) {
160 		/* XXX event outside of the defined fsm, ignore it. */
161 		log_debug("group_fsm: group %s, event '%s' not expected in "
162 		    "state '%s'", inet_ntoa(group->addr),
163 		    group_event_name(event), group_state_name(old_state));
164 		return (0);
165 	}
166 
167 	for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++) {
168 		switch (grp_fsm[i].action[j]) {
169 		case GRP_ACT_START_TMR:
170 			ret = start_dead_timer(group);
171 			break;
172 		case GRP_ACT_START_TMR_ALL:
173 			ret = start_dead_timer_all(group);
174 			break;
175 		case GRP_ACT_START_RETRANS_TMR:
176 			ret = start_retrans_timer(group);
177 			break;
178 		case GRP_ACT_START_V1_HOST_TMR:
179 			ret = start_v1_host_timer(group);
180 			break;
181 		case GRP_ACT_SEND_GRP_QUERY:
182 			ret = send_igmp_query(group->iface, group);
183 			break;
184 		case GRP_ACT_ADD_GROUP:
185 			mfc.origin.s_addr = 0;
186 			mfc.group = group->addr;
187 			mfc.ifindex = group->iface->ifindex;
188 			dvmrpe_imsg_compose_rde(IMSG_GROUP_ADD, 0, 0, &mfc,
189 			    sizeof(mfc));
190 			break;
191 		case GRP_ACT_DEL_GROUP:
192 			mfc.origin.s_addr = 0;
193 			mfc.group = group->addr;
194 			mfc.ifindex = group->iface->ifindex;
195 			dvmrpe_imsg_compose_rde(IMSG_GROUP_DEL, 0, 0, &mfc,
196 			    sizeof(mfc));
197 			break;
198 		case GRP_ACT_CLR_RETRANS_TMR:
199 			ret = stop_retrans_timer(group);
200 			break;
201 		case GRP_ACT_NOTHING:
202 		case GRP_ACT_END:
203 			/* do nothing */
204 			break;
205 		}
206 
207 		if (ret) {
208 			log_debug("group_fsm: error changing state for "
209 			    "group %s, event '%s', state '%s'",
210 			    inet_ntoa(group->addr), group_event_name(event),
211 			    group_state_name(old_state));
212 			return (-1);
213 		}
214 	}
215 
216 	if (new_state != 0)
217 		group->state = new_state;
218 
219 	for (j = 0; grp_fsm[i].action[j] != GRP_ACT_END; j++)
220 		log_debug("group_fsm: event '%s' resulted in action '%s' and "
221 		    "changing state for group %s from '%s' to '%s'",
222 		    group_event_name(event),
223 		    group_action_name(grp_fsm[i].action[j]),
224 		    inet_ntoa(group->addr), group_state_name(old_state),
225 		    group_state_name(group->state));
226 
227 	return (ret);
228 }
229 
230 /* timers */
231 void
dead_timer(int fd,short event,void * arg)232 dead_timer(int fd, short event, void *arg)
233 {
234 	struct group *group = arg;
235 
236 	log_debug("dead_timer: %s", inet_ntoa(group->addr));
237 
238 	group_fsm(group, GRP_EVT_TMR_EXPIRED);
239 }
240 
241 int
start_dead_timer(struct group * group)242 start_dead_timer(struct group *group)
243 {
244 	struct timeval	tv;
245 
246 	log_debug("start_dead_timer: %s", inet_ntoa(group->addr));
247 
248 	/* Group Membership Interval */
249 	timerclear(&tv);
250 	tv.tv_sec = group->iface->robustness * group->iface->query_interval +
251 	    (group->iface->query_resp_interval / 2);
252 
253 	return (evtimer_add(&group->dead_timer, &tv));
254 }
255 
256 int
start_dead_timer_all(struct group * group)257 start_dead_timer_all(struct group *group)
258 {
259 	struct timeval	tv;
260 
261 	log_debug("start_dead_timer_all: %s", inet_ntoa(group->addr));
262 
263 	timerclear(&tv);
264 	if (group->iface->state == IF_STA_QUERIER) {
265 		/* querier */
266 		tv.tv_sec = group->iface->last_member_query_interval *
267 		    group->iface->last_member_query_cnt;
268 	} else {
269 		/* non querier */
270 		/* XXX max response time received in packet */
271 		tv.tv_sec = group->iface->recv_query_resp_interval *
272 		    group->iface->last_member_query_cnt;
273 	}
274 
275 	return (evtimer_add(&group->dead_timer, &tv));
276 }
277 
278 int
stop_dead_timer(struct group * group)279 stop_dead_timer(struct group *group)
280 {
281 	log_debug("stop_dead_timer: %s", inet_ntoa(group->addr));
282 
283 	return (evtimer_del(&group->dead_timer));
284 }
285 
286 void
v1_host_timer(int fd,short event,void * arg)287 v1_host_timer(int fd, short event, void *arg)
288 {
289 	struct group *group = arg;
290 
291 	log_debug("v1_host_timer: %s", inet_ntoa(group->addr));
292 
293 	group_fsm(group, GRP_EVT_V1_HOST_TMR_EXP);
294 }
295 
296 int
start_v1_host_timer(struct group * group)297 start_v1_host_timer(struct group *group)
298 {
299 	struct timeval	tv;
300 
301 	log_debug("start_v1_host_timer: %s", inet_ntoa(group->addr));
302 
303 	/* Group Membership Interval */
304 	timerclear(&tv);
305 	tv.tv_sec = group->iface->robustness * group->iface->query_interval +
306 	    (group->iface->query_resp_interval / 2);
307 
308 	return (evtimer_add(&group->v1_host_timer, &tv));
309 }
310 
311 int
stop_v1_host_timer(struct group * group)312 stop_v1_host_timer(struct group *group)
313 {
314 	log_debug("stop_v1_host_timer: %s", inet_ntoa(group->addr));
315 
316 	return (evtimer_del(&group->v1_host_timer));
317 }
318 
319 void
retrans_timer(int fd,short event,void * arg)320 retrans_timer(int fd, short event, void *arg)
321 {
322 	struct group *group = arg;
323 	struct timeval tv;
324 
325 	log_debug("retrans_timer: %s", inet_ntoa(group->addr));
326 
327 	send_igmp_query(group->iface, group);
328 
329 	/* reschedule retrans_timer */
330 	if (group->state == GRP_STA_CHECK_MEMB) {
331 		timerclear(&tv);
332 		tv.tv_sec = group->iface->last_member_query_interval;
333 		evtimer_add(&group->retrans_timer, &tv);
334 	}
335 }
336 
337 int
start_retrans_timer(struct group * group)338 start_retrans_timer(struct group *group)
339 {
340 	struct timeval	tv;
341 
342 	log_debug("start_retrans_timer: %s", inet_ntoa(group->addr));
343 
344 	timerclear(&tv);
345 	tv.tv_sec = group->iface->last_member_query_interval;
346 
347 	return (evtimer_add(&group->retrans_timer, &tv));
348 }
349 
350 int
stop_retrans_timer(struct group * group)351 stop_retrans_timer(struct group *group)
352 {
353 	log_debug("stop_retrans_timer: %s", inet_ntoa(group->addr));
354 
355 	return (evtimer_del(&group->retrans_timer));
356 }
357 
358 /* group list */
359 struct group *
group_list_add(struct iface * iface,u_int32_t group)360 group_list_add(struct iface *iface, u_int32_t group)
361 {
362 	struct group	*ge;
363 	struct timeval	 now;
364 
365 	/* validate group id */
366 	if (!IN_MULTICAST(htonl(group)))
367 		fatalx("group_list_add: invalid group");
368 
369 	if ((ge = group_list_find(iface, group)) != NULL) {
370 		return (ge);
371 	}
372 
373 	if ((ge = calloc(1, sizeof(*ge))) == NULL)
374 		fatal("group_list_add");
375 
376 	ge->addr.s_addr = group;
377 	ge->state = GRP_STA_NO_MEMB_PRSNT;
378 	evtimer_set(&ge->dead_timer, dead_timer, ge);
379 	evtimer_set(&ge->v1_host_timer, v1_host_timer, ge);
380 	evtimer_set(&ge->retrans_timer, retrans_timer, ge);
381 
382 	gettimeofday(&now, NULL);
383 	ge->uptime = now.tv_sec;
384 
385 	TAILQ_INSERT_TAIL(&iface->group_list, ge, entry);
386 	iface->group_cnt++;
387 
388 	ge->iface = iface;
389 
390 	log_debug("group_list_add: interface %s, group %s", iface->name,
391 	    inet_ntoa(ge->addr));
392 
393 	return (ge);
394 }
395 
396 void
group_list_remove(struct iface * iface,struct group * group)397 group_list_remove(struct iface *iface, struct group *group)
398 {
399 	log_debug("group_list_remove: interface %s, group %s", iface->name,
400 	    inet_ntoa(group->addr));
401 
402 	/* stop timers */
403 	stop_dead_timer(group);
404 	start_v1_host_timer(group);
405 	stop_retrans_timer(group);
406 
407 	TAILQ_REMOVE(&iface->group_list, group, entry);
408 	free(group);
409 	iface->group_cnt--;
410 }
411 
412 struct group *
group_list_find(struct iface * iface,u_int32_t group)413 group_list_find(struct iface *iface, u_int32_t group)
414 {
415 	struct group	*ge = NULL;
416 
417 	/* validate group id */
418 	if (!IN_MULTICAST(htonl(group)))
419 		fatalx("group_list_find: invalid group");
420 
421 	TAILQ_FOREACH(ge, &iface->group_list, entry) {
422 		if (ge->addr.s_addr == group)
423 			return (ge);
424 	}
425 
426 	return (ge);
427 }
428 
429 void
group_list_clr(struct iface * iface)430 group_list_clr(struct iface *iface)
431 {
432 	struct group	*ge;
433 
434 	while ((ge = TAILQ_FIRST(&iface->group_list)) != NULL) {
435 		TAILQ_REMOVE(&iface->group_list, ge, entry);
436 		free(ge);
437 	}
438 	iface->group_cnt = 0;
439 }
440 
441 int
group_list_empty(struct iface * iface)442 group_list_empty(struct iface *iface)
443 {
444 	return (TAILQ_EMPTY(&iface->group_list));
445 }
446 
447 void
group_list_dump(struct iface * iface,struct ctl_conn * c)448 group_list_dump(struct iface *iface, struct ctl_conn *c)
449 {
450 	struct group		*ge;
451 	struct ctl_group	*gctl;
452 
453 	TAILQ_FOREACH(ge, &iface->group_list, entry) {
454 		gctl = group_to_ctl(ge);
455 		imsg_compose_event(&c->iev, IMSG_CTL_SHOW_IGMP, 0, 0,
456 		    -1, gctl, sizeof(struct ctl_group));
457 	}
458 }
459 
460 /* names */
461 const char *
group_event_name(int event)462 group_event_name(int event)
463 {
464 	return (group_event_names[event]);
465 }
466 
467 const char *
group_action_name(int action)468 group_action_name(int action)
469 {
470 	return (group_action_names[action]);
471 }
472 
473 struct ctl_group *
group_to_ctl(struct group * group)474 group_to_ctl(struct group *group)
475 {
476 	static struct ctl_group	 gctl;
477 	struct timeval		 tv, now, res;
478 
479 	memcpy(&gctl.addr, &group->addr, sizeof(gctl.addr));
480 
481 	gctl.state = group->state;
482 
483 	gettimeofday(&now, NULL);
484 	if (evtimer_pending(&group->dead_timer, &tv)) {
485 		timersub(&tv, &now, &res);
486 		gctl.dead_timer = res.tv_sec;
487 	} else
488 		gctl.dead_timer = 0;
489 
490 	if (group->state != GRP_STA_NO_MEMB_PRSNT) {
491 		gctl.uptime = now.tv_sec - group->uptime;
492 	} else
493 		gctl.uptime = 0;
494 
495 	return (&gctl);
496 }
497