1/********************************************************************* 2 PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved. 3 See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage. 4 5 RFC 1112, 2236, 3376, 3569, 3678, 4607 6 7 Authors: Kristof Roelants (IGMPv3), Simon Maes, Brecht Van Cauwenberghe 8 *********************************************************************/ 9 10#include "pico_stack.h" 11#include "pico_ipv4.h" 12#include "pico_igmp.h" 13#include "pico_config.h" 14#include "pico_eth.h" 15#include "pico_addressing.h" 16#include "pico_frame.h" 17#include "pico_tree.h" 18#include "pico_device.h" 19#include "pico_socket.h" 20#include "pico_mcast.h" 21 22#if defined(PICO_SUPPORT_IGMP) && defined(PICO_SUPPORT_MCAST) 23 24#ifdef DEBUG_IGMP 25 #define igmp_dbg dbg 26#else 27 #define igmp_dbg(...) do {} while(0) 28#endif 29 30/* membership states */ 31#define IGMP_STATE_NON_MEMBER (0x0) 32#define IGMP_STATE_DELAYING_MEMBER (0x1) 33#define IGMP_STATE_IDLE_MEMBER (0x2) 34 35/* events */ 36#define IGMP_EVENT_DELETE_GROUP (0x0) 37#define IGMP_EVENT_CREATE_GROUP (0x1) 38#define IGMP_EVENT_UPDATE_GROUP (0x2) 39#define IGMP_EVENT_QUERY_RECV (0x3) 40#define IGMP_EVENT_REPORT_RECV (0x4) 41#define IGMP_EVENT_TIMER_EXPIRED (0x5) 42 43/* message types */ 44#define IGMP_TYPE_MEM_QUERY (0x11) 45#define IGMP_TYPE_MEM_REPORT_V1 (0x12) 46#define IGMP_TYPE_MEM_REPORT_V2 (0x16) 47#define IGMP_TYPE_LEAVE_GROUP (0x17) 48#define IGMP_TYPE_MEM_REPORT_V3 (0x22) 49 50/* group record types */ 51#define IGMP_MODE_IS_INCLUDE (1) 52#define IGMP_MODE_IS_EXCLUDE (2) 53#define IGMP_CHANGE_TO_INCLUDE_MODE (3) 54#define IGMP_CHANGE_TO_EXCLUDE_MODE (4) 55#define IGMP_ALLOW_NEW_SOURCES (5) 56#define IGMP_BLOCK_OLD_SOURCES (6) 57 58/* host flag */ 59#define IGMP_HOST_LAST (0x1) 60#define IGMP_HOST_NOT_LAST (0x0) 61 62/* list of timers, counters and their default values */ 63#define IGMP_ROBUSTNESS (2u) 64#define IGMP_QUERY_INTERVAL (125) /* secs */ 65#define IGMP_QUERY_RESPONSE_INTERVAL (10u) /* secs */ 66#define IGMP_STARTUP_QUERY_INTERVAL (IGMPV3_QUERY_INTERVAL / 4) 67#define IGMP_STARTUP_QUERY_COUNT (IGMPV3_ROBUSTNESS) 68#define IGMP_LAST_MEMBER_QUERY_INTERVAL (1) /* secs */ 69#define IGMP_LAST_MEMBER_QUERY_COUNT (IGMPV3_ROBUSTNESS) 70#define IGMP_UNSOLICITED_REPORT_INTERVAL (1) /* secs */ 71#define IGMP_DEFAULT_MAX_RESPONSE_TIME (100) 72 73/* custom timers types */ 74#define IGMP_TIMER_GROUP_REPORT (1) 75#define IGMP_TIMER_V1_QUERIER (2) 76#define IGMP_TIMER_V2_QUERIER (3) 77 78/* IGMP groups */ 79#define IGMP_ALL_HOST_GROUP long_be(0xE0000001) /* 224.0.0.1 */ 80#define IGMP_ALL_ROUTER_GROUP long_be(0xE0000002) /* 224.0.0.2 */ 81#define IGMPV3_ALL_ROUTER_GROUP long_be(0xE0000016) /* 224.0.0.22 */ 82 83/* misc */ 84#define IGMP_TIMER_STOPPED (1) 85#define IP_OPTION_ROUTER_ALERT_LEN (4u) 86#define IGMP_MAX_GROUPS (32) /* max 255 */ 87 88PACKED_STRUCT_DEF igmp_message { 89 uint8_t type; 90 uint8_t max_resp_time; 91 uint16_t crc; 92 uint32_t mcast_group; 93}; 94 95PACKED_STRUCT_DEF igmpv3_query { 96 uint8_t type; 97 uint8_t max_resp_time; 98 uint16_t crc; 99 uint32_t mcast_group; 100 uint8_t rsq; 101 uint8_t qqic; 102 uint16_t sources; 103}; 104 105PACKED_STRUCT_DEF igmpv3_group_record { 106 uint8_t type; 107 uint8_t aux; 108 uint16_t sources; 109 uint32_t mcast_group; 110}; 111 112PACKED_STRUCT_DEF igmpv3_report { 113 uint8_t type; 114 uint8_t res0; 115 uint16_t crc; 116 uint16_t res1; 117 uint16_t groups; 118}; 119 120struct igmp_timer { 121 uint8_t type; 122 uint8_t stopped; 123 pico_time start; 124 pico_time delay; 125 struct pico_ip4 mcast_link; 126 struct pico_ip4 mcast_group; 127 struct pico_frame *f; 128 void (*callback)(struct igmp_timer *t); 129}; 130 131/* queues */ 132static struct pico_queue igmp_in = { 133 0 134}; 135static struct pico_queue igmp_out = { 136 0 137}; 138 139/* finite state machine caller */ 140static int pico_igmp_process_event(struct mcast_parameters *p); 141 142/* state callback prototype */ 143typedef int (*callback)(struct mcast_parameters *); 144 145static inline int igmpt_type_compare(struct igmp_timer *a, struct igmp_timer *b) 146{ 147 if (a->type < b->type) 148 return -1; 149 150 if (a->type > b->type) 151 return 1; 152 153 return 0; 154} 155 156 157static inline int igmpt_group_compare(struct igmp_timer *a, struct igmp_timer *b) 158{ 159 return pico_ipv4_compare(&a->mcast_group, &b->mcast_group); 160} 161 162static inline int igmpt_link_compare(struct igmp_timer *a, struct igmp_timer *b) 163{ 164 return pico_ipv4_compare(&a->mcast_link, &b->mcast_link); 165} 166 167/* redblack trees */ 168static int igmp_timer_cmp(void *ka, void *kb) 169{ 170 struct igmp_timer *a = ka, *b = kb; 171 int cmp = igmpt_type_compare(a, b); 172 if (cmp) 173 return cmp; 174 175 cmp = igmpt_group_compare(a, b); 176 if (cmp) 177 return cmp; 178 179 return igmpt_link_compare(a, b); 180 181} 182static PICO_TREE_DECLARE(IGMPTimers, igmp_timer_cmp); 183 184static inline int igmpparm_group_compare(struct mcast_parameters *a, struct mcast_parameters *b) 185{ 186 return pico_ipv4_compare(&a->mcast_group.ip4, &b->mcast_group.ip4); 187} 188 189static inline int igmpparm_link_compare(struct mcast_parameters *a, struct mcast_parameters *b) 190{ 191 return pico_ipv4_compare(&a->mcast_link.ip4, &b->mcast_link.ip4); 192} 193 194static int igmp_parameters_cmp(void *ka, void *kb) 195{ 196 struct mcast_parameters *a = ka, *b = kb; 197 int cmp = igmpparm_group_compare(a, b); 198 if (cmp) 199 return cmp; 200 201 return igmpparm_link_compare(a, b); 202} 203static PICO_TREE_DECLARE(IGMPParameters, igmp_parameters_cmp); 204 205static int igmp_sources_cmp(void *ka, void *kb) 206{ 207 struct pico_ip4 *a = ka, *b = kb; 208 return pico_ipv4_compare(a, b); 209} 210static PICO_TREE_DECLARE(IGMPAllow, igmp_sources_cmp); 211static PICO_TREE_DECLARE(IGMPBlock, igmp_sources_cmp); 212 213static struct mcast_parameters *pico_igmp_find_parameter(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group) 214{ 215 struct mcast_parameters test = { 216 0 217 }; 218 if (!mcast_link || !mcast_group) 219 return NULL; 220 221 test.mcast_link.ip4 = *mcast_link; 222 test.mcast_group.ip4 = *mcast_group; 223 return pico_tree_findKey(&IGMPParameters, &test); 224} 225 226static int pico_igmp_delete_parameter(struct mcast_parameters *p) 227{ 228 if (pico_tree_delete(&IGMPParameters, p)) 229 PICO_FREE(p); 230 else 231 return -1; 232 233 return 0; 234} 235 236static void pico_igmp_timer_expired(pico_time now, void *arg) 237{ 238 struct igmp_timer *t = NULL, *timer = NULL, test = { 239 0 240 }; 241 242 IGNORE_PARAMETER(now); 243 t = (struct igmp_timer *)arg; 244 test.type = t->type; 245 test.mcast_link = t->mcast_link; 246 test.mcast_group = t->mcast_group; 247 igmp_dbg("IGMP: timer expired for %08X link %08X type %u, delay %lu\n", t->mcast_group.addr, t->mcast_link.addr, t->type, t->delay); 248 timer = pico_tree_findKey(&IGMPTimers, &test); 249 if (!timer) { 250 return; 251 } 252 253 if (timer->stopped == IGMP_TIMER_STOPPED) { 254 pico_tree_delete(&IGMPTimers, timer); 255 PICO_FREE(t); 256 return; 257 } 258 259 if (timer->start + timer->delay < PICO_TIME_MS()) { 260 pico_tree_delete(&IGMPTimers, timer); 261 if (timer->callback) 262 timer->callback(timer); 263 264 PICO_FREE(timer); 265 } else { 266 igmp_dbg("IGMP: restart timer for %08X, delay %lu, new delay %lu\n", t->mcast_group.addr, t->delay, (timer->start + timer->delay) - PICO_TIME_MS()); 267 if (!pico_timer_add((timer->start + timer->delay) - PICO_TIME_MS(), &pico_igmp_timer_expired, timer)) { 268 igmp_dbg("IGMP: Failed to start expiration timer\n"); 269 pico_tree_delete(&IGMPTimers, timer); 270 PICO_FREE(timer); 271 } 272 } 273 274 return; 275} 276 277static int pico_igmp_timer_reset(struct igmp_timer *t) 278{ 279 struct igmp_timer *timer = NULL, test = { 280 0 281 }; 282 283 igmp_dbg("IGMP: reset timer for %08X, delay %lu\n", t->mcast_group.addr, t->delay); 284 test.type = t->type; 285 test.mcast_link = t->mcast_link; 286 test.mcast_group = t->mcast_group; 287 timer = pico_tree_findKey(&IGMPTimers, &test); 288 if (!timer) 289 return -1; 290 291 *timer = *t; 292 timer->start = PICO_TIME_MS(); 293 return 0; 294} 295 296static int pico_igmp_timer_start(struct igmp_timer *t) 297{ 298 struct igmp_timer *timer = NULL, test = { 299 0 300 }; 301 302 igmp_dbg("IGMP: start timer for %08X link %08X type %u, delay %lu\n", t->mcast_group.addr, t->mcast_link.addr, t->type, t->delay); 303 test.type = t->type; 304 test.mcast_link = t->mcast_link; 305 test.mcast_group = t->mcast_group; 306 timer = pico_tree_findKey(&IGMPTimers, &test); 307 if (timer) 308 return pico_igmp_timer_reset(t); 309 310 timer = PICO_ZALLOC(sizeof(struct igmp_timer)); 311 if (!timer) { 312 pico_err = PICO_ERR_ENOMEM; 313 return -1; 314 } 315 316 *timer = *t; 317 timer->start = PICO_TIME_MS(); 318 319 if (pico_tree_insert(&IGMPTimers, timer)) { 320 igmp_dbg("IGMP: Failed to insert timer in tree\n"); 321 PICO_FREE(timer); 322 return -1; 323 } 324 325 if (!pico_timer_add(timer->delay, &pico_igmp_timer_expired, timer)) { 326 igmp_dbg("IGMP: Failed to start expiration timer\n"); 327 pico_tree_delete(&IGMPTimers, timer); 328 PICO_FREE(timer); 329 return -1; 330 } 331 return 0; 332} 333 334static int pico_igmp_timer_stop(struct igmp_timer *t) 335{ 336 struct igmp_timer *timer = NULL, test = { 337 0 338 }; 339 340 test.type = t->type; 341 test.mcast_link = t->mcast_link; 342 test.mcast_group = t->mcast_group; 343 timer = pico_tree_findKey(&IGMPTimers, &test); 344 if (!timer) 345 return -1; 346 347 igmp_dbg("IGMP: stop timer for %08X, delay %lu\n", timer->mcast_group.addr, timer->delay); 348 timer->stopped = IGMP_TIMER_STOPPED; 349 return 0; 350} 351 352static int pico_igmp_timer_is_running(struct igmp_timer *t) 353{ 354 struct igmp_timer *timer = NULL, test = { 355 0 356 }; 357 358 test.type = t->type; 359 test.mcast_link = t->mcast_link; 360 test.mcast_group = t->mcast_group; 361 timer = pico_tree_findKey(&IGMPTimers, &test); 362 if (timer) 363 return 1; 364 365 return 0; 366} 367 368static struct igmp_timer *pico_igmp_find_timer(uint8_t type, struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group) 369{ 370 struct igmp_timer test = { 371 0 372 }; 373 374 test.type = type; 375 test.mcast_link = *mcast_link; 376 test.mcast_group = *mcast_group; 377 return pico_tree_findKey(&IGMPTimers, &test); 378} 379 380static void pico_igmp_report_expired(struct igmp_timer *t) 381{ 382 struct mcast_parameters *p = NULL; 383 384 p = pico_igmp_find_parameter(&t->mcast_link, &t->mcast_group); 385 if (!p) 386 return; 387 388 p->event = IGMP_EVENT_TIMER_EXPIRED; 389 pico_igmp_process_event(p); 390} 391 392static void pico_igmp_v2querier_expired(struct igmp_timer *t) 393{ 394 struct pico_ipv4_link *link = NULL; 395 struct pico_tree_node *index = NULL, *_tmp = NULL; 396 397 link = pico_ipv4_link_by_dev(t->f->dev); 398 if (!link) 399 return; 400 401 /* When changing compatibility mode, cancel all pending response 402 * and retransmission timers. 403 */ 404 pico_tree_foreach_safe(index, &IGMPTimers, _tmp) 405 { 406 ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED; 407 pico_tree_delete(&IGMPTimers, index->keyValue); 408 } 409 igmp_dbg("IGMP: switch to compatibility mode IGMPv3\n"); 410 link->mcast_compatibility = PICO_IGMPV3; 411 return; 412} 413 414static int pico_igmp_is_checksum_valid(struct pico_frame *f) 415{ 416 struct pico_ipv4_hdr *hdr = NULL; 417 uint8_t ihl = 24, datalen = 0; 418 419 hdr = (struct pico_ipv4_hdr *)f->net_hdr; 420 ihl = (uint8_t)((hdr->vhl & 0x0F) * 4); /* IHL is in 32bit words */ 421 datalen = (uint8_t)(short_be(hdr->len) - ihl); 422 423 if (short_be(pico_checksum(f->transport_hdr, datalen)) == 0) 424 return 1; 425 426 igmp_dbg("IGMP: invalid checksum\n"); 427 return 0; 428} 429 430/* RFC 3376 $7.1 */ 431static int pico_igmp_compatibility_mode(struct pico_frame *f) 432{ 433 struct pico_ipv4_hdr *hdr = NULL; 434 struct pico_ipv4_link *link = NULL; 435 struct pico_tree_node *index = NULL, *_tmp = NULL; 436 struct igmp_timer t = { 437 0 438 }; 439 uint8_t ihl = 24, datalen = 0; 440 struct igmp_message *message = NULL; 441 struct mcast_parameters *p = NULL; 442 struct pico_ip4 mcast_group = { 443 0 444 }; 445 446 link = pico_ipv4_link_by_dev(f->dev); 447 if (!link) 448 return -1; 449 450 hdr = (struct pico_ipv4_hdr *) f->net_hdr; 451 ihl = (uint8_t)((hdr->vhl & 0x0F) * 4); /* IHL is in 32bit words */ 452 datalen = (uint8_t)(short_be(hdr->len) - ihl); 453 igmp_dbg("IGMP: IHL = %u, LEN = %u, OCTETS = %u\n", ihl, short_be(hdr->len), datalen); 454 455 if (datalen >= 12) { 456 /* IGMPv3 query */ 457 t.type = IGMP_TIMER_V2_QUERIER; 458 if (pico_igmp_timer_is_running(&t)) { /* IGMPv2 querier present timer still running */ 459 igmp_dbg("Timer is already running\n"); 460 return -1; 461 } else { 462 link->mcast_compatibility = PICO_IGMPV3; 463 igmp_dbg("IGMP Compatibility: v3\n"); 464 return 0; 465 } 466 } else if (datalen == 8) { 467 struct igmp_message *query = (struct igmp_message *)f->transport_hdr; 468 /* Check if max_resp_time is set RFC 3376 $7.1 */ 469 if (query->max_resp_time != 0) { 470 /* IGMPv2 query */ 471 /* When changing compatibility mode, cancel all pending response 472 * and retransmission timers. 473 */ 474 pico_tree_foreach_safe(index, &IGMPTimers, _tmp) 475 { 476 ((struct igmp_timer *)index->keyValue)->stopped = IGMP_TIMER_STOPPED; 477 pico_tree_delete(&IGMPTimers, index->keyValue); 478 } 479 igmp_dbg("IGMP: switch to compatibility mode IGMPv2\n"); 480 link->mcast_compatibility = PICO_IGMPV2; 481 /* Reset the event and state to prevent deadlock */ 482 message = (struct igmp_message *)f->transport_hdr; 483 mcast_group.addr = message->mcast_group; 484 p = pico_igmp_find_parameter(&link->address, &mcast_group); 485 if(p) { 486 p->state = IGMP_STATE_NON_MEMBER; 487 p->event = IGMP_EVENT_CREATE_GROUP; 488 } 489 490 t.type = IGMP_TIMER_V2_QUERIER; 491 t.delay = ((IGMP_ROBUSTNESS * link->mcast_last_query_interval) + IGMP_QUERY_RESPONSE_INTERVAL) * 1000; 492 t.f = f; 493 t.callback = pico_igmp_v2querier_expired; 494 /* only one of this type of timer may exist! */ 495 if (pico_igmp_timer_start(&t) < 0) 496 return -1; 497 } else { 498 /* IGMPv1 query, not supported */ 499 return -1; 500 } 501 } else { 502 /* invalid query, silently ignored */ 503 return -1; 504 } 505 506 return 0; 507} 508 509static struct mcast_parameters *pico_igmp_analyse_packet(struct pico_frame *f) 510{ 511 struct igmp_message *message = NULL; 512 struct mcast_parameters *p = NULL; 513 struct pico_ipv4_link *link = NULL; 514 struct pico_ip4 mcast_group = { 515 0 516 }; 517 518 link = pico_ipv4_link_by_dev(f->dev); 519 if (!link) 520 return NULL; 521 522 /* IGMPv2 and IGMPv3 have a similar structure for the first 8 bytes */ 523 message = (struct igmp_message *)f->transport_hdr; 524 mcast_group.addr = message->mcast_group; 525 p = pico_igmp_find_parameter(&link->address, &mcast_group); 526 if (!p && mcast_group.addr == 0) { /* general query */ 527 p = PICO_ZALLOC(sizeof(struct mcast_parameters)); 528 if (!p) 529 return NULL; 530 531 p->state = IGMP_STATE_NON_MEMBER; 532 p->mcast_link.ip4 = link->address; 533 p->mcast_group.ip4 = mcast_group; 534 if (pico_tree_insert(&IGMPParameters, p)) { 535 igmp_dbg("IGMP: Failed to insert parameters in tree\n"); 536 PICO_FREE(p); 537 return NULL; 538 } 539 } else if (!p) { 540 return NULL; 541 } 542 543 switch (message->type) { 544 case IGMP_TYPE_MEM_QUERY: 545 p->event = IGMP_EVENT_QUERY_RECV; 546 break; 547 case IGMP_TYPE_MEM_REPORT_V1: 548 p->event = IGMP_EVENT_REPORT_RECV; 549 break; 550 case IGMP_TYPE_MEM_REPORT_V2: 551 p->event = IGMP_EVENT_REPORT_RECV; 552 break; 553 case IGMP_TYPE_MEM_REPORT_V3: 554 p->event = IGMP_EVENT_REPORT_RECV; 555 break; 556 default: 557 return NULL; 558 } 559 p->max_resp_time = message->max_resp_time; /* if IGMPv3 report this will be 0 (res0 field) */ 560 p->f = f; 561 562 return p; 563} 564 565static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f) 566{ 567 struct mcast_parameters *p = NULL; 568 IGNORE_PARAMETER(self); 569 570 if (!pico_igmp_is_checksum_valid(f)) 571 goto out; 572 573 p = pico_igmp_analyse_packet(f); 574 if (!p) 575 goto out; 576 577 if (pico_igmp_compatibility_mode(f) < 0) 578 goto out; 579 580 return pico_igmp_process_event(p); 581 582out: 583 pico_frame_discard(f); 584 return 0; 585} 586 587static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f) 588{ 589 /* packets are directly transferred to the IP layer by calling pico_ipv4_frame_push */ 590 IGNORE_PARAMETER(self); 591 IGNORE_PARAMETER(f); 592 return 0; 593} 594 595/* Interface: protocol definition */ 596struct pico_protocol pico_proto_igmp = { 597 .name = "igmp", 598 .proto_number = PICO_PROTO_IGMP, 599 .layer = PICO_LAYER_TRANSPORT, 600 .process_in = pico_igmp_process_in, 601 .process_out = pico_igmp_process_out, 602 .q_in = &igmp_in, 603 .q_out = &igmp_out, 604}; 605 606int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t filter_mode, struct pico_tree *_MCASTFilter, uint8_t state) 607{ 608 struct mcast_parameters *p = NULL; 609 610 if (!mcast_link || !mcast_group) { 611 pico_err = PICO_ERR_EINVAL; 612 return -1; 613 } 614 615 if (mcast_group->addr == IGMP_ALL_HOST_GROUP) 616 return 0; 617 618 p = pico_igmp_find_parameter(mcast_link, mcast_group); 619 if (!p && state == PICO_IGMP_STATE_CREATE) { 620 p = PICO_ZALLOC(sizeof(struct mcast_parameters)); 621 if (!p) { 622 pico_err = PICO_ERR_ENOMEM; 623 return -1; 624 } 625 626 p->state = IGMP_STATE_NON_MEMBER; 627 p->mcast_link.ip4 = *mcast_link; 628 p->mcast_group.ip4 = *mcast_group; 629 if (pico_tree_insert(&IGMPParameters, p)) { 630 igmp_dbg("IGMP: Failed to insert parameters in tree\n"); 631 PICO_FREE(p); 632 return -1; 633 } 634 635 } else if (!p) { 636 pico_err = PICO_ERR_EINVAL; 637 return -1; 638 } 639 640 switch (state) { 641 case PICO_IGMP_STATE_CREATE: 642 p->event = IGMP_EVENT_CREATE_GROUP; 643 break; 644 645 case PICO_IGMP_STATE_UPDATE: 646 p->event = IGMP_EVENT_UPDATE_GROUP; 647 break; 648 649 case PICO_IGMP_STATE_DELETE: 650 p->event = IGMP_EVENT_DELETE_GROUP; 651 break; 652 653 default: 654 return -1; 655 } 656 p->filter_mode = filter_mode; 657 p->MCASTFilter = _MCASTFilter; 658 659 return pico_igmp_process_event(p); 660} 661 662static int pico_igmp_send_report(struct mcast_parameters *p, struct pico_frame *f) 663{ 664 struct pico_ip4 dst = { 665 0 666 }; 667 struct pico_ip4 mcast_group = { 668 0 669 }; 670 struct pico_ipv4_link *link = NULL; 671 672 link = pico_ipv4_link_get((struct pico_ip4*)&p->mcast_link); 673 if (!link) 674 return -1; 675 676 mcast_group = p->mcast_group.ip4; 677 switch (link->mcast_compatibility) { 678 case PICO_IGMPV2: 679 if (p->event == IGMP_EVENT_DELETE_GROUP) 680 dst.addr = IGMP_ALL_ROUTER_GROUP; 681 else 682 dst.addr = mcast_group.addr; 683 684 break; 685 686 case PICO_IGMPV3: 687 dst.addr = IGMPV3_ALL_ROUTER_GROUP; 688 break; 689 690 default: 691 pico_err = PICO_ERR_EPROTONOSUPPORT; 692 return -1; 693 } 694 695 igmp_dbg("IGMP: send membership report on group %08X to %08X\n", mcast_group.addr, dst.addr); 696 pico_ipv4_frame_push(f, &dst, PICO_PROTO_IGMP); 697 return 0; 698} 699static int8_t pico_igmpv3_generate_filter(struct mcast_filter_parameters *filter, struct mcast_parameters *p) 700{ 701 struct pico_mcast_group *g = NULL, test = { 702 0 703 }; 704 struct pico_tree *IGMPFilter = NULL; 705 struct pico_ipv4_link *link = (struct pico_ipv4_link*) filter->link; 706 filter->p = (struct mcast_parameters *)p; 707 filter->allow = &IGMPAllow; 708 filter->block = &IGMPBlock; 709 filter->filter = IGMPFilter; 710 filter->sources = 0; 711 filter->proto = PICO_IGMPV3; 712 test.mcast_addr = p->mcast_group; 713 g = pico_tree_findKey(link->MCASTGroups, &test); 714 if (!g) { 715 pico_err = PICO_ERR_EINVAL; 716 return -1; 717 } 718 719 filter->g = (struct pico_mcast_group *)g; 720 return pico_mcast_generate_filter(filter, p); 721} 722static int8_t pico_igmpv3_generate_report(struct mcast_filter_parameters *filter, struct mcast_parameters *p) 723{ 724 struct igmpv3_report *report = NULL; 725 struct igmpv3_group_record *record = NULL; 726 struct pico_tree_node *index = NULL; 727 struct pico_device *dev = NULL; 728 uint16_t len = 0; 729 uint16_t i = 0; 730 len = (uint16_t)(sizeof(struct igmpv3_report) + sizeof(struct igmpv3_group_record) + (filter->sources * sizeof(struct pico_ip4))); 731 dev = pico_ipv4_link_find((struct pico_ip4 *)&p->mcast_link); 732 p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(IP_OPTION_ROUTER_ALERT_LEN + len)); 733 p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN); 734 p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN; 735 p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN); 736 /* p->f->len is correctly set by alloc */ 737 738 report = (struct igmpv3_report *)p->f->transport_hdr; 739 report->type = IGMP_TYPE_MEM_REPORT_V3; 740 report->res0 = 0; 741 report->crc = 0; 742 report->res1 = 0; 743 report->groups = short_be(1); 744 745 record = (struct igmpv3_group_record *)(((uint8_t *)report) + sizeof(struct igmpv3_report)); 746 record->type = filter->record_type; 747 record->aux = 0; 748 record->sources = short_be(filter->sources); 749 record->mcast_group = p->mcast_group.ip4.addr; 750 if (filter->filter && !pico_tree_empty(filter->filter)) { 751 uint32_t *source_addr = (uint32_t *)((uint8_t *)record + sizeof(struct igmpv3_group_record)); 752 i = 0; 753 pico_tree_foreach(index, filter->filter) 754 { 755 source_addr[i] = ((struct pico_ip4 *)index->keyValue)->addr; 756 i++; 757 } 758 } 759 760 if(i != filter->sources) { 761 return -1; 762 } 763 764 report->crc = short_be(pico_checksum(report, len)); 765 return 0; 766} 767static int8_t pico_igmpv2_generate_report(struct mcast_parameters *p) 768{ 769 struct igmp_message *report = NULL; 770 uint8_t report_type = IGMP_TYPE_MEM_REPORT_V2; 771 struct pico_device *dev = NULL; 772 if (p->event == IGMP_EVENT_DELETE_GROUP) 773 report_type = IGMP_TYPE_LEAVE_GROUP; 774 775 dev = pico_ipv4_link_find((struct pico_ip4 *)&p->mcast_link); 776 p->f = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, IP_OPTION_ROUTER_ALERT_LEN + sizeof(struct igmp_message)); 777 p->f->net_len = (uint16_t)(p->f->net_len + IP_OPTION_ROUTER_ALERT_LEN); 778 p->f->transport_hdr += IP_OPTION_ROUTER_ALERT_LEN; 779 p->f->transport_len = (uint16_t)(p->f->transport_len - IP_OPTION_ROUTER_ALERT_LEN); 780 /* p->f->len is correctly set by alloc */ 781 782 report = (struct igmp_message *)p->f->transport_hdr; 783 report->type = report_type; 784 report->max_resp_time = IGMP_DEFAULT_MAX_RESPONSE_TIME; 785 report->mcast_group = p->mcast_group.ip4.addr; 786 787 report->crc = 0; 788 report->crc = short_be(pico_checksum(report, sizeof(struct igmp_message))); 789 return 0; 790} 791static int8_t pico_igmp_generate_report(struct mcast_parameters *p) 792{ 793 struct mcast_filter_parameters filter; 794 int8_t result; 795 796 filter.link = (union pico_link *)pico_ipv4_link_get((struct pico_ip4 *) &p->mcast_link); 797 if (!filter.link) { 798 pico_err = PICO_ERR_EINVAL; 799 return -1; 800 } 801 802 switch (filter.link->ipv4.mcast_compatibility) { 803 case PICO_IGMPV1: 804 pico_err = PICO_ERR_EPROTONOSUPPORT; 805 return -1; 806 807 case PICO_IGMPV2: 808 { 809 return pico_igmpv2_generate_report(p); 810 } 811 case PICO_IGMPV3: 812 { 813 result = pico_igmpv3_generate_filter(&filter, p); 814 if(result < 0) 815 return -1; 816 817 if(result != MCAST_NO_REPORT) 818 return pico_igmpv3_generate_report(&filter, p); 819 } 820 break; 821 default: 822 pico_err = PICO_ERR_EINVAL; 823 return -1; 824 } 825 826 return 0; 827} 828 829/* stop timer, send leave if flag set */ 830static int stslifs(struct mcast_parameters *p) 831{ 832 struct igmp_timer t = { 833 0 834 }; 835 836 igmp_dbg("IGMP: event = leave group | action = stop timer, send leave if flag set\n"); 837 838 t.type = IGMP_TIMER_GROUP_REPORT; 839 t.mcast_link = p->mcast_link.ip4; 840 t.mcast_group = p->mcast_group.ip4; 841 if (pico_igmp_timer_stop(&t) < 0) 842 return -1; 843 844 if(pico_igmp_generate_report(p) < 0) 845 return -1; 846 /* always send leave, even if not last host */ 847 if (pico_igmp_send_report(p, p->f) < 0) 848 return -1; 849 850 pico_igmp_delete_parameter(p); 851 igmp_dbg("IGMP: new state = non-member\n"); 852 return 0; 853} 854 855/* send report, set flag, start timer */ 856static int srsfst(struct mcast_parameters *p) 857{ 858 struct igmp_timer t = { 859 0 860 }; 861 struct pico_frame *copy_frame = NULL; 862 863 igmp_dbg("IGMP: event = join group | action = send report, set flag, start timer\n"); 864 865 p->last_host = IGMP_HOST_LAST; 866 867 if (pico_igmp_generate_report(p) < 0) 868 return -1; 869 870 if (!p->f) 871 return 0; 872 873 copy_frame = pico_frame_copy(p->f); 874 if (!copy_frame) { 875 pico_err = PICO_ERR_ENOMEM; 876 return -1; 877 } 878 879 if (pico_igmp_send_report(p, copy_frame) < 0) 880 return -1; 881 882 t.type = IGMP_TIMER_GROUP_REPORT; 883 t.mcast_link = p->mcast_link.ip4; 884 t.mcast_group = p->mcast_group.ip4; 885 t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000)); 886 t.f = p->f; 887 t.callback = pico_igmp_report_expired; 888 if (pico_igmp_timer_start(&t) < 0) 889 return -1; 890 891 p->state = IGMP_STATE_DELAYING_MEMBER; 892 igmp_dbg("IGMP: new state = delaying member\n"); 893 return 0; 894} 895 896/* merge report, send report, reset timer (IGMPv3 only) */ 897static int mrsrrt(struct mcast_parameters *p) 898{ 899 struct igmp_timer *t = NULL; 900 struct pico_frame *copy_frame = NULL; 901 struct pico_ipv4_link *link = NULL; 902 903 igmp_dbg("IGMP: event = update group | action = merge report, send report, reset timer (IGMPv3 only)\n"); 904 905 link = pico_ipv4_link_get((struct pico_ip4 *)&p->mcast_link); 906 if (!link) 907 return -1; 908 909 if (link->mcast_compatibility != PICO_IGMPV3) { 910 igmp_dbg("IGMP: no IGMPv3 compatible router on network\n"); 911 return -1; 912 } 913 914 /* XXX: merge with pending report rfc 3376 $5.1 */ 915 916 copy_frame = pico_frame_copy(p->f); 917 if (!copy_frame) 918 return -1; 919 920 if (pico_igmp_send_report(p, copy_frame) < 0) 921 return -1; 922 923 t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link.ip4, &p->mcast_group.ip4); 924 if (!t) 925 return -1; 926 927 t->delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000)); 928 pico_igmp_timer_reset(t); 929 930 p->state = IGMP_STATE_DELAYING_MEMBER; 931 igmp_dbg("IGMP: new state = delaying member\n"); 932 return 0; 933} 934 935/* send report, start timer (IGMPv3 only) */ 936static int srst(struct mcast_parameters *p) 937{ 938 struct igmp_timer t = { 939 0 940 }; 941 struct pico_frame *copy_frame = NULL; 942 struct pico_ipv4_link *link = NULL; 943 944 igmp_dbg("IGMP: event = update group | action = send report, start timer (IGMPv3 only)\n"); 945 946 link = pico_ipv4_link_get(&p->mcast_link.ip4); 947 if (!link) 948 return -1; 949 950 if (link->mcast_compatibility != PICO_IGMPV3) { 951 igmp_dbg("IGMP: no IGMPv3 compatible router on network\n"); 952 return -1; 953 } 954 955 if (pico_igmp_generate_report(p) < 0) 956 return -1; 957 958 if (!p->f) 959 return 0; 960 961 copy_frame = pico_frame_copy(p->f); 962 if (!copy_frame) 963 return -1; 964 965 if (pico_igmp_send_report(p, copy_frame) < 0) 966 return -1; 967 968 t.type = IGMP_TIMER_GROUP_REPORT; 969 t.mcast_link = p->mcast_link.ip4; 970 t.mcast_group = p->mcast_group.ip4; 971 t.delay = (pico_rand() % (IGMP_UNSOLICITED_REPORT_INTERVAL * 10000)); 972 t.f = p->f; 973 t.callback = pico_igmp_report_expired; 974 if (pico_igmp_timer_start(&t) < 0) 975 return -1; 976 977 p->state = IGMP_STATE_DELAYING_MEMBER; 978 igmp_dbg("IGMP: new state = delaying member\n"); 979 return 0; 980} 981 982/* send leave if flag set */ 983static int slifs(struct mcast_parameters *p) 984{ 985 igmp_dbg("IGMP: event = leave group | action = send leave if flag set\n"); 986 987 /* always send leave, even if not last host */ 988 if(pico_igmp_generate_report(p) < 0) 989 return -1; 990 if (pico_igmp_send_report(p, p->f) < 0) 991 return -1; 992 993 pico_igmp_delete_parameter(p); 994 igmp_dbg("IGMP: new state = non-member\n"); 995 return 0; 996} 997 998/* start timer */ 999static int st(struct mcast_parameters *p) 1000{ 1001 struct igmp_timer t = { 1002 0 1003 }; 1004 1005 igmp_dbg("IGMP: event = query received | action = start timer\n"); 1006 1007 if (pico_igmp_generate_report(p) < 0) { 1008 igmp_dbg("Failed to generate report\n"); 1009 return -1; 1010 } 1011 1012 if (!p->f) { 1013 igmp_dbg("No pending frame\n"); 1014 return -1; 1015 } 1016 1017 t.type = IGMP_TIMER_GROUP_REPORT; 1018 t.mcast_link = p->mcast_link.ip4; 1019 t.mcast_group = p->mcast_group.ip4; 1020 t.delay = (pico_rand() % ((1u + p->max_resp_time) * 100u)); 1021 t.f = p->f; 1022 t.callback = pico_igmp_report_expired; 1023 if (pico_igmp_timer_start(&t) < 0) 1024 return -1; 1025 1026 p->state = IGMP_STATE_DELAYING_MEMBER; 1027 igmp_dbg("IGMP: new state = delaying member\n"); 1028 return 0; 1029} 1030 1031/* stop timer, clear flag */ 1032static int stcl(struct mcast_parameters *p) 1033{ 1034 struct igmp_timer t = { 1035 0 1036 }; 1037 1038 igmp_dbg("IGMP: event = report received | action = stop timer, clear flag\n"); 1039 1040 t.type = IGMP_TIMER_GROUP_REPORT; 1041 t.mcast_link = p->mcast_link.ip4; 1042 t.mcast_group = p->mcast_group.ip4; 1043 if (pico_igmp_timer_stop(&t) < 0) 1044 return -1; 1045 1046 p->last_host = IGMP_HOST_NOT_LAST; 1047 p->state = IGMP_STATE_IDLE_MEMBER; 1048 igmp_dbg("IGMP: new state = idle member\n"); 1049 return 0; 1050} 1051 1052/* send report, set flag */ 1053static int srsf(struct mcast_parameters *p) 1054{ 1055 igmp_dbg("IGMP: event = timer expired | action = send report, set flag\n"); 1056 1057 if (pico_igmp_send_report(p, p->f) < 0) 1058 return -1; 1059 1060 p->state = IGMP_STATE_IDLE_MEMBER; 1061 igmp_dbg("IGMP: new state = idle member\n"); 1062 return 0; 1063} 1064 1065/* reset timer if max response time < current timer */ 1066static int rtimrtct(struct mcast_parameters *p) 1067{ 1068 struct igmp_timer *t = NULL; 1069 uint32_t time_to_run = 0; 1070 1071 igmp_dbg("IGMP: event = query received | action = reset timer if max response time < current timer\n"); 1072 1073 t = pico_igmp_find_timer(IGMP_TIMER_GROUP_REPORT, &p->mcast_link.ip4, &p->mcast_group.ip4); 1074 if (!t) 1075 return -1; 1076 1077 time_to_run = (uint32_t)(t->start + t->delay - PICO_TIME_MS()); 1078 if ((p->max_resp_time * 100u) < time_to_run) { /* max_resp_time in units of 1/10 seconds */ 1079 t->delay = pico_rand() % ((1u + p->max_resp_time) * 100u); 1080 pico_igmp_timer_reset(t); 1081 } 1082 1083 p->state = IGMP_STATE_DELAYING_MEMBER; 1084 igmp_dbg("IGMP: new state = delaying member\n"); 1085 return 0; 1086} 1087 1088static int discard(struct mcast_parameters *p) 1089{ 1090 igmp_dbg("IGMP: ignore and discard frame\n"); 1091 pico_frame_discard(p->f); 1092 return 0; 1093} 1094 1095/* finite state machine table */ 1096static const callback host_membership_diagram_table[3][6] = 1097{ /* event |Delete Group |Create Group |Update Group |Query Received |Report Received |Timer Expired */ 1098/* state Non-Member */ 1099 { discard, srsfst, srsfst, discard, discard, discard }, 1100/* state Delaying Member */ { stslifs, mrsrrt, mrsrrt, rtimrtct, stcl, srsf }, 1101/* state Idle Member */ { slifs, srst, srst, st, discard, discard } 1102}; 1103 1104static int pico_igmp_process_event(struct mcast_parameters *p) 1105{ 1106 struct pico_tree_node *index = NULL; 1107 struct mcast_parameters *_p = NULL; 1108 1109 igmp_dbg("IGMP: process event on group address %08X\n", p->mcast_group.ip4.addr); 1110 if (p->event == IGMP_EVENT_QUERY_RECV && p->mcast_group.ip4.addr == 0) { /* general query */ 1111 pico_tree_foreach(index, &IGMPParameters) { 1112 _p = index->keyValue; 1113 _p->max_resp_time = p->max_resp_time; 1114 _p->event = IGMP_EVENT_QUERY_RECV; 1115 igmp_dbg("IGMP: for each mcast_group = %08X | state = %u\n", _p->mcast_group.ip4.addr, _p->state); 1116 host_membership_diagram_table[_p->state][_p->event](_p); 1117 } 1118 } else { 1119 igmp_dbg("IGMP: state = %u (0: non-member - 1: delaying member - 2: idle member)\n", p->state); 1120 host_membership_diagram_table[p->state][p->event](p); 1121 } 1122 1123 return 0; 1124} 1125 1126#else 1127static struct pico_queue igmp_in = { 1128 0 1129}; 1130static struct pico_queue igmp_out = { 1131 0 1132}; 1133 1134static int pico_igmp_process_in(struct pico_protocol *self, struct pico_frame *f) 1135{ 1136 IGNORE_PARAMETER(self); 1137 IGNORE_PARAMETER(f); 1138 pico_err = PICO_ERR_EPROTONOSUPPORT; 1139 return -1; 1140} 1141 1142static int pico_igmp_process_out(struct pico_protocol *self, struct pico_frame *f) 1143{ 1144 IGNORE_PARAMETER(self); 1145 IGNORE_PARAMETER(f); 1146 return -1; 1147} 1148 1149/* Interface: protocol definition */ 1150struct pico_protocol pico_proto_igmp = { 1151 .name = "igmp", 1152 .proto_number = PICO_PROTO_IGMP, 1153 .layer = PICO_LAYER_TRANSPORT, 1154 .process_in = pico_igmp_process_in, 1155 .process_out = pico_igmp_process_out, 1156 .q_in = &igmp_in, 1157 .q_out = &igmp_out, 1158}; 1159 1160int pico_igmp_state_change(struct pico_ip4 *mcast_link, struct pico_ip4 *mcast_group, uint8_t filter_mode, struct pico_tree *_MCASTFilter, uint8_t state) 1161{ 1162 IGNORE_PARAMETER(mcast_link); 1163 IGNORE_PARAMETER(mcast_group); 1164 IGNORE_PARAMETER(filter_mode); 1165 IGNORE_PARAMETER(_MCASTFilter); 1166 IGNORE_PARAMETER(state); 1167 pico_err = PICO_ERR_EPROTONOSUPPORT; 1168 return -1; 1169} 1170#endif 1171