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