GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/dvmrpd/group.c Lines: 0 151 0.0 %
Date: 2017-11-07 Branches: 0 55 0.0 %

Line Branch Exec Source
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
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
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
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
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
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
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
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
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
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
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
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 *
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
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 *
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
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
442
group_list_empty(struct iface *iface)
443
{
444
	return (TAILQ_EMPTY(&iface->group_list));
445
}
446
447
void
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 *
462
group_event_name(int event)
463
{
464
	return (group_event_names[event]);
465
}
466
467
const char *
468
group_action_name(int action)
469
{
470
	return (group_action_names[action]);
471
}
472
473
struct ctl_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
}