Annotation of embedaddon/quagga/pimd/pim_igmpv3.c, revision 1.1
1.1 ! misho 1: /*
! 2: PIM for Quagga
! 3: Copyright (C) 2008 Everton da Silva Marques
! 4:
! 5: This program is free software; you can redistribute it and/or modify
! 6: it under the terms of the GNU General Public License as published by
! 7: the Free Software Foundation; either version 2 of the License, or
! 8: (at your option) any later version.
! 9:
! 10: This program is distributed in the hope that it will be useful, but
! 11: WITHOUT ANY WARRANTY; without even the implied warranty of
! 12: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
! 13: General Public License for more details.
! 14:
! 15: You should have received a copy of the GNU General Public License
! 16: along with this program; see the file COPYING; if not, write to the
! 17: Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
! 18: MA 02110-1301 USA
! 19:
! 20: $QuaggaId: $Format:%an, %ai, %h$ $
! 21: */
! 22:
! 23: #include <zebra.h>
! 24: #include "log.h"
! 25: #include "memory.h"
! 26:
! 27: #include "pimd.h"
! 28: #include "pim_iface.h"
! 29: #include "pim_igmp.h"
! 30: #include "pim_igmpv3.h"
! 31: #include "pim_str.h"
! 32: #include "pim_util.h"
! 33: #include "pim_time.h"
! 34: #include "pim_zebra.h"
! 35: #include "pim_oil.h"
! 36:
! 37: static void group_retransmit_timer_on(struct igmp_group *group);
! 38: static long igmp_group_timer_remain_msec(struct igmp_group *group);
! 39: static long igmp_source_timer_remain_msec(struct igmp_source *source);
! 40: static void group_query_send(struct igmp_group *group);
! 41: static void source_query_send_by_flag(struct igmp_group *group,
! 42: int num_sources_tosend);
! 43:
! 44: static void on_trace(const char *label,
! 45: struct interface *ifp, struct in_addr from,
! 46: struct in_addr group_addr,
! 47: int num_sources, struct in_addr *sources)
! 48: {
! 49: if (PIM_DEBUG_IGMP_TRACE) {
! 50: char from_str[100];
! 51: char group_str[100];
! 52:
! 53: pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
! 54: pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
! 55:
! 56: zlog_debug("%s: from %s on %s: group=%s sources=%d",
! 57: label, from_str, ifp->name, group_str, num_sources);
! 58: }
! 59: }
! 60:
! 61: int igmp_group_compat_mode(const struct igmp_sock *igmp,
! 62: const struct igmp_group *group)
! 63: {
! 64: struct pim_interface *pim_ifp;
! 65: int64_t now_dsec;
! 66: long older_host_present_interval_dsec;
! 67:
! 68: zassert(igmp);
! 69: zassert(igmp->interface);
! 70: zassert(igmp->interface->info);
! 71:
! 72: pim_ifp = igmp->interface->info;
! 73:
! 74: /*
! 75: RFC 3376: 8.13. Older Host Present Interval
! 76:
! 77: This value MUST be ((the Robustness Variable) times (the Query
! 78: Interval)) plus (one Query Response Interval).
! 79:
! 80: older_host_present_interval_dsec = \
! 81: igmp->querier_robustness_variable * \
! 82: 10 * igmp->querier_query_interval + \
! 83: pim_ifp->query_max_response_time_dsec;
! 84: */
! 85: older_host_present_interval_dsec =
! 86: PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
! 87: igmp->querier_query_interval,
! 88: pim_ifp->igmp_query_max_response_time_dsec);
! 89:
! 90: now_dsec = pim_time_monotonic_dsec();
! 91: if (now_dsec < 1) {
! 92: /* broken timer logged by pim_time_monotonic_dsec() */
! 93: return 3;
! 94: }
! 95:
! 96: if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
! 97: return 1; /* IGMPv1 */
! 98:
! 99: if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
! 100: return 2; /* IGMPv2 */
! 101:
! 102: return 3; /* IGMPv3 */
! 103: }
! 104:
! 105: void igmp_group_reset_gmi(struct igmp_group *group)
! 106: {
! 107: long group_membership_interval_msec;
! 108: struct pim_interface *pim_ifp;
! 109: struct igmp_sock *igmp;
! 110: struct interface *ifp;
! 111:
! 112: igmp = group->group_igmp_sock;
! 113: ifp = igmp->interface;
! 114: pim_ifp = ifp->info;
! 115:
! 116: /*
! 117: RFC 3376: 8.4. Group Membership Interval
! 118:
! 119: The Group Membership Interval is the amount of time that must pass
! 120: before a multicast router decides there are no more members of a
! 121: group or a particular source on a network.
! 122:
! 123: This value MUST be ((the Robustness Variable) times (the Query
! 124: Interval)) plus (one Query Response Interval).
! 125:
! 126: group_membership_interval_msec = querier_robustness_variable *
! 127: (1000 * querier_query_interval) +
! 128: 100 * query_response_interval_dsec;
! 129: */
! 130: group_membership_interval_msec =
! 131: PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
! 132: igmp->querier_query_interval,
! 133: pim_ifp->igmp_query_max_response_time_dsec);
! 134:
! 135: if (PIM_DEBUG_IGMP_TRACE) {
! 136: char group_str[100];
! 137: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 138: zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
! 139: group_str,
! 140: group_membership_interval_msec / 1000,
! 141: group_membership_interval_msec % 1000,
! 142: ifp->name);
! 143: }
! 144:
! 145: /*
! 146: RFC 3376: 6.2.2. Definition of Group Timers
! 147:
! 148: The group timer is only used when a group is in EXCLUDE mode and
! 149: it represents the time for the *filter-mode* of the group to
! 150: expire and switch to INCLUDE mode.
! 151: */
! 152: zassert(group->group_filtermode_isexcl);
! 153:
! 154: igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
! 155: }
! 156:
! 157: static int igmp_source_timer(struct thread *t)
! 158: {
! 159: struct igmp_source *source;
! 160: struct igmp_group *group;
! 161:
! 162: zassert(t);
! 163: source = THREAD_ARG(t);
! 164: zassert(source);
! 165:
! 166: group = source->source_group;
! 167:
! 168: if (PIM_DEBUG_IGMP_TRACE) {
! 169: char group_str[100];
! 170: char source_str[100];
! 171: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 172: pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
! 173: zlog_debug("%s: Source timer expired for group %s source %s on %s",
! 174: __PRETTY_FUNCTION__,
! 175: group_str, source_str,
! 176: group->group_igmp_sock->interface->name);
! 177: }
! 178:
! 179: zassert(source->t_source_timer);
! 180: source->t_source_timer = 0;
! 181:
! 182: /*
! 183: RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
! 184:
! 185: Group
! 186: Filter-Mode Source Timer Value Action
! 187: ----------- ------------------ ------
! 188: INCLUDE TIMER == 0 Suggest to stop forwarding
! 189: traffic from source and
! 190: remove source record. If
! 191: there are no more source
! 192: records for the group, delete
! 193: group record.
! 194:
! 195: EXCLUDE TIMER == 0 Suggest to not forward
! 196: traffic from source
! 197: (DO NOT remove record)
! 198:
! 199: Source timer switched from (T > 0) to (T == 0): disable forwarding.
! 200: */
! 201:
! 202: zassert(!source->t_source_timer);
! 203:
! 204: if (group->group_filtermode_isexcl) {
! 205: /* EXCLUDE mode */
! 206:
! 207: igmp_source_forward_stop(source);
! 208: }
! 209: else {
! 210: /* INCLUDE mode */
! 211:
! 212: /* igmp_source_delete() will stop forwarding source */
! 213: igmp_source_delete(source);
! 214:
! 215: /*
! 216: If there are no more source records for the group, delete group
! 217: record.
! 218: */
! 219: if (!listcount(group->group_source_list)) {
! 220: igmp_group_delete_empty_include(group);
! 221: }
! 222: }
! 223:
! 224: return 0;
! 225: }
! 226:
! 227: static void source_timer_off(struct igmp_group *group,
! 228: struct igmp_source *source)
! 229: {
! 230: if (!source->t_source_timer)
! 231: return;
! 232:
! 233: if (PIM_DEBUG_IGMP_TRACE) {
! 234: char group_str[100];
! 235: char source_str[100];
! 236: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 237: pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
! 238: zlog_debug("Cancelling TIMER event for group %s source %s on %s",
! 239: group_str, source_str,
! 240: group->group_igmp_sock->interface->name);
! 241: }
! 242:
! 243: THREAD_OFF(source->t_source_timer);
! 244: zassert(!source->t_source_timer);
! 245: }
! 246:
! 247: static void igmp_source_timer_on(struct igmp_group *group,
! 248: struct igmp_source *source,
! 249: long interval_msec)
! 250: {
! 251: source_timer_off(group, source);
! 252:
! 253: if (PIM_DEBUG_IGMP_EVENTS) {
! 254: char group_str[100];
! 255: char source_str[100];
! 256: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 257: pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
! 258: zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
! 259: interval_msec / 1000,
! 260: interval_msec % 1000,
! 261: group_str, source_str,
! 262: group->group_igmp_sock->interface->name);
! 263: }
! 264:
! 265: THREAD_TIMER_MSEC_ON(master, source->t_source_timer,
! 266: igmp_source_timer,
! 267: source, interval_msec);
! 268: zassert(source->t_source_timer);
! 269:
! 270: /*
! 271: RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
! 272:
! 273: Source timer switched from (T == 0) to (T > 0): enable forwarding.
! 274: */
! 275: igmp_source_forward_start(source);
! 276: }
! 277:
! 278: void igmp_source_reset_gmi(struct igmp_sock *igmp,
! 279: struct igmp_group *group,
! 280: struct igmp_source *source)
! 281: {
! 282: long group_membership_interval_msec;
! 283: struct pim_interface *pim_ifp;
! 284: struct interface *ifp;
! 285:
! 286: ifp = igmp->interface;
! 287: pim_ifp = ifp->info;
! 288:
! 289: group_membership_interval_msec =
! 290: PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
! 291: igmp->querier_query_interval,
! 292: pim_ifp->igmp_query_max_response_time_dsec);
! 293:
! 294: if (PIM_DEBUG_IGMP_TRACE) {
! 295: char group_str[100];
! 296: char source_str[100];
! 297:
! 298: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 299: pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
! 300:
! 301: zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
! 302: source_str,
! 303: group_membership_interval_msec / 1000,
! 304: group_membership_interval_msec % 1000,
! 305: group_str,
! 306: ifp->name);
! 307: }
! 308:
! 309: igmp_source_timer_on(group, source,
! 310: group_membership_interval_msec);
! 311: }
! 312:
! 313: static void source_mark_delete_flag(struct list *source_list)
! 314: {
! 315: struct listnode *src_node;
! 316: struct igmp_source *src;
! 317:
! 318: for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
! 319: IGMP_SOURCE_DO_DELETE(src->source_flags);
! 320: }
! 321: }
! 322:
! 323: static void source_mark_send_flag(struct list *source_list)
! 324: {
! 325: struct listnode *src_node;
! 326: struct igmp_source *src;
! 327:
! 328: for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
! 329: IGMP_SOURCE_DO_SEND(src->source_flags);
! 330: }
! 331: }
! 332:
! 333: static int source_mark_send_flag_by_timer(struct list *source_list)
! 334: {
! 335: struct listnode *src_node;
! 336: struct igmp_source *src;
! 337: int num_marked_sources = 0;
! 338:
! 339: for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
! 340: /* Is source timer running? */
! 341: if (src->t_source_timer) {
! 342: IGMP_SOURCE_DO_SEND(src->source_flags);
! 343: ++num_marked_sources;
! 344: }
! 345: else {
! 346: IGMP_SOURCE_DONT_SEND(src->source_flags);
! 347: }
! 348: }
! 349:
! 350: return num_marked_sources;
! 351: }
! 352:
! 353: static void source_clear_send_flag(struct list *source_list)
! 354: {
! 355: struct listnode *src_node;
! 356: struct igmp_source *src;
! 357:
! 358: for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
! 359: IGMP_SOURCE_DONT_SEND(src->source_flags);
! 360: }
! 361: }
! 362:
! 363: /*
! 364: Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
! 365: */
! 366: static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
! 367: {
! 368: zassert(group->group_filtermode_isexcl);
! 369:
! 370: if (listcount(group->group_source_list) < 1) {
! 371: igmp_anysource_forward_start(group);
! 372: }
! 373: }
! 374:
! 375: void igmp_source_free(struct igmp_source *source)
! 376: {
! 377: /* make sure there is no source timer running */
! 378: zassert(!source->t_source_timer);
! 379:
! 380: XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
! 381: }
! 382:
! 383: static void source_channel_oil_detach(struct igmp_source *source)
! 384: {
! 385: if (source->source_channel_oil) {
! 386: pim_channel_oil_del(source->source_channel_oil);
! 387: source->source_channel_oil = 0;
! 388: }
! 389: }
! 390:
! 391: /*
! 392: igmp_source_delete: stop fowarding, and delete the source
! 393: igmp_source_forward_stop: stop fowarding, but keep the source
! 394: */
! 395: void igmp_source_delete(struct igmp_source *source)
! 396: {
! 397: struct igmp_group *group;
! 398:
! 399: group = source->source_group;
! 400:
! 401: if (PIM_DEBUG_IGMP_TRACE) {
! 402: char group_str[100];
! 403: char source_str[100];
! 404: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 405: pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
! 406: zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
! 407: source_str, group_str,
! 408: group->group_igmp_sock->fd,
! 409: group->group_igmp_sock->interface->name);
! 410: }
! 411:
! 412: source_timer_off(group, source);
! 413: igmp_source_forward_stop(source);
! 414:
! 415: /* sanity check that forwarding has been disabled */
! 416: if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
! 417: char group_str[100];
! 418: char source_str[100];
! 419: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 420: pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
! 421: zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
! 422: __PRETTY_FUNCTION__,
! 423: source_str, group_str,
! 424: group->group_igmp_sock->fd,
! 425: group->group_igmp_sock->interface->name);
! 426: /* warning only */
! 427: }
! 428:
! 429: source_channel_oil_detach(source);
! 430:
! 431: /*
! 432: notice that listnode_delete() can't be moved
! 433: into igmp_source_free() because the later is
! 434: called by list_delete_all_node()
! 435: */
! 436: listnode_delete(group->group_source_list, source);
! 437:
! 438: igmp_source_free(source);
! 439:
! 440: if (group->group_filtermode_isexcl) {
! 441: group_exclude_fwd_anysrc_ifempty(group);
! 442: }
! 443: }
! 444:
! 445: static void source_delete_by_flag(struct list *source_list)
! 446: {
! 447: struct listnode *src_node;
! 448: struct listnode *src_nextnode;
! 449: struct igmp_source *src;
! 450:
! 451: for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
! 452: if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
! 453: igmp_source_delete(src);
! 454: }
! 455:
! 456: void igmp_source_delete_expired(struct list *source_list)
! 457: {
! 458: struct listnode *src_node;
! 459: struct listnode *src_nextnode;
! 460: struct igmp_source *src;
! 461:
! 462: for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
! 463: if (!src->t_source_timer)
! 464: igmp_source_delete(src);
! 465: }
! 466:
! 467: struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
! 468: struct in_addr src_addr)
! 469: {
! 470: struct listnode *src_node;
! 471: struct igmp_source *src;
! 472:
! 473: for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
! 474: if (src_addr.s_addr == src->source_addr.s_addr)
! 475: return src;
! 476:
! 477: return 0;
! 478: }
! 479:
! 480: static struct igmp_source *source_new(struct igmp_group *group,
! 481: struct in_addr src_addr,
! 482: const char *ifname)
! 483: {
! 484: struct igmp_source *src;
! 485:
! 486: if (PIM_DEBUG_IGMP_TRACE) {
! 487: char group_str[100];
! 488: char source_str[100];
! 489: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 490: pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
! 491: zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
! 492: source_str, group_str,
! 493: group->group_igmp_sock->fd,
! 494: ifname);
! 495: }
! 496:
! 497: src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
! 498: if (!src) {
! 499: zlog_warn("%s %s: XMALLOC() failure",
! 500: __FILE__, __PRETTY_FUNCTION__);
! 501: return 0; /* error, not found, could not create */
! 502: }
! 503:
! 504: src->t_source_timer = 0;
! 505: src->source_group = group; /* back pointer */
! 506: src->source_addr = src_addr;
! 507: src->source_creation = pim_time_monotonic_sec();
! 508: src->source_flags = 0;
! 509: src->source_query_retransmit_count = 0;
! 510: src->source_channel_oil = 0;
! 511:
! 512: listnode_add(group->group_source_list, src);
! 513:
! 514: zassert(!src->t_source_timer); /* source timer == 0 */
! 515:
! 516: /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
! 517: igmp_anysource_forward_stop(group);
! 518:
! 519: return src;
! 520: }
! 521:
! 522: static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
! 523: struct igmp_group *group,
! 524: struct in_addr src_addr,
! 525: const char *ifname)
! 526: {
! 527: struct igmp_source *src;
! 528:
! 529: src = igmp_find_source_by_addr(group, src_addr);
! 530: if (src) {
! 531: return src;
! 532: }
! 533:
! 534: src = source_new(group, src_addr, ifname);
! 535: if (!src) {
! 536: return 0;
! 537: }
! 538:
! 539: return src;
! 540: }
! 541:
! 542: static void allow(struct igmp_sock *igmp, struct in_addr from,
! 543: struct in_addr group_addr,
! 544: int num_sources, struct in_addr *sources)
! 545: {
! 546: struct interface *ifp = igmp->interface;
! 547: struct igmp_group *group;
! 548: int i;
! 549:
! 550: /* non-existant group is created as INCLUDE {empty} */
! 551: group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
! 552: if (!group) {
! 553: return;
! 554: }
! 555:
! 556: /* scan received sources */
! 557: for (i = 0; i < num_sources; ++i) {
! 558: struct igmp_source *source;
! 559: struct in_addr *src_addr;
! 560:
! 561: src_addr = sources + i;
! 562:
! 563: source = add_source_by_addr(igmp, group, *src_addr, ifp->name);
! 564: if (!source) {
! 565: continue;
! 566: }
! 567:
! 568: /*
! 569: RFC 3376: 6.4.1. Reception of Current-State Records
! 570:
! 571: When receiving IS_IN reports for groups in EXCLUDE mode is
! 572: sources should be moved from set with (timers = 0) to set with
! 573: (timers > 0).
! 574:
! 575: igmp_source_reset_gmi() below, resetting the source timers to
! 576: GMI, accomplishes this.
! 577: */
! 578: igmp_source_reset_gmi(igmp, group, source);
! 579:
! 580: } /* scan received sources */
! 581: }
! 582:
! 583: void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
! 584: struct in_addr group_addr,
! 585: int num_sources, struct in_addr *sources)
! 586: {
! 587: on_trace(__PRETTY_FUNCTION__,
! 588: igmp->interface, from, group_addr, num_sources, sources);
! 589:
! 590: allow(igmp, from, group_addr, num_sources, sources);
! 591: }
! 592:
! 593: static void isex_excl(struct igmp_group *group,
! 594: int num_sources, struct in_addr *sources)
! 595: {
! 596: int i;
! 597:
! 598: /* EXCLUDE mode */
! 599: zassert(group->group_filtermode_isexcl);
! 600:
! 601: /* E.1: set deletion flag for known sources (X,Y) */
! 602: source_mark_delete_flag(group->group_source_list);
! 603:
! 604: /* scan received sources (A) */
! 605: for (i = 0; i < num_sources; ++i) {
! 606: struct igmp_source *source;
! 607: struct in_addr *src_addr;
! 608:
! 609: src_addr = sources + i;
! 610:
! 611: /* E.2: lookup reported source from (A) in (X,Y) */
! 612: source = igmp_find_source_by_addr(group, *src_addr);
! 613: if (source) {
! 614: /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
! 615: IGMP_SOURCE_DONT_DELETE(source->source_flags);
! 616: }
! 617: else {
! 618: /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
! 619: source = source_new(group, *src_addr,
! 620: group->group_igmp_sock->interface->name);
! 621: if (!source) {
! 622: /* ugh, internal malloc failure, skip source */
! 623: continue;
! 624: }
! 625: zassert(!source->t_source_timer); /* timer == 0 */
! 626: igmp_source_reset_gmi(group->group_igmp_sock, group, source);
! 627: zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
! 628: }
! 629:
! 630: } /* scan received sources */
! 631:
! 632: /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
! 633: source_delete_by_flag(group->group_source_list);
! 634: }
! 635:
! 636: static void isex_incl(struct igmp_group *group,
! 637: int num_sources, struct in_addr *sources)
! 638: {
! 639: int i;
! 640:
! 641: /* INCLUDE mode */
! 642: zassert(!group->group_filtermode_isexcl);
! 643:
! 644: /* I.1: set deletion flag for known sources (A) */
! 645: source_mark_delete_flag(group->group_source_list);
! 646:
! 647: /* scan received sources (B) */
! 648: for (i = 0; i < num_sources; ++i) {
! 649: struct igmp_source *source;
! 650: struct in_addr *src_addr;
! 651:
! 652: src_addr = sources + i;
! 653:
! 654: /* I.2: lookup reported source (B) */
! 655: source = igmp_find_source_by_addr(group, *src_addr);
! 656: if (source) {
! 657: /* I.3: if found, clear deletion flag (A*B) */
! 658: IGMP_SOURCE_DONT_DELETE(source->source_flags);
! 659: }
! 660: else {
! 661: /* I.4: if not found, create source with timer=0 (B-A) */
! 662: source = source_new(group, *src_addr,
! 663: group->group_igmp_sock->interface->name);
! 664: if (!source) {
! 665: /* ugh, internal malloc failure, skip source */
! 666: continue;
! 667: }
! 668: zassert(!source->t_source_timer); /* (B-A) timer=0 */
! 669: }
! 670:
! 671: } /* scan received sources */
! 672:
! 673: /* I.5: delete all sources marked with deletion flag (A-B) */
! 674: source_delete_by_flag(group->group_source_list);
! 675:
! 676: group->group_filtermode_isexcl = 1; /* boolean=true */
! 677:
! 678: zassert(group->group_filtermode_isexcl);
! 679:
! 680: group_exclude_fwd_anysrc_ifempty(group);
! 681: }
! 682:
! 683: void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
! 684: struct in_addr group_addr,
! 685: int num_sources, struct in_addr *sources)
! 686: {
! 687: struct interface *ifp = igmp->interface;
! 688: struct igmp_group *group;
! 689:
! 690: on_trace(__PRETTY_FUNCTION__,
! 691: ifp, from, group_addr, num_sources, sources);
! 692:
! 693: /* non-existant group is created as INCLUDE {empty} */
! 694: group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
! 695: if (!group) {
! 696: return;
! 697: }
! 698:
! 699: if (group->group_filtermode_isexcl) {
! 700: /* EXCLUDE mode */
! 701: isex_excl(group, num_sources, sources);
! 702: }
! 703: else {
! 704: /* INCLUDE mode */
! 705: isex_incl(group, num_sources, sources);
! 706: zassert(group->group_filtermode_isexcl);
! 707: }
! 708:
! 709: zassert(group->group_filtermode_isexcl);
! 710:
! 711: igmp_group_reset_gmi(group);
! 712: }
! 713:
! 714: static void toin_incl(struct igmp_group *group,
! 715: int num_sources, struct in_addr *sources)
! 716: {
! 717: struct igmp_sock *igmp = group->group_igmp_sock;
! 718: int num_sources_tosend = listcount(group->group_source_list);
! 719: int i;
! 720:
! 721: /* Set SEND flag for all known sources (A) */
! 722: source_mark_send_flag(group->group_source_list);
! 723:
! 724: /* Scan received sources (B) */
! 725: for (i = 0; i < num_sources; ++i) {
! 726: struct igmp_source *source;
! 727: struct in_addr *src_addr;
! 728:
! 729: src_addr = sources + i;
! 730:
! 731: /* Lookup reported source (B) */
! 732: source = igmp_find_source_by_addr(group, *src_addr);
! 733: if (source) {
! 734: /* If found, clear SEND flag (A*B) */
! 735: IGMP_SOURCE_DONT_SEND(source->source_flags);
! 736: --num_sources_tosend;
! 737: }
! 738: else {
! 739: /* If not found, create new source */
! 740: source = source_new(group, *src_addr,
! 741: group->group_igmp_sock->interface->name);
! 742: if (!source) {
! 743: /* ugh, internal malloc failure, skip source */
! 744: continue;
! 745: }
! 746: }
! 747:
! 748: /* (B)=GMI */
! 749: igmp_source_reset_gmi(igmp, group, source);
! 750: }
! 751:
! 752: /* Send sources marked with SEND flag: Q(G,A-B) */
! 753: if (num_sources_tosend > 0) {
! 754: source_query_send_by_flag(group, num_sources_tosend);
! 755: }
! 756: }
! 757:
! 758: static void toin_excl(struct igmp_group *group,
! 759: int num_sources, struct in_addr *sources)
! 760: {
! 761: struct igmp_sock *igmp = group->group_igmp_sock;
! 762: int num_sources_tosend;
! 763: int i;
! 764:
! 765: /* Set SEND flag for X (sources with timer > 0) */
! 766: num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
! 767:
! 768: /* Scan received sources (A) */
! 769: for (i = 0; i < num_sources; ++i) {
! 770: struct igmp_source *source;
! 771: struct in_addr *src_addr;
! 772:
! 773: src_addr = sources + i;
! 774:
! 775: /* Lookup reported source (A) */
! 776: source = igmp_find_source_by_addr(group, *src_addr);
! 777: if (source) {
! 778: if (source->t_source_timer) {
! 779: /* If found and timer running, clear SEND flag (X*A) */
! 780: IGMP_SOURCE_DONT_SEND(source->source_flags);
! 781: --num_sources_tosend;
! 782: }
! 783: }
! 784: else {
! 785: /* If not found, create new source */
! 786: source = source_new(group, *src_addr,
! 787: group->group_igmp_sock->interface->name);
! 788: if (!source) {
! 789: /* ugh, internal malloc failure, skip source */
! 790: continue;
! 791: }
! 792: }
! 793:
! 794: /* (A)=GMI */
! 795: igmp_source_reset_gmi(igmp, group, source);
! 796: }
! 797:
! 798: /* Send sources marked with SEND flag: Q(G,X-A) */
! 799: if (num_sources_tosend > 0) {
! 800: source_query_send_by_flag(group, num_sources_tosend);
! 801: }
! 802:
! 803: /* Send Q(G) */
! 804: group_query_send(group);
! 805: }
! 806:
! 807: void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
! 808: struct in_addr group_addr,
! 809: int num_sources, struct in_addr *sources)
! 810: {
! 811: struct interface *ifp = igmp->interface;
! 812: struct igmp_group *group;
! 813:
! 814: on_trace(__PRETTY_FUNCTION__,
! 815: ifp, from, group_addr, num_sources, sources);
! 816:
! 817: /* non-existant group is created as INCLUDE {empty} */
! 818: group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
! 819: if (!group) {
! 820: return;
! 821: }
! 822:
! 823: if (group->group_filtermode_isexcl) {
! 824: /* EXCLUDE mode */
! 825: toin_excl(group, num_sources, sources);
! 826: }
! 827: else {
! 828: /* INCLUDE mode */
! 829: toin_incl(group, num_sources, sources);
! 830: }
! 831: }
! 832:
! 833: static void toex_incl(struct igmp_group *group,
! 834: int num_sources, struct in_addr *sources)
! 835: {
! 836: int num_sources_tosend = 0;
! 837: int i;
! 838:
! 839: zassert(!group->group_filtermode_isexcl);
! 840:
! 841: /* Set DELETE flag for all known sources (A) */
! 842: source_mark_delete_flag(group->group_source_list);
! 843:
! 844: /* Clear off SEND flag from all known sources (A) */
! 845: source_clear_send_flag(group->group_source_list);
! 846:
! 847: /* Scan received sources (B) */
! 848: for (i = 0; i < num_sources; ++i) {
! 849: struct igmp_source *source;
! 850: struct in_addr *src_addr;
! 851:
! 852: src_addr = sources + i;
! 853:
! 854: /* Lookup reported source (B) */
! 855: source = igmp_find_source_by_addr(group, *src_addr);
! 856: if (source) {
! 857: /* If found, clear deletion flag: (A*B) */
! 858: IGMP_SOURCE_DONT_DELETE(source->source_flags);
! 859: /* and set SEND flag (A*B) */
! 860: IGMP_SOURCE_DO_SEND(source->source_flags);
! 861: ++num_sources_tosend;
! 862: }
! 863: else {
! 864: /* If source not found, create source with timer=0: (B-A)=0 */
! 865: source = source_new(group, *src_addr,
! 866: group->group_igmp_sock->interface->name);
! 867: if (!source) {
! 868: /* ugh, internal malloc failure, skip source */
! 869: continue;
! 870: }
! 871: zassert(!source->t_source_timer); /* (B-A) timer=0 */
! 872: }
! 873:
! 874: } /* Scan received sources (B) */
! 875:
! 876: group->group_filtermode_isexcl = 1; /* boolean=true */
! 877:
! 878: /* Delete all sources marked with DELETE flag (A-B) */
! 879: source_delete_by_flag(group->group_source_list);
! 880:
! 881: /* Send sources marked with SEND flag: Q(G,A*B) */
! 882: if (num_sources_tosend > 0) {
! 883: source_query_send_by_flag(group, num_sources_tosend);
! 884: }
! 885:
! 886: zassert(group->group_filtermode_isexcl);
! 887:
! 888: group_exclude_fwd_anysrc_ifempty(group);
! 889: }
! 890:
! 891: static void toex_excl(struct igmp_group *group,
! 892: int num_sources, struct in_addr *sources)
! 893: {
! 894: int num_sources_tosend = 0;
! 895: int i;
! 896:
! 897: /* set DELETE flag for all known sources (X,Y) */
! 898: source_mark_delete_flag(group->group_source_list);
! 899:
! 900: /* clear off SEND flag from all known sources (X,Y) */
! 901: source_clear_send_flag(group->group_source_list);
! 902:
! 903: /* scan received sources (A) */
! 904: for (i = 0; i < num_sources; ++i) {
! 905: struct igmp_source *source;
! 906: struct in_addr *src_addr;
! 907:
! 908: src_addr = sources + i;
! 909:
! 910: /* lookup reported source (A) in known sources (X,Y) */
! 911: source = igmp_find_source_by_addr(group, *src_addr);
! 912: if (source) {
! 913: /* if found, clear off DELETE flag from reported source (A) */
! 914: IGMP_SOURCE_DONT_DELETE(source->source_flags);
! 915: }
! 916: else {
! 917: /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
! 918: long group_timer_msec;
! 919: source = source_new(group, *src_addr,
! 920: group->group_igmp_sock->interface->name);
! 921: if (!source) {
! 922: /* ugh, internal malloc failure, skip source */
! 923: continue;
! 924: }
! 925:
! 926: zassert(!source->t_source_timer); /* timer == 0 */
! 927: group_timer_msec = igmp_group_timer_remain_msec(group);
! 928: igmp_source_timer_on(group, source, group_timer_msec);
! 929: zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
! 930:
! 931: /* make sure source is created with DELETE flag unset */
! 932: zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
! 933: }
! 934:
! 935: /* make sure reported source has DELETE flag unset */
! 936: zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
! 937:
! 938: if (source->t_source_timer) {
! 939: /* if source timer>0 mark SEND flag: Q(G,A-Y) */
! 940: IGMP_SOURCE_DO_SEND(source->source_flags);
! 941: ++num_sources_tosend;
! 942: }
! 943:
! 944: } /* scan received sources (A) */
! 945:
! 946: /*
! 947: delete all sources marked with DELETE flag:
! 948: Delete (X-A)
! 949: Delete (Y-A)
! 950: */
! 951: source_delete_by_flag(group->group_source_list);
! 952:
! 953: /* send sources marked with SEND flag: Q(G,A-Y) */
! 954: if (num_sources_tosend > 0) {
! 955: source_query_send_by_flag(group, num_sources_tosend);
! 956: }
! 957: }
! 958:
! 959: void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
! 960: struct in_addr group_addr,
! 961: int num_sources, struct in_addr *sources)
! 962: {
! 963: struct interface *ifp = igmp->interface;
! 964: struct igmp_group *group;
! 965:
! 966: on_trace(__PRETTY_FUNCTION__,
! 967: ifp, from, group_addr, num_sources, sources);
! 968:
! 969: /* non-existant group is created as INCLUDE {empty} */
! 970: group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
! 971: if (!group) {
! 972: return;
! 973: }
! 974:
! 975: if (group->group_filtermode_isexcl) {
! 976: /* EXCLUDE mode */
! 977: toex_excl(group, num_sources, sources);
! 978: }
! 979: else {
! 980: /* INCLUDE mode */
! 981: toex_incl(group, num_sources, sources);
! 982: zassert(group->group_filtermode_isexcl);
! 983: }
! 984: zassert(group->group_filtermode_isexcl);
! 985:
! 986: /* Group Timer=GMI */
! 987: igmp_group_reset_gmi(group);
! 988: }
! 989:
! 990: void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
! 991: struct in_addr group_addr,
! 992: int num_sources, struct in_addr *sources)
! 993: {
! 994: on_trace(__PRETTY_FUNCTION__,
! 995: igmp->interface, from, group_addr, num_sources, sources);
! 996:
! 997: allow(igmp, from, group_addr, num_sources, sources);
! 998: }
! 999:
! 1000: /*
! 1001: RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
! 1002:
! 1003: When transmitting a group specific query, if the group timer is
! 1004: larger than LMQT, the "Suppress Router-Side Processing" bit is set
! 1005: in the query message.
! 1006: */
! 1007: static void group_retransmit_group(struct igmp_group *group)
! 1008: {
! 1009: char query_buf[PIM_IGMP_BUFSIZE_WRITE];
! 1010: struct igmp_sock *igmp;
! 1011: struct pim_interface *pim_ifp;
! 1012: long lmqc; /* Last Member Query Count */
! 1013: long lmqi_msec; /* Last Member Query Interval */
! 1014: long lmqt_msec; /* Last Member Query Time */
! 1015: int s_flag;
! 1016:
! 1017: igmp = group->group_igmp_sock;
! 1018: pim_ifp = igmp->interface->info;
! 1019:
! 1020: lmqc = igmp->querier_robustness_variable;
! 1021: lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
! 1022: lmqt_msec = lmqc * lmqi_msec;
! 1023:
! 1024: /*
! 1025: RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
! 1026:
! 1027: When transmitting a group specific query, if the group timer is
! 1028: larger than LMQT, the "Suppress Router-Side Processing" bit is set
! 1029: in the query message.
! 1030: */
! 1031: s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
! 1032:
! 1033: if (PIM_DEBUG_IGMP_TRACE) {
! 1034: char group_str[100];
! 1035: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 1036: zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
! 1037: group_str, igmp->interface->name, s_flag,
! 1038: group->group_specific_query_retransmit_count);
! 1039: }
! 1040:
! 1041: /*
! 1042: RFC3376: 4.1.12. IP Destination Addresses for Queries
! 1043:
! 1044: Group-Specific and Group-and-Source-Specific Queries are sent with
! 1045: an IP destination address equal to the multicast address of
! 1046: interest.
! 1047: */
! 1048:
! 1049: pim_igmp_send_membership_query(group,
! 1050: igmp->fd,
! 1051: igmp->interface->name,
! 1052: query_buf,
! 1053: sizeof(query_buf),
! 1054: 0 /* num_sources_tosend */,
! 1055: group->group_addr /* dst_addr */,
! 1056: group->group_addr /* group_addr */,
! 1057: pim_ifp->igmp_specific_query_max_response_time_dsec,
! 1058: s_flag,
! 1059: igmp->querier_robustness_variable,
! 1060: igmp->querier_query_interval);
! 1061: }
! 1062:
! 1063: /*
! 1064: RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
! 1065:
! 1066: When building a group and source specific query for a group G, two
! 1067: separate query messages are sent for the group. The first one has
! 1068: the "Suppress Router-Side Processing" bit set and contains all the
! 1069: sources with retransmission state and timers greater than LMQT. The
! 1070: second has the "Suppress Router-Side Processing" bit clear and
! 1071: contains all the sources with retransmission state and timers lower
! 1072: or equal to LMQT. If either of the two calculated messages does not
! 1073: contain any sources, then its transmission is suppressed.
! 1074: */
! 1075: static int group_retransmit_sources(struct igmp_group *group,
! 1076: int send_with_sflag_set)
! 1077: {
! 1078: struct igmp_sock *igmp;
! 1079: struct pim_interface *pim_ifp;
! 1080: long lmqc; /* Last Member Query Count */
! 1081: long lmqi_msec; /* Last Member Query Interval */
! 1082: long lmqt_msec; /* Last Member Query Time */
! 1083: char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
! 1084: char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
! 1085: int query_buf1_max_sources;
! 1086: int query_buf2_max_sources;
! 1087: struct in_addr *source_addr1;
! 1088: struct in_addr *source_addr2;
! 1089: int num_sources_tosend1;
! 1090: int num_sources_tosend2;
! 1091: struct listnode *src_node;
! 1092: struct igmp_source *src;
! 1093: int num_retransmit_sources_left = 0;
! 1094:
! 1095: query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
! 1096: query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
! 1097:
! 1098: source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
! 1099: source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
! 1100:
! 1101: igmp = group->group_igmp_sock;
! 1102: pim_ifp = igmp->interface->info;
! 1103:
! 1104: lmqc = igmp->querier_robustness_variable;
! 1105: lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
! 1106: lmqt_msec = lmqc * lmqi_msec;
! 1107:
! 1108: /* Scan all group sources */
! 1109: for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
! 1110:
! 1111: /* Source has retransmission state? */
! 1112: if (src->source_query_retransmit_count < 1)
! 1113: continue;
! 1114:
! 1115: if (--src->source_query_retransmit_count > 0) {
! 1116: ++num_retransmit_sources_left;
! 1117: }
! 1118:
! 1119: /* Copy source address into appropriate query buffer */
! 1120: if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
! 1121: *source_addr1 = src->source_addr;
! 1122: ++source_addr1;
! 1123: }
! 1124: else {
! 1125: *source_addr2 = src->source_addr;
! 1126: ++source_addr2;
! 1127: }
! 1128:
! 1129: }
! 1130:
! 1131: num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
! 1132: num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
! 1133:
! 1134: if (PIM_DEBUG_IGMP_TRACE) {
! 1135: char group_str[100];
! 1136: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 1137: zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
! 1138: group_str, igmp->interface->name,
! 1139: num_sources_tosend1,
! 1140: num_sources_tosend2,
! 1141: send_with_sflag_set,
! 1142: num_retransmit_sources_left);
! 1143: }
! 1144:
! 1145: if (num_sources_tosend1 > 0) {
! 1146: /*
! 1147: Send group-and-source-specific query with s_flag set and all
! 1148: sources with timers greater than LMQT.
! 1149: */
! 1150:
! 1151: if (send_with_sflag_set) {
! 1152:
! 1153: query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
! 1154: if (num_sources_tosend1 > query_buf1_max_sources) {
! 1155: char group_str[100];
! 1156: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 1157: zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
! 1158: __PRETTY_FUNCTION__, group_str, igmp->interface->name,
! 1159: num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
! 1160: }
! 1161: else {
! 1162: /*
! 1163: RFC3376: 4.1.12. IP Destination Addresses for Queries
! 1164:
! 1165: Group-Specific and Group-and-Source-Specific Queries are sent with
! 1166: an IP destination address equal to the multicast address of
! 1167: interest.
! 1168: */
! 1169:
! 1170: pim_igmp_send_membership_query(group,
! 1171: igmp->fd,
! 1172: igmp->interface->name,
! 1173: query_buf1,
! 1174: sizeof(query_buf1),
! 1175: num_sources_tosend1,
! 1176: group->group_addr,
! 1177: group->group_addr,
! 1178: pim_ifp->igmp_specific_query_max_response_time_dsec,
! 1179: 1 /* s_flag */,
! 1180: igmp->querier_robustness_variable,
! 1181: igmp->querier_query_interval);
! 1182:
! 1183: }
! 1184:
! 1185: } /* send_with_sflag_set */
! 1186:
! 1187: }
! 1188:
! 1189: if (num_sources_tosend2 > 0) {
! 1190: /*
! 1191: Send group-and-source-specific query with s_flag clear and all
! 1192: sources with timers lower or equal to LMQT.
! 1193: */
! 1194:
! 1195: query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
! 1196: if (num_sources_tosend2 > query_buf2_max_sources) {
! 1197: char group_str[100];
! 1198: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 1199: zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
! 1200: __PRETTY_FUNCTION__, group_str, igmp->interface->name,
! 1201: num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
! 1202: }
! 1203: else {
! 1204: /*
! 1205: RFC3376: 4.1.12. IP Destination Addresses for Queries
! 1206:
! 1207: Group-Specific and Group-and-Source-Specific Queries are sent with
! 1208: an IP destination address equal to the multicast address of
! 1209: interest.
! 1210: */
! 1211:
! 1212: pim_igmp_send_membership_query(group,
! 1213: igmp->fd,
! 1214: igmp->interface->name,
! 1215: query_buf2,
! 1216: sizeof(query_buf2),
! 1217: num_sources_tosend2,
! 1218: group->group_addr,
! 1219: group->group_addr,
! 1220: pim_ifp->igmp_specific_query_max_response_time_dsec,
! 1221: 0 /* s_flag */,
! 1222: igmp->querier_robustness_variable,
! 1223: igmp->querier_query_interval);
! 1224:
! 1225: }
! 1226: }
! 1227:
! 1228: return num_retransmit_sources_left;
! 1229: }
! 1230:
! 1231: static int igmp_group_retransmit(struct thread *t)
! 1232: {
! 1233: struct igmp_group *group;
! 1234: int num_retransmit_sources_left;
! 1235: int send_with_sflag_set; /* boolean */
! 1236:
! 1237: zassert(t);
! 1238: group = THREAD_ARG(t);
! 1239: zassert(group);
! 1240:
! 1241: if (PIM_DEBUG_IGMP_TRACE) {
! 1242: char group_str[100];
! 1243: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 1244: zlog_debug("group_retransmit_timer: group %s on %s",
! 1245: group_str, group->group_igmp_sock->interface->name);
! 1246: }
! 1247:
! 1248: /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
! 1249: if (group->group_specific_query_retransmit_count > 0) {
! 1250:
! 1251: /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
! 1252: group_retransmit_group(group);
! 1253: --group->group_specific_query_retransmit_count;
! 1254:
! 1255: /*
! 1256: RFC3376: 6.6.3.2
! 1257: If a group specific query is scheduled to be transmitted at the
! 1258: same time as a group and source specific query for the same group,
! 1259: then transmission of the group and source specific message with the
! 1260: "Suppress Router-Side Processing" bit set may be suppressed.
! 1261: */
! 1262: send_with_sflag_set = 0; /* boolean=false */
! 1263: }
! 1264: else {
! 1265: send_with_sflag_set = 1; /* boolean=true */
! 1266: }
! 1267:
! 1268: /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
! 1269: num_retransmit_sources_left = group_retransmit_sources(group,
! 1270: send_with_sflag_set);
! 1271:
! 1272: group->t_group_query_retransmit_timer = 0;
! 1273:
! 1274: /*
! 1275: Keep group retransmit timer running if there is any retransmit
! 1276: counter pending
! 1277: */
! 1278: if ((num_retransmit_sources_left > 0) ||
! 1279: (group->group_specific_query_retransmit_count > 0)) {
! 1280: group_retransmit_timer_on(group);
! 1281: }
! 1282:
! 1283: return 0;
! 1284: }
! 1285:
! 1286: /*
! 1287: group_retransmit_timer_on:
! 1288: if group retransmit timer isn't running, starts it;
! 1289: otherwise, do nothing
! 1290: */
! 1291: static void group_retransmit_timer_on(struct igmp_group *group)
! 1292: {
! 1293: struct igmp_sock *igmp;
! 1294: struct pim_interface *pim_ifp;
! 1295: long lmqi_msec; /* Last Member Query Interval */
! 1296:
! 1297: /* if group retransmit timer is running, do nothing */
! 1298: if (group->t_group_query_retransmit_timer) {
! 1299: return;
! 1300: }
! 1301:
! 1302: igmp = group->group_igmp_sock;
! 1303: pim_ifp = igmp->interface->info;
! 1304:
! 1305: lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
! 1306:
! 1307: if (PIM_DEBUG_IGMP_TRACE) {
! 1308: char group_str[100];
! 1309: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 1310: zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
! 1311: lmqi_msec / 1000,
! 1312: lmqi_msec % 1000,
! 1313: group_str,
! 1314: igmp->interface->name);
! 1315: }
! 1316:
! 1317: THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
! 1318: igmp_group_retransmit,
! 1319: group, lmqi_msec);
! 1320: }
! 1321:
! 1322: static long igmp_group_timer_remain_msec(struct igmp_group *group)
! 1323: {
! 1324: return pim_time_timer_remain_msec(group->t_group_timer);
! 1325: }
! 1326:
! 1327: static long igmp_source_timer_remain_msec(struct igmp_source *source)
! 1328: {
! 1329: return pim_time_timer_remain_msec(source->t_source_timer);
! 1330: }
! 1331:
! 1332: /*
! 1333: RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
! 1334: */
! 1335: static void group_query_send(struct igmp_group *group)
! 1336: {
! 1337: long lmqc; /* Last Member Query Count */
! 1338:
! 1339: lmqc = group->group_igmp_sock->querier_robustness_variable;
! 1340:
! 1341: /* lower group timer to lmqt */
! 1342: igmp_group_timer_lower_to_lmqt(group);
! 1343:
! 1344: /* reset retransmission counter */
! 1345: group->group_specific_query_retransmit_count = lmqc;
! 1346:
! 1347: /* immediately send group specific query (decrease retransmit counter by 1)*/
! 1348: group_retransmit_group(group);
! 1349:
! 1350: /* make sure group retransmit timer is running */
! 1351: group_retransmit_timer_on(group);
! 1352: }
! 1353:
! 1354: /*
! 1355: RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
! 1356: */
! 1357: static void source_query_send_by_flag(struct igmp_group *group,
! 1358: int num_sources_tosend)
! 1359: {
! 1360: struct igmp_sock *igmp;
! 1361: struct pim_interface *pim_ifp;
! 1362: struct listnode *src_node;
! 1363: struct igmp_source *src;
! 1364: long lmqc; /* Last Member Query Count */
! 1365: long lmqi_msec; /* Last Member Query Interval */
! 1366: long lmqt_msec; /* Last Member Query Time */
! 1367:
! 1368: zassert(num_sources_tosend > 0);
! 1369:
! 1370: igmp = group->group_igmp_sock;
! 1371: pim_ifp = igmp->interface->info;
! 1372:
! 1373: lmqc = igmp->querier_robustness_variable;
! 1374: lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
! 1375: lmqt_msec = lmqc * lmqi_msec;
! 1376:
! 1377: /*
! 1378: RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
! 1379:
! 1380: (...) for each of the sources in X of group G, with source timer larger
! 1381: than LMQT:
! 1382: o Set number of retransmissions for each source to [Last Member
! 1383: Query Count].
! 1384: o Lower source timer to LMQT.
! 1385: */
! 1386: for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
! 1387: if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
! 1388: /* source "src" in X of group G */
! 1389: if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
! 1390: src->source_query_retransmit_count = lmqc;
! 1391: igmp_source_timer_lower_to_lmqt(src);
! 1392: }
! 1393: }
! 1394: }
! 1395:
! 1396: /* send group-and-source specific queries */
! 1397: group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
! 1398:
! 1399: /* make sure group retransmit timer is running */
! 1400: group_retransmit_timer_on(group);
! 1401: }
! 1402:
! 1403: static void block_excl(struct igmp_group *group,
! 1404: int num_sources, struct in_addr *sources)
! 1405: {
! 1406: int num_sources_tosend = 0;
! 1407: int i;
! 1408:
! 1409: /* 1. clear off SEND flag from all known sources (X,Y) */
! 1410: source_clear_send_flag(group->group_source_list);
! 1411:
! 1412: /* 2. scan received sources (A) */
! 1413: for (i = 0; i < num_sources; ++i) {
! 1414: struct igmp_source *source;
! 1415: struct in_addr *src_addr;
! 1416:
! 1417: src_addr = sources + i;
! 1418:
! 1419: /* lookup reported source (A) in known sources (X,Y) */
! 1420: source = igmp_find_source_by_addr(group, *src_addr);
! 1421: if (!source) {
! 1422: /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
! 1423: long group_timer_msec;
! 1424: source = source_new(group, *src_addr,
! 1425: group->group_igmp_sock->interface->name);
! 1426: if (!source) {
! 1427: /* ugh, internal malloc failure, skip source */
! 1428: continue;
! 1429: }
! 1430:
! 1431: zassert(!source->t_source_timer); /* timer == 0 */
! 1432: group_timer_msec = igmp_group_timer_remain_msec(group);
! 1433: igmp_source_timer_on(group, source, group_timer_msec);
! 1434: zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
! 1435: }
! 1436:
! 1437: if (source->t_source_timer) {
! 1438: /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
! 1439: IGMP_SOURCE_DO_SEND(source->source_flags);
! 1440: ++num_sources_tosend;
! 1441: }
! 1442: }
! 1443:
! 1444: /* 5. send sources marked with SEND flag: Q(G,A-Y) */
! 1445: if (num_sources_tosend > 0) {
! 1446: source_query_send_by_flag(group, num_sources_tosend);
! 1447: }
! 1448: }
! 1449:
! 1450: static void block_incl(struct igmp_group *group,
! 1451: int num_sources, struct in_addr *sources)
! 1452: {
! 1453: int num_sources_tosend = 0;
! 1454: int i;
! 1455:
! 1456: /* 1. clear off SEND flag from all known sources (B) */
! 1457: source_clear_send_flag(group->group_source_list);
! 1458:
! 1459: /* 2. scan received sources (A) */
! 1460: for (i = 0; i < num_sources; ++i) {
! 1461: struct igmp_source *source;
! 1462: struct in_addr *src_addr;
! 1463:
! 1464: src_addr = sources + i;
! 1465:
! 1466: /* lookup reported source (A) in known sources (B) */
! 1467: source = igmp_find_source_by_addr(group, *src_addr);
! 1468: if (source) {
! 1469: /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
! 1470: IGMP_SOURCE_DO_SEND(source->source_flags);
! 1471: ++num_sources_tosend;
! 1472: }
! 1473: }
! 1474:
! 1475: /* 4. send sources marked with SEND flag: Q(G,A*B) */
! 1476: if (num_sources_tosend > 0) {
! 1477: source_query_send_by_flag(group, num_sources_tosend);
! 1478: }
! 1479: }
! 1480:
! 1481: void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
! 1482: struct in_addr group_addr,
! 1483: int num_sources, struct in_addr *sources)
! 1484: {
! 1485: struct interface *ifp = igmp->interface;
! 1486: struct igmp_group *group;
! 1487:
! 1488: on_trace(__PRETTY_FUNCTION__,
! 1489: ifp, from, group_addr, num_sources, sources);
! 1490:
! 1491: /* non-existant group is created as INCLUDE {empty} */
! 1492: group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
! 1493: if (!group) {
! 1494: return;
! 1495: }
! 1496:
! 1497: if (group->group_filtermode_isexcl) {
! 1498: /* EXCLUDE mode */
! 1499: block_excl(group, num_sources, sources);
! 1500: }
! 1501: else {
! 1502: /* INCLUDE mode */
! 1503: block_incl(group, num_sources, sources);
! 1504: }
! 1505: }
! 1506:
! 1507: void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
! 1508: {
! 1509: struct igmp_sock *igmp;
! 1510: struct interface *ifp;
! 1511: struct pim_interface *pim_ifp;
! 1512: char *ifname;
! 1513: int lmqi_dsec; /* Last Member Query Interval */
! 1514: int lmqc; /* Last Member Query Count */
! 1515: int lmqt_msec; /* Last Member Query Time */
! 1516:
! 1517: /*
! 1518: RFC 3376: 6.2.2. Definition of Group Timers
! 1519:
! 1520: The group timer is only used when a group is in EXCLUDE mode and
! 1521: it represents the time for the *filter-mode* of the group to
! 1522: expire and switch to INCLUDE mode.
! 1523: */
! 1524: if (!group->group_filtermode_isexcl) {
! 1525: return;
! 1526: }
! 1527:
! 1528: igmp = group->group_igmp_sock;
! 1529: ifp = igmp->interface;
! 1530: pim_ifp = ifp->info;
! 1531: ifname = ifp->name;
! 1532:
! 1533: lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
! 1534: lmqc = igmp->querier_robustness_variable;
! 1535: lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
! 1536:
! 1537: if (PIM_DEBUG_IGMP_TRACE) {
! 1538: char group_str[100];
! 1539: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 1540: zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
! 1541: __PRETTY_FUNCTION__,
! 1542: group_str, ifname,
! 1543: lmqc, lmqi_dsec, lmqt_msec);
! 1544: }
! 1545:
! 1546: zassert(group->group_filtermode_isexcl);
! 1547:
! 1548: igmp_group_timer_on(group, lmqt_msec, ifname);
! 1549: }
! 1550:
! 1551: void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
! 1552: {
! 1553: struct igmp_group *group;
! 1554: struct igmp_sock *igmp;
! 1555: struct interface *ifp;
! 1556: struct pim_interface *pim_ifp;
! 1557: char *ifname;
! 1558: int lmqi_dsec; /* Last Member Query Interval */
! 1559: int lmqc; /* Last Member Query Count */
! 1560: int lmqt_msec; /* Last Member Query Time */
! 1561:
! 1562: group = source->source_group;
! 1563: igmp = group->group_igmp_sock;
! 1564: ifp = igmp->interface;
! 1565: pim_ifp = ifp->info;
! 1566: ifname = ifp->name;
! 1567:
! 1568: lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
! 1569: lmqc = igmp->querier_robustness_variable;
! 1570: lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
! 1571:
! 1572: if (PIM_DEBUG_IGMP_TRACE) {
! 1573: char group_str[100];
! 1574: char source_str[100];
! 1575: pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
! 1576: pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
! 1577: zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
! 1578: __PRETTY_FUNCTION__,
! 1579: group_str, source_str, ifname,
! 1580: lmqc, lmqi_dsec, lmqt_msec);
! 1581: }
! 1582:
! 1583: igmp_source_timer_on(group, source, lmqt_msec);
! 1584: }
! 1585:
! 1586: /*
! 1587: Copy sources to message:
! 1588:
! 1589: struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
! 1590: if (num_sources > 0) {
! 1591: struct listnode *node;
! 1592: struct igmp_source *src;
! 1593: int i = 0;
! 1594:
! 1595: for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
! 1596: sources[i++] = src->source_addr;
! 1597: }
! 1598: }
! 1599: */
! 1600: void pim_igmp_send_membership_query(struct igmp_group *group,
! 1601: int fd,
! 1602: const char *ifname,
! 1603: char *query_buf,
! 1604: int query_buf_size,
! 1605: int num_sources,
! 1606: struct in_addr dst_addr,
! 1607: struct in_addr group_addr,
! 1608: int query_max_response_time_dsec,
! 1609: uint8_t s_flag,
! 1610: uint8_t querier_robustness_variable,
! 1611: uint16_t querier_query_interval)
! 1612: {
! 1613: ssize_t msg_size;
! 1614: uint8_t max_resp_code;
! 1615: uint8_t qqic;
! 1616: ssize_t sent;
! 1617: struct sockaddr_in to;
! 1618: socklen_t tolen;
! 1619: uint16_t checksum;
! 1620:
! 1621: zassert(num_sources >= 0);
! 1622:
! 1623: msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
! 1624: if (msg_size > query_buf_size) {
! 1625: zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
! 1626: __FILE__, __PRETTY_FUNCTION__,
! 1627: msg_size, query_buf_size);
! 1628: return;
! 1629: }
! 1630:
! 1631: s_flag = PIM_FORCE_BOOLEAN(s_flag);
! 1632: zassert((s_flag == 0) || (s_flag == 1));
! 1633:
! 1634: max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
! 1635: qqic = igmp_msg_encode16to8(querier_query_interval);
! 1636:
! 1637: /*
! 1638: RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
! 1639:
! 1640: If non-zero, the QRV field contains the [Robustness Variable]
! 1641: value used by the querier, i.e., the sender of the Query. If the
! 1642: querier's [Robustness Variable] exceeds 7, the maximum value of
! 1643: the QRV field, the QRV is set to zero.
! 1644: */
! 1645: if (querier_robustness_variable > 7) {
! 1646: querier_robustness_variable = 0;
! 1647: }
! 1648:
! 1649: query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
! 1650: query_buf[1] = max_resp_code;
! 1651: *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
! 1652: memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
! 1653:
! 1654: query_buf[8] = (s_flag << 3) | querier_robustness_variable;
! 1655: query_buf[9] = qqic;
! 1656: *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
! 1657:
! 1658: checksum = in_cksum(query_buf, msg_size);
! 1659: *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
! 1660:
! 1661: if (PIM_DEBUG_IGMP_PACKETS) {
! 1662: char dst_str[100];
! 1663: char group_str[100];
! 1664: pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
! 1665: pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
! 1666: zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
! 1667: __PRETTY_FUNCTION__,
! 1668: dst_str, ifname, group_str, num_sources,
! 1669: msg_size, s_flag, querier_robustness_variable,
! 1670: querier_query_interval, qqic, checksum);
! 1671: }
! 1672:
! 1673: #if 0
! 1674: memset(&to, 0, sizeof(to));
! 1675: #endif
! 1676: to.sin_family = AF_INET;
! 1677: to.sin_addr = dst_addr;
! 1678: #if 0
! 1679: to.sin_port = htons(0);
! 1680: #endif
! 1681: tolen = sizeof(to);
! 1682:
! 1683: sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
! 1684: (struct sockaddr *)&to, tolen);
! 1685: if (sent != (ssize_t) msg_size) {
! 1686: int e = errno;
! 1687: char dst_str[100];
! 1688: char group_str[100];
! 1689: pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
! 1690: pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
! 1691: if (sent < 0) {
! 1692: zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
! 1693: __PRETTY_FUNCTION__,
! 1694: dst_str, ifname, group_str, msg_size,
! 1695: e, safe_strerror(e));
! 1696: }
! 1697: else {
! 1698: zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
! 1699: __PRETTY_FUNCTION__,
! 1700: dst_str, ifname, group_str,
! 1701: msg_size, sent);
! 1702: }
! 1703: return;
! 1704: }
! 1705:
! 1706: /*
! 1707: s_flag sanity test: s_flag must be set for general queries
! 1708:
! 1709: RFC 3376: 6.6.1. Timer Updates
! 1710:
! 1711: When a router sends or receives a query with a clear Suppress
! 1712: Router-Side Processing flag, it must update its timers to reflect
! 1713: the correct timeout values for the group or sources being queried.
! 1714:
! 1715: General queries don't trigger timer update.
! 1716: */
! 1717: if (!s_flag) {
! 1718: /* general query? */
! 1719: if (PIM_INADDR_IS_ANY(group_addr)) {
! 1720: char dst_str[100];
! 1721: char group_str[100];
! 1722: pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
! 1723: pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
! 1724: zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
! 1725: __PRETTY_FUNCTION__,
! 1726: dst_str, ifname, group_str, num_sources);
! 1727: }
! 1728: }
! 1729:
! 1730: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>