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 } 497