3 * Copyright (C) 2008 Everton da Silva Marques
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.
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.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 #include "lib_errors.h"
27 #include "pim_iface.h"
29 #include "pim_igmpv3.h"
33 #include "pim_zebra.h"
36 static void group_retransmit_timer_on(struct gm_group
*group
);
37 static long igmp_group_timer_remain_msec(struct gm_group
*group
);
38 static long igmp_source_timer_remain_msec(struct gm_source
*source
);
39 static void group_query_send(struct gm_group
*group
);
40 static void source_query_send_by_flag(struct gm_group
*group
,
41 int num_sources_tosend
);
43 static void on_trace(const char *label
, struct interface
*ifp
,
44 struct in_addr from
, struct in_addr group_addr
,
45 int num_sources
, struct in_addr
*sources
)
47 if (PIM_DEBUG_IGMP_TRACE
) {
48 char from_str
[INET_ADDRSTRLEN
];
49 char group_str
[INET_ADDRSTRLEN
];
51 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
52 pim_inet4_dump("<group?>", group_addr
, group_str
,
55 zlog_debug("%s: from %s on %s: group=%s sources=%d", label
,
56 from_str
, ifp
->name
, group_str
, num_sources
);
60 static inline long igmp_gmi_msec(struct gm_group
*group
)
62 struct pim_interface
*pim_ifp
= group
->interface
->info
;
64 struct listnode
*sock_node
;
66 long qrv
= 0, qqi
= 0;
68 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
69 qrv
= MAX(qrv
, igmp
->querier_robustness_variable
);
70 qqi
= MAX(qqi
, igmp
->querier_query_interval
);
72 return PIM_IGMP_GMI_MSEC(qrv
, qqi
,
73 pim_ifp
->gm_query_max_response_time_dsec
);
76 void igmp_group_reset_gmi(struct gm_group
*group
)
78 long group_membership_interval_msec
;
79 struct interface
*ifp
;
81 ifp
= group
->interface
;
84 RFC 3376: 8.4. Group Membership Interval
86 The Group Membership Interval is the amount of time that must pass
87 before a multicast router decides there are no more members of a
88 group or a particular source on a network.
90 This value MUST be ((the Robustness Variable) times (the Query
91 Interval)) plus (one Query Response Interval).
93 group_membership_interval_msec = querier_robustness_variable *
94 (1000 * querier_query_interval) +
95 100 * query_response_interval_dsec;
97 group_membership_interval_msec
= igmp_gmi_msec(group
);
99 if (PIM_DEBUG_IGMP_TRACE
) {
100 char group_str
[INET_ADDRSTRLEN
];
101 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
104 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
105 group_str
, group_membership_interval_msec
/ 1000,
106 group_membership_interval_msec
% 1000, ifp
->name
);
110 RFC 3376: 6.2.2. Definition of Group Timers
112 The group timer is only used when a group is in EXCLUDE mode and
113 it represents the time for the *filter-mode* of the group to
114 expire and switch to INCLUDE mode.
116 assert(group
->group_filtermode_isexcl
);
118 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
121 static int igmp_source_timer(struct thread
*t
)
123 struct gm_source
*source
;
124 struct gm_group
*group
;
126 source
= THREAD_ARG(t
);
128 group
= source
->source_group
;
130 if (PIM_DEBUG_IGMP_TRACE
) {
131 char group_str
[INET_ADDRSTRLEN
];
132 char source_str
[INET_ADDRSTRLEN
];
133 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
135 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
138 "%s: Source timer expired for group %s source %s on %s",
139 __func__
, group_str
, source_str
,
140 group
->interface
->name
);
144 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
147 Filter-Mode Source Timer Value Action
148 ----------- ------------------ ------
149 INCLUDE TIMER == 0 Suggest to stop forwarding
150 traffic from source and
151 remove source record. If
152 there are no more source
153 records for the group, delete
156 EXCLUDE TIMER == 0 Suggest to not forward
158 (DO NOT remove record)
160 Source timer switched from (T > 0) to (T == 0): disable forwarding.
163 if (group
->group_filtermode_isexcl
) {
166 igmp_source_forward_stop(source
);
170 /* igmp_source_delete() will stop forwarding source */
171 igmp_source_delete(source
);
174 If there are no more source records for the group, delete
178 if (!listcount(group
->group_source_list
)) {
179 igmp_group_delete_empty_include(group
);
186 static void source_timer_off(struct gm_group
*group
, struct gm_source
*source
)
188 if (!source
->t_source_timer
)
191 if (PIM_DEBUG_IGMP_TRACE
) {
192 char group_str
[INET_ADDRSTRLEN
];
193 char source_str
[INET_ADDRSTRLEN
];
194 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
196 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
199 "Cancelling TIMER event for group %s source %s on %s",
200 group_str
, source_str
, group
->interface
->name
);
203 THREAD_OFF(source
->t_source_timer
);
206 static void igmp_source_timer_on(struct gm_group
*group
,
207 struct gm_source
*source
, long interval_msec
)
209 source_timer_off(group
, source
);
210 struct pim_interface
*pim_ifp
= group
->interface
->info
;
212 if (PIM_DEBUG_IGMP_EVENTS
) {
213 char group_str
[INET_ADDRSTRLEN
];
214 char source_str
[INET_ADDRSTRLEN
];
215 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
217 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
220 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
221 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
222 source_str
, group
->interface
->name
);
225 thread_add_timer_msec(router
->master
, igmp_source_timer
, source
,
226 interval_msec
, &source
->t_source_timer
);
229 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
231 Source timer switched from (T == 0) to (T > 0): enable forwarding.
233 igmp_source_forward_start(pim_ifp
->pim
, source
);
236 void igmp_source_reset_gmi(struct gm_group
*group
, struct gm_source
*source
)
238 long group_membership_interval_msec
;
239 struct interface
*ifp
;
241 ifp
= group
->interface
;
243 group_membership_interval_msec
= igmp_gmi_msec(group
);
245 if (PIM_DEBUG_IGMP_TRACE
) {
246 char group_str
[INET_ADDRSTRLEN
];
247 char source_str
[INET_ADDRSTRLEN
];
249 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
251 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
255 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
256 source_str
, group_membership_interval_msec
/ 1000,
257 group_membership_interval_msec
% 1000, group_str
,
261 igmp_source_timer_on(group
, source
, group_membership_interval_msec
);
264 static void source_mark_delete_flag(struct gm_group
*group
)
266 struct listnode
*src_node
;
267 struct gm_source
*src
;
269 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
270 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
274 static void source_mark_send_flag(struct gm_group
*group
)
276 struct listnode
*src_node
;
277 struct gm_source
*src
;
279 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
280 IGMP_SOURCE_DO_SEND(src
->source_flags
);
284 static int source_mark_send_flag_by_timer(struct gm_group
*group
)
286 struct listnode
*src_node
;
287 struct gm_source
*src
;
288 int num_marked_sources
= 0;
290 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
291 /* Is source timer running? */
292 if (src
->t_source_timer
) {
293 IGMP_SOURCE_DO_SEND(src
->source_flags
);
294 ++num_marked_sources
;
296 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
300 return num_marked_sources
;
303 static void source_clear_send_flag(struct list
*source_list
)
305 struct listnode
*src_node
;
306 struct gm_source
*src
;
308 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
309 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
314 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
316 static void group_exclude_fwd_anysrc_ifempty(struct gm_group
*group
)
318 struct pim_interface
*pim_ifp
= group
->interface
->info
;
320 assert(group
->group_filtermode_isexcl
);
322 if (listcount(group
->group_source_list
) < 1) {
323 igmp_anysource_forward_start(pim_ifp
->pim
, group
);
327 void igmp_source_free(struct gm_source
*source
)
329 /* make sure there is no source timer running */
330 assert(!source
->t_source_timer
);
332 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
335 static void source_channel_oil_detach(struct gm_source
*source
)
337 if (source
->source_channel_oil
) {
338 pim_channel_oil_del(source
->source_channel_oil
, __func__
);
339 source
->source_channel_oil
= NULL
;
344 igmp_source_delete: stop fowarding, and delete the source
345 igmp_source_forward_stop: stop fowarding, but keep the source
347 void igmp_source_delete(struct gm_source
*source
)
349 struct gm_group
*group
;
352 group
= source
->source_group
;
354 if (PIM_DEBUG_IGMP_TRACE
) {
355 char group_str
[INET_ADDRSTRLEN
];
356 char source_str
[INET_ADDRSTRLEN
];
357 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
359 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
362 "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d",
363 source_str
, group_str
, group
->interface
->name
,
364 source
->source_channel_oil
365 ? source
->source_channel_oil
->oil_ref_count
369 source_timer_off(group
, source
);
370 igmp_source_forward_stop(source
);
372 /* sanity check that forwarding has been disabled */
373 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
374 char group_str
[INET_ADDRSTRLEN
];
375 char source_str
[INET_ADDRSTRLEN
];
376 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
378 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
381 "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s",
382 __func__
, source_str
, group_str
,
383 group
->interface
->name
);
387 source_channel_oil_detach(source
);
390 notice that listnode_delete() can't be moved
391 into igmp_source_free() because the later is
392 called by list_delete_all_node()
394 listnode_delete(group
->group_source_list
, source
);
396 src
.s_addr
= source
->source_addr
.s_addr
;
397 igmp_source_free(source
);
399 /* Group source list is empty and current source is * then
400 *,G group going away so do not trigger start */
401 if (group
->group_filtermode_isexcl
402 && (listcount(group
->group_source_list
) != 0)
403 && src
.s_addr
!= INADDR_ANY
) {
404 group_exclude_fwd_anysrc_ifempty(group
);
408 static void source_delete_by_flag(struct list
*source_list
)
410 struct listnode
*src_node
;
411 struct listnode
*src_nextnode
;
412 struct gm_source
*src
;
414 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
415 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
416 igmp_source_delete(src
);
419 void igmp_source_delete_expired(struct list
*source_list
)
421 struct listnode
*src_node
;
422 struct listnode
*src_nextnode
;
423 struct gm_source
*src
;
425 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
426 if (!src
->t_source_timer
)
427 igmp_source_delete(src
);
430 struct gm_source
*igmp_find_source_by_addr(struct gm_group
*group
,
431 struct in_addr src_addr
)
433 struct listnode
*src_node
;
434 struct gm_source
*src
;
436 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
437 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
443 struct gm_source
*igmp_get_source_by_addr(struct gm_group
*group
,
444 struct in_addr src_addr
, bool *new)
446 struct gm_source
*src
;
451 src
= igmp_find_source_by_addr(group
, src_addr
);
455 if (PIM_DEBUG_IGMP_TRACE
) {
456 char group_str
[INET_ADDRSTRLEN
];
457 char source_str
[INET_ADDRSTRLEN
];
458 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
460 pim_inet4_dump("<source?>", src_addr
, source_str
,
463 "Creating new IGMP source %s for group %s on interface %s",
464 source_str
, group_str
, group
->interface
->name
);
467 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
469 src
->t_source_timer
= NULL
;
470 src
->source_group
= group
; /* back pointer */
471 src
->source_addr
= src_addr
;
472 src
->source_creation
= pim_time_monotonic_sec();
473 src
->source_flags
= 0;
474 src
->source_query_retransmit_count
= 0;
475 src
->source_channel_oil
= NULL
;
477 listnode_add(group
->group_source_list
, src
);
479 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
480 igmp_anysource_forward_stop(group
);
484 static void allow(struct gm_sock
*igmp
, struct in_addr from
,
485 struct in_addr group_addr
, int num_sources
,
486 struct in_addr
*sources
)
488 struct gm_source
*source
;
489 struct gm_group
*group
;
492 if (num_sources
== 0) {
494 RFC 3376: 3.1. Socket-State
495 If the requested filter mode is INCLUDE *and* the requested
496 source list is empty, then the entry corresponding to the
497 requested interface and multicast address is deleted if
498 present. If no such entry is present, the request is ignored.
499 So, deleting the group present.
501 group
= find_group_by_addr(igmp
, group_addr
);
505 if (group
->group_filtermode_isexcl
) {
506 if (listcount(group
->group_source_list
) == 1) {
507 struct in_addr star
= {.s_addr
= INADDR_ANY
};
509 source
= igmp_find_source_by_addr(group
, star
);
511 igmp_source_reset_gmi(group
, source
);
514 igmp_group_delete(group
);
520 /* non-existant group is created as INCLUDE {empty} */
521 group
= igmp_add_group_by_addr(igmp
, group_addr
);
526 /* scan received sources */
527 for (i
= 0; i
< num_sources
; ++i
) {
528 struct in_addr
*src_addr
;
530 src_addr
= sources
+ i
;
532 source
= igmp_get_source_by_addr(group
, *src_addr
, NULL
);
537 RFC 3376: 6.4.1. Reception of Current-State Records
539 When receiving IS_IN reports for groups in EXCLUDE mode is
540 sources should be moved from set with (timers = 0) to set with
543 igmp_source_reset_gmi() below, resetting the source timers to
544 GMI, accomplishes this.
546 igmp_source_reset_gmi(group
, source
);
548 } /* scan received sources */
551 void igmpv3_report_isin(struct gm_sock
*igmp
, struct in_addr from
,
552 struct in_addr group_addr
, int num_sources
,
553 struct in_addr
*sources
)
555 on_trace(__func__
, igmp
->interface
, from
, group_addr
, num_sources
,
558 allow(igmp
, from
, group_addr
, num_sources
, sources
);
561 static void isex_excl(struct gm_group
*group
, int num_sources
,
562 struct in_addr
*sources
)
564 struct gm_source
*source
;
568 assert(group
->group_filtermode_isexcl
);
570 /* E.1: set deletion flag for known sources (X,Y) */
571 source_mark_delete_flag(group
);
573 /* scan received sources (A) */
574 for (i
= 0; i
< num_sources
; ++i
) {
575 struct in_addr
*src_addr
;
578 src_addr
= sources
+ i
;
580 /* E.2: lookup reported source from (A) in (X,Y) */
581 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
586 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
587 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
589 /* E.4: if not found, create source with timer=GMI:
591 assert(!source
->t_source_timer
); /* timer == 0 */
592 igmp_source_reset_gmi(group
, source
);
593 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
596 } /* scan received sources */
599 * If we are in isexcl mode and num_sources == 0
600 * than that means we have a *,g entry that
601 * needs to be handled
603 if (group
->group_filtermode_isexcl
&& num_sources
== 0) {
604 struct in_addr star
= {.s_addr
= INADDR_ANY
};
605 source
= igmp_find_source_by_addr(group
, star
);
607 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
608 igmp_source_reset_gmi(group
, source
);
612 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
613 source_delete_by_flag(group
->group_source_list
);
616 static void isex_incl(struct gm_group
*group
, int num_sources
,
617 struct in_addr
*sources
)
622 assert(!group
->group_filtermode_isexcl
);
624 /* I.1: set deletion flag for known sources (A) */
625 source_mark_delete_flag(group
);
627 /* scan received sources (B) */
628 for (i
= 0; i
< num_sources
; ++i
) {
629 struct gm_source
*source
;
630 struct in_addr
*src_addr
;
633 src_addr
= sources
+ i
;
635 /* I.2: lookup reported source (B) */
636 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
641 /* I.3: if found, clear deletion flag (A*B) */
642 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
644 /* I.4: if not found, create source with timer=0 (B-A)
646 assert(!source
->t_source_timer
); /* (B-A) timer=0 */
649 } /* scan received sources */
651 /* I.5: delete all sources marked with deletion flag (A-B) */
652 source_delete_by_flag(group
->group_source_list
);
654 group
->group_filtermode_isexcl
= 1; /* boolean=true */
656 assert(group
->group_filtermode_isexcl
);
658 group_exclude_fwd_anysrc_ifempty(group
);
661 void igmpv3_report_isex(struct gm_sock
*igmp
, struct in_addr from
,
662 struct in_addr group_addr
, int num_sources
,
663 struct in_addr
*sources
, int from_igmp_v2_report
)
665 struct interface
*ifp
= igmp
->interface
;
666 struct gm_group
*group
;
668 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
670 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
673 /* non-existant group is created as INCLUDE {empty} */
674 group
= igmp_add_group_by_addr(igmp
, group_addr
);
679 /* So we can display how we learned the group in our show command output
681 if (from_igmp_v2_report
)
682 group
->igmp_version
= 2;
684 if (group
->group_filtermode_isexcl
) {
686 isex_excl(group
, num_sources
, sources
);
689 isex_incl(group
, num_sources
, sources
);
690 assert(group
->group_filtermode_isexcl
);
693 assert(group
->group_filtermode_isexcl
);
695 igmp_group_reset_gmi(group
);
698 static void toin_incl(struct gm_group
*group
, int num_sources
,
699 struct in_addr
*sources
)
701 int num_sources_tosend
= listcount(group
->group_source_list
);
704 /* Set SEND flag for all known sources (A) */
705 source_mark_send_flag(group
);
707 /* Scan received sources (B) */
708 for (i
= 0; i
< num_sources
; ++i
) {
709 struct gm_source
*source
;
710 struct in_addr
*src_addr
;
713 src_addr
= sources
+ i
;
715 /* Lookup reported source (B) */
716 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
721 /* If found, clear SEND flag (A*B) */
722 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
723 --num_sources_tosend
;
727 igmp_source_reset_gmi(group
, source
);
730 /* Send sources marked with SEND flag: Q(G,A-B) */
731 if (num_sources_tosend
> 0) {
732 source_query_send_by_flag(group
, num_sources_tosend
);
736 static void toin_excl(struct gm_group
*group
, int num_sources
,
737 struct in_addr
*sources
)
739 int num_sources_tosend
;
742 /* Set SEND flag for X (sources with timer > 0) */
743 num_sources_tosend
= source_mark_send_flag_by_timer(group
);
745 /* Scan received sources (A) */
746 for (i
= 0; i
< num_sources
; ++i
) {
747 struct gm_source
*source
;
748 struct in_addr
*src_addr
;
751 src_addr
= sources
+ i
;
753 /* Lookup reported source (A) */
754 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
758 if (source
->t_source_timer
) {
759 /* If found and timer running, clear SEND flag
761 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
762 --num_sources_tosend
;
766 igmp_source_reset_gmi(group
, source
);
769 /* Send sources marked with SEND flag: Q(G,X-A) */
770 if (num_sources_tosend
> 0) {
771 source_query_send_by_flag(group
, num_sources_tosend
);
775 group_query_send(group
);
778 void igmpv3_report_toin(struct gm_sock
*igmp
, struct in_addr from
,
779 struct in_addr group_addr
, int num_sources
,
780 struct in_addr
*sources
)
782 struct interface
*ifp
= igmp
->interface
;
783 struct gm_group
*group
;
785 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
788 * If the requested filter mode is INCLUDE *and* the requested source
789 * list is empty, then the entry corresponding to the requested
790 * interface and multicast address is deleted if present. If no such
791 * entry is present, the request is ignored.
794 /* non-existant group is created as INCLUDE {empty} */
795 group
= igmp_add_group_by_addr(igmp
, group_addr
);
800 group
= find_group_by_addr(igmp
, group_addr
);
805 if (group
->group_filtermode_isexcl
) {
807 toin_excl(group
, num_sources
, sources
);
810 toin_incl(group
, num_sources
, sources
);
814 static void toex_incl(struct gm_group
*group
, int num_sources
,
815 struct in_addr
*sources
)
817 int num_sources_tosend
= 0;
820 assert(!group
->group_filtermode_isexcl
);
822 /* Set DELETE flag for all known sources (A) */
823 source_mark_delete_flag(group
);
825 /* Clear off SEND flag from all known sources (A) */
826 source_clear_send_flag(group
->group_source_list
);
828 /* Scan received sources (B) */
829 for (i
= 0; i
< num_sources
; ++i
) {
830 struct gm_source
*source
;
831 struct in_addr
*src_addr
;
834 src_addr
= sources
+ i
;
836 /* Lookup reported source (B) */
837 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
839 /* If found, clear deletion flag: (A*B) */
840 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
841 /* and set SEND flag (A*B) */
842 IGMP_SOURCE_DO_SEND(source
->source_flags
);
843 ++num_sources_tosend
;
846 } /* Scan received sources (B) */
848 group
->group_filtermode_isexcl
= 1; /* boolean=true */
850 /* Delete all sources marked with DELETE flag (A-B) */
851 source_delete_by_flag(group
->group_source_list
);
853 /* Send sources marked with SEND flag: Q(G,A*B) */
854 if (num_sources_tosend
> 0) {
855 source_query_send_by_flag(group
, num_sources_tosend
);
858 assert(group
->group_filtermode_isexcl
);
860 group_exclude_fwd_anysrc_ifempty(group
);
863 static void toex_excl(struct gm_group
*group
, int num_sources
,
864 struct in_addr
*sources
)
866 int num_sources_tosend
= 0;
869 /* set DELETE flag for all known sources (X,Y) */
870 source_mark_delete_flag(group
);
872 /* clear off SEND flag from all known sources (X,Y) */
873 source_clear_send_flag(group
->group_source_list
);
875 if (num_sources
== 0) {
876 struct gm_source
*source
;
877 struct in_addr any
= {.s_addr
= INADDR_ANY
};
879 source
= igmp_find_source_by_addr(group
, any
);
881 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
884 /* scan received sources (A) */
885 for (i
= 0; i
< num_sources
; ++i
) {
886 struct gm_source
*source
;
887 struct in_addr
*src_addr
;
890 src_addr
= sources
+ i
;
892 /* lookup reported source (A) in known sources (X,Y) */
893 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
898 /* if found, clear off DELETE flag from reported source
900 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
902 /* if not found, create source with Group Timer:
903 * (A-X-Y)=Group Timer */
904 long group_timer_msec
;
906 assert(!source
->t_source_timer
); /* timer == 0 */
907 group_timer_msec
= igmp_group_timer_remain_msec(group
);
908 igmp_source_timer_on(group
, source
, group_timer_msec
);
909 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
911 /* make sure source is created with DELETE flag unset */
912 assert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
915 /* make sure reported source has DELETE flag unset */
916 assert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
918 if (source
->t_source_timer
) {
919 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
920 IGMP_SOURCE_DO_SEND(source
->source_flags
);
921 ++num_sources_tosend
;
924 } /* scan received sources (A) */
927 delete all sources marked with DELETE flag:
931 source_delete_by_flag(group
->group_source_list
);
933 /* send sources marked with SEND flag: Q(G,A-Y) */
934 if (num_sources_tosend
> 0) {
935 source_query_send_by_flag(group
, num_sources_tosend
);
939 void igmpv3_report_toex(struct gm_sock
*igmp
, struct in_addr from
,
940 struct in_addr group_addr
, int num_sources
,
941 struct in_addr
*sources
)
943 struct interface
*ifp
= igmp
->interface
;
944 struct gm_group
*group
;
946 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
948 /* non-existant group is created as INCLUDE {empty} */
949 group
= igmp_add_group_by_addr(igmp
, group_addr
);
954 if (group
->group_filtermode_isexcl
) {
956 toex_excl(group
, num_sources
, sources
);
959 toex_incl(group
, num_sources
, sources
);
960 assert(group
->group_filtermode_isexcl
);
962 assert(group
->group_filtermode_isexcl
);
964 /* Group Timer=GMI */
965 igmp_group_reset_gmi(group
);
968 void igmpv3_report_allow(struct gm_sock
*igmp
, struct in_addr from
,
969 struct in_addr group_addr
, int num_sources
,
970 struct in_addr
*sources
)
972 on_trace(__func__
, igmp
->interface
, from
, group_addr
, num_sources
,
975 allow(igmp
, from
, group_addr
, num_sources
, sources
);
978 static void igmp_send_query_group(struct gm_group
*group
, char *query_buf
,
979 size_t query_buf_size
, int num_sources
,
982 struct interface
*ifp
= group
->interface
;
983 struct pim_interface
*pim_ifp
= ifp
->info
;
984 struct gm_sock
*igmp
;
985 struct listnode
*sock_node
;
987 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
989 pim_ifp
->igmp_version
, group
, igmp
->fd
, ifp
->name
,
990 query_buf
, query_buf_size
, num_sources
,
991 group
->group_addr
, group
->group_addr
,
992 pim_ifp
->gm_specific_query_max_response_time_dsec
,
993 s_flag
, igmp
->querier_robustness_variable
,
994 igmp
->querier_query_interval
);
999 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1001 When transmitting a group specific query, if the group timer is
1002 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1003 in the query message.
1005 static void group_retransmit_group(struct gm_group
*group
)
1007 struct pim_interface
*pim_ifp
;
1008 long lmqc
; /* Last Member Query Count */
1009 long lmqi_msec
; /* Last Member Query Interval */
1010 long lmqt_msec
; /* Last Member Query Time */
1014 pim_ifp
= group
->interface
->info
;
1016 if (pim_ifp
->igmp_version
== 3) {
1017 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1019 query_buf_size
= IGMP_V12_MSG_SIZE
;
1022 char query_buf
[query_buf_size
];
1024 lmqc
= pim_ifp
->gm_last_member_query_count
;
1025 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1026 lmqt_msec
= lmqc
* lmqi_msec
;
1029 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1031 When transmitting a group specific query, if the group timer is
1032 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1033 in the query message.
1035 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1037 if (PIM_DEBUG_IGMP_TRACE
) {
1038 char group_str
[INET_ADDRSTRLEN
];
1039 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1042 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1043 group_str
, group
->interface
->name
, s_flag
,
1044 group
->group_specific_query_retransmit_count
);
1048 RFC3376: 4.1.12. IP Destination Addresses for Queries
1050 Group-Specific and Group-and-Source-Specific Queries are sent with
1051 an IP destination address equal to the multicast address of
1055 igmp_send_query_group(group
, query_buf
, sizeof(query_buf
), 0, s_flag
);
1059 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1061 When building a group and source specific query for a group G, two
1062 separate query messages are sent for the group. The first one has
1063 the "Suppress Router-Side Processing" bit set and contains all the
1064 sources with retransmission state and timers greater than LMQT. The
1065 second has the "Suppress Router-Side Processing" bit clear and
1066 contains all the sources with retransmission state and timers lower
1067 or equal to LMQT. If either of the two calculated messages does not
1068 contain any sources, then its transmission is suppressed.
1070 static int group_retransmit_sources(struct gm_group
*group
,
1071 int send_with_sflag_set
)
1073 struct pim_interface
*pim_ifp
;
1074 long lmqc
; /* Last Member Query Count */
1075 long lmqi_msec
; /* Last Member Query Interval */
1076 long lmqt_msec
; /* Last Member Query Time */
1077 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1078 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1079 int query_buf1_max_sources
;
1080 int query_buf2_max_sources
;
1081 struct in_addr
*source_addr1
;
1082 struct in_addr
*source_addr2
;
1083 int num_sources_tosend1
;
1084 int num_sources_tosend2
;
1085 struct listnode
*src_node
;
1086 struct gm_source
*src
;
1087 int num_retransmit_sources_left
= 0;
1089 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1090 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1092 pim_ifp
= group
->interface
->info
;
1094 lmqc
= pim_ifp
->gm_last_member_query_count
;
1095 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1096 lmqt_msec
= lmqc
* lmqi_msec
;
1098 /* Scan all group sources */
1099 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1101 /* Source has retransmission state? */
1102 if (src
->source_query_retransmit_count
< 1)
1105 if (--src
->source_query_retransmit_count
> 0) {
1106 ++num_retransmit_sources_left
;
1109 /* Copy source address into appropriate query buffer */
1110 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1111 *source_addr1
= src
->source_addr
;
1114 *source_addr2
= src
->source_addr
;
1119 num_sources_tosend1
=
1121 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1122 num_sources_tosend2
=
1124 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1126 if (PIM_DEBUG_IGMP_TRACE
) {
1127 char group_str
[INET_ADDRSTRLEN
];
1128 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1131 "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",
1132 group_str
, group
->interface
->name
, num_sources_tosend1
,
1133 num_sources_tosend2
, send_with_sflag_set
,
1134 num_retransmit_sources_left
);
1137 if (num_sources_tosend1
> 0) {
1139 Send group-and-source-specific query with s_flag set and all
1140 sources with timers greater than LMQT.
1143 if (send_with_sflag_set
) {
1145 query_buf1_max_sources
=
1146 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1148 if (num_sources_tosend1
> query_buf1_max_sources
) {
1149 char group_str
[INET_ADDRSTRLEN
];
1150 pim_inet4_dump("<group?>", group
->group_addr
,
1151 group_str
, sizeof(group_str
));
1153 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1154 __func__
, group_str
,
1155 group
->interface
->name
,
1156 num_sources_tosend1
, sizeof(query_buf1
),
1157 query_buf1_max_sources
);
1160 RFC3376: 4.1.12. IP Destination Addresses for
1163 Group-Specific and Group-and-Source-Specific
1164 Queries are sent with
1165 an IP destination address equal to the
1166 multicast address of
1170 igmp_send_query_group(
1171 group
, query_buf1
, sizeof(query_buf1
),
1172 num_sources_tosend1
, 1 /* s_flag */);
1175 } /* send_with_sflag_set */
1178 if (num_sources_tosend2
> 0) {
1180 Send group-and-source-specific query with s_flag clear and all
1181 sources with timers lower or equal to LMQT.
1184 query_buf2_max_sources
=
1185 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1186 if (num_sources_tosend2
> query_buf2_max_sources
) {
1187 char group_str
[INET_ADDRSTRLEN
];
1188 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1191 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1192 __func__
, group_str
, group
->interface
->name
,
1193 num_sources_tosend2
, sizeof(query_buf2
),
1194 query_buf2_max_sources
);
1197 RFC3376: 4.1.12. IP Destination Addresses for Queries
1199 Group-Specific and Group-and-Source-Specific Queries
1201 an IP destination address equal to the multicast
1206 igmp_send_query_group(
1207 group
, query_buf2
, sizeof(query_buf2
),
1208 num_sources_tosend2
, 0 /* s_flag */);
1212 return num_retransmit_sources_left
;
1215 static int igmp_group_retransmit(struct thread
*t
)
1217 struct gm_group
*group
;
1218 int num_retransmit_sources_left
;
1219 int send_with_sflag_set
; /* boolean */
1221 group
= THREAD_ARG(t
);
1223 if (PIM_DEBUG_IGMP_TRACE
) {
1224 char group_str
[INET_ADDRSTRLEN
];
1225 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1227 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1228 group
->interface
->name
);
1231 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1232 if (group
->group_specific_query_retransmit_count
> 0) {
1234 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1235 group_retransmit_group(group
);
1236 --group
->group_specific_query_retransmit_count
;
1240 If a group specific query is scheduled to be transmitted at
1242 same time as a group and source specific query for the same
1244 then transmission of the group and source specific message
1246 "Suppress Router-Side Processing" bit set may be suppressed.
1248 send_with_sflag_set
= 0; /* boolean=false */
1250 send_with_sflag_set
= 1; /* boolean=true */
1253 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1254 num_retransmit_sources_left
=
1255 group_retransmit_sources(group
, send_with_sflag_set
);
1258 Keep group retransmit timer running if there is any retransmit
1261 if ((num_retransmit_sources_left
> 0)
1262 || (group
->group_specific_query_retransmit_count
> 0)) {
1263 group_retransmit_timer_on(group
);
1270 group_retransmit_timer_on:
1271 if group retransmit timer isn't running, starts it;
1272 otherwise, do nothing
1274 static void group_retransmit_timer_on(struct gm_group
*group
)
1276 struct pim_interface
*pim_ifp
;
1277 long lmqi_msec
; /* Last Member Query Interval */
1279 /* if group retransmit timer is running, do nothing */
1280 if (group
->t_group_query_retransmit_timer
) {
1284 pim_ifp
= group
->interface
->info
;
1286 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1288 if (PIM_DEBUG_IGMP_TRACE
) {
1289 char group_str
[INET_ADDRSTRLEN
];
1290 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1293 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1294 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1295 group
->interface
->name
);
1298 thread_add_timer_msec(router
->master
, igmp_group_retransmit
, group
,
1300 &group
->t_group_query_retransmit_timer
);
1303 static long igmp_group_timer_remain_msec(struct gm_group
*group
)
1305 return pim_time_timer_remain_msec(group
->t_group_timer
);
1308 static long igmp_source_timer_remain_msec(struct gm_source
*source
)
1310 return pim_time_timer_remain_msec(source
->t_source_timer
);
1314 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1316 static void group_query_send(struct gm_group
*group
)
1318 struct pim_interface
*pim_ifp
;
1319 long lmqc
; /* Last Member Query Count */
1321 pim_ifp
= group
->interface
->info
;
1322 lmqc
= pim_ifp
->gm_last_member_query_count
;
1324 /* lower group timer to lmqt */
1325 igmp_group_timer_lower_to_lmqt(group
);
1327 /* reset retransmission counter */
1328 group
->group_specific_query_retransmit_count
= lmqc
;
1330 /* immediately send group specific query (decrease retransmit counter by
1332 group_retransmit_group(group
);
1334 /* make sure group retransmit timer is running */
1335 group_retransmit_timer_on(group
);
1339 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1341 static void source_query_send_by_flag(struct gm_group
*group
,
1342 int num_sources_tosend
)
1344 struct pim_interface
*pim_ifp
;
1345 struct listnode
*src_node
;
1346 struct gm_source
*src
;
1347 long lmqc
; /* Last Member Query Count */
1348 long lmqi_msec
; /* Last Member Query Interval */
1349 long lmqt_msec
; /* Last Member Query Time */
1351 assert(num_sources_tosend
> 0);
1353 pim_ifp
= group
->interface
->info
;
1355 lmqc
= pim_ifp
->gm_last_member_query_count
;
1356 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1357 lmqt_msec
= lmqc
* lmqi_msec
;
1360 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1363 (...) for each of the sources in X of group G, with source timer
1366 o Set number of retransmissions for each source to [Last Member
1368 o Lower source timer to LMQT.
1370 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1371 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1372 /* source "src" in X of group G */
1373 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1374 src
->source_query_retransmit_count
= lmqc
;
1375 igmp_source_timer_lower_to_lmqt(src
);
1380 /* send group-and-source specific queries */
1381 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1383 /* make sure group retransmit timer is running */
1384 group_retransmit_timer_on(group
);
1387 static void block_excl(struct gm_group
*group
, int num_sources
,
1388 struct in_addr
*sources
)
1390 int num_sources_tosend
= 0;
1393 /* 1. clear off SEND flag from all known sources (X,Y) */
1394 source_clear_send_flag(group
->group_source_list
);
1396 /* 2. scan received sources (A) */
1397 for (i
= 0; i
< num_sources
; ++i
) {
1398 struct gm_source
*source
;
1399 struct in_addr
*src_addr
;
1402 src_addr
= sources
+ i
;
1404 /* lookup reported source (A) in known sources (X,Y) */
1405 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
1410 /* 3: if not found, create source with Group Timer:
1411 * (A-X-Y)=Group Timer */
1412 long group_timer_msec
;
1414 assert(!source
->t_source_timer
); /* timer == 0 */
1415 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1416 igmp_source_timer_on(group
, source
, group_timer_msec
);
1417 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1420 if (source
->t_source_timer
) {
1421 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1422 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1423 ++num_sources_tosend
;
1427 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1428 if (num_sources_tosend
> 0) {
1429 source_query_send_by_flag(group
, num_sources_tosend
);
1433 static void block_incl(struct gm_group
*group
, int num_sources
,
1434 struct in_addr
*sources
)
1436 int num_sources_tosend
= 0;
1439 /* 1. clear off SEND flag from all known sources (B) */
1440 source_clear_send_flag(group
->group_source_list
);
1442 /* 2. scan received sources (A) */
1443 for (i
= 0; i
< num_sources
; ++i
) {
1444 struct gm_source
*source
;
1445 struct in_addr
*src_addr
;
1447 src_addr
= sources
+ i
;
1449 /* lookup reported source (A) in known sources (B) */
1450 source
= igmp_find_source_by_addr(group
, *src_addr
);
1452 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1453 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1454 ++num_sources_tosend
;
1458 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1459 if (num_sources_tosend
> 0) {
1460 source_query_send_by_flag(group
, num_sources_tosend
);
1464 void igmpv3_report_block(struct gm_sock
*igmp
, struct in_addr from
,
1465 struct in_addr group_addr
, int num_sources
,
1466 struct in_addr
*sources
)
1468 struct interface
*ifp
= igmp
->interface
;
1469 struct gm_group
*group
;
1471 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
1473 /* non-existant group is created as INCLUDE {empty} */
1474 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1479 if (group
->group_filtermode_isexcl
) {
1481 block_excl(group
, num_sources
, sources
);
1484 block_incl(group
, num_sources
, sources
);
1488 void igmp_group_timer_lower_to_lmqt(struct gm_group
*group
)
1490 struct interface
*ifp
;
1491 struct pim_interface
*pim_ifp
;
1493 int lmqi_dsec
; /* Last Member Query Interval */
1494 int lmqc
; /* Last Member Query Count */
1495 int lmqt_msec
; /* Last Member Query Time */
1498 RFC 3376: 6.2.2. Definition of Group Timers
1500 The group timer is only used when a group is in EXCLUDE mode and
1501 it represents the time for the *filter-mode* of the group to
1502 expire and switch to INCLUDE mode.
1504 if (!group
->group_filtermode_isexcl
) {
1508 ifp
= group
->interface
;
1509 pim_ifp
= ifp
->info
;
1512 lmqi_dsec
= pim_ifp
->gm_specific_query_max_response_time_dsec
;
1513 lmqc
= pim_ifp
->gm_last_member_query_count
;
1514 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1515 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1517 if (PIM_DEBUG_IGMP_TRACE
) {
1518 char group_str
[INET_ADDRSTRLEN
];
1519 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1522 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1523 __func__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1527 assert(group
->group_filtermode_isexcl
);
1529 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1532 void igmp_source_timer_lower_to_lmqt(struct gm_source
*source
)
1534 struct gm_group
*group
;
1535 struct interface
*ifp
;
1536 struct pim_interface
*pim_ifp
;
1538 int lmqi_dsec
; /* Last Member Query Interval */
1539 int lmqc
; /* Last Member Query Count */
1540 int lmqt_msec
; /* Last Member Query Time */
1542 group
= source
->source_group
;
1543 ifp
= group
->interface
;
1544 pim_ifp
= ifp
->info
;
1547 lmqi_dsec
= pim_ifp
->gm_specific_query_max_response_time_dsec
;
1548 lmqc
= pim_ifp
->gm_last_member_query_count
;
1549 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1550 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1552 if (PIM_DEBUG_IGMP_TRACE
) {
1553 char group_str
[INET_ADDRSTRLEN
];
1554 char source_str
[INET_ADDRSTRLEN
];
1555 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1557 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
1558 sizeof(source_str
));
1560 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1561 __func__
, group_str
, source_str
, ifname
, lmqc
,
1562 lmqi_dsec
, lmqt_msec
);
1565 igmp_source_timer_on(group
, source
, lmqt_msec
);
1568 void igmp_v3_send_query(struct gm_group
*group
, int fd
, const char *ifname
,
1569 char *query_buf
, int query_buf_size
, int num_sources
,
1570 struct in_addr dst_addr
, struct in_addr group_addr
,
1571 int query_max_response_time_dsec
, uint8_t s_flag
,
1572 uint8_t querier_robustness_variable
,
1573 uint16_t querier_query_interval
)
1576 uint8_t max_resp_code
;
1579 struct sockaddr_in to
;
1583 assert(num_sources
>= 0);
1585 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1586 if (msg_size
> query_buf_size
) {
1589 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1590 __FILE__
, __func__
, msg_size
, query_buf_size
);
1594 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1595 assert((s_flag
== 0) || (s_flag
== 1));
1597 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1598 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1601 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1603 If non-zero, the QRV field contains the [Robustness Variable]
1604 value used by the querier, i.e., the sender of the Query. If the
1605 querier's [Robustness Variable] exceeds 7, the maximum value of
1606 the QRV field, the QRV is set to zero.
1608 if (querier_robustness_variable
> 7) {
1609 querier_robustness_variable
= 0;
1612 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1613 query_buf
[1] = max_resp_code
;
1614 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
1615 0; /* for computing checksum */
1616 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
1618 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1619 query_buf
[9] = qqic
;
1620 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) =
1623 checksum
= in_cksum(query_buf
, msg_size
);
1624 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1626 if (PIM_DEBUG_IGMP_PACKETS
) {
1627 char dst_str
[INET_ADDRSTRLEN
];
1628 char group_str
[INET_ADDRSTRLEN
];
1629 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1630 pim_inet4_dump("<group?>", group_addr
, group_str
,
1633 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1634 dst_str
, ifname
, group_str
, num_sources
, msg_size
,
1635 s_flag
, querier_robustness_variable
,
1636 querier_query_interval
, qqic
);
1639 memset(&to
, 0, sizeof(to
));
1640 to
.sin_family
= AF_INET
;
1641 to
.sin_addr
= dst_addr
;
1644 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1645 (struct sockaddr
*)&to
, tolen
);
1646 if (sent
!= (ssize_t
)msg_size
) {
1647 char dst_str
[INET_ADDRSTRLEN
];
1648 char group_str
[INET_ADDRSTRLEN
];
1649 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1650 pim_inet4_dump("<group?>", group_addr
, group_str
,
1654 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1655 dst_str
, ifname
, group_str
, msg_size
, errno
,
1656 safe_strerror(errno
));
1659 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1660 dst_str
, ifname
, group_str
, msg_size
, sent
);
1666 s_flag sanity test: s_flag must be set for general queries
1668 RFC 3376: 6.6.1. Timer Updates
1670 When a router sends or receives a query with a clear Suppress
1671 Router-Side Processing flag, it must update its timers to reflect
1672 the correct timeout values for the group or sources being queried.
1674 General queries don't trigger timer update.
1677 /* general query? */
1678 if (PIM_INADDR_IS_ANY(group_addr
)) {
1679 char dst_str
[INET_ADDRSTRLEN
];
1680 char group_str
[INET_ADDRSTRLEN
];
1681 pim_inet4_dump("<dst?>", dst_addr
, dst_str
,
1683 pim_inet4_dump("<group?>", group_addr
, group_str
,
1686 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1687 __func__
, dst_str
, ifname
, group_str
,
1693 void igmp_v3_recv_query(struct gm_sock
*igmp
, const char *from_str
,
1696 struct interface
*ifp
;
1697 struct pim_interface
*pim_ifp
;
1698 struct in_addr group_addr
;
1699 uint8_t resv_s_qrv
= 0;
1704 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1705 ifp
= igmp
->interface
;
1706 pim_ifp
= ifp
->info
;
1709 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1711 * Routers adopt the QRV value from the most recently received Query
1712 * as their own [Robustness Variable] value, unless that most
1713 * recently received QRV was zero, in which case the receivers use
1714 * the default [Robustness Variable] value specified in section 8.1
1715 * or a statically configured value.
1717 resv_s_qrv
= igmp_msg
[8];
1718 qrv
= 7 & resv_s_qrv
;
1719 igmp
->querier_robustness_variable
=
1720 qrv
? qrv
: pim_ifp
->gm_default_robustness_variable
;
1723 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1725 * Multicast routers that are not the current querier adopt the QQI
1726 * value from the most recently received Query as their own [Query
1727 * Interval] value, unless that most recently received QQI was zero,
1728 * in which case the receiving routers use the default.
1730 if (igmp
->t_other_querier_timer
) {
1731 /* other querier present */
1735 qqi
= igmp_msg_decode8to16(qqic
);
1736 igmp
->querier_query_interval
=
1737 qqi
? qqi
: pim_ifp
->gm_default_query_interval
;
1739 if (PIM_DEBUG_IGMP_TRACE
) {
1740 char ifaddr_str
[INET_ADDRSTRLEN
];
1741 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
1742 sizeof(ifaddr_str
));
1744 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1746 qqi
? "recv-non-default" : "default",
1747 igmp
->querier_query_interval
, qqic
, from_str
);
1752 * RFC 3376: 6.6.1. Timer Updates
1754 * When a router sends or receives a query with a clear Suppress
1755 * Router-Side Processing flag, it must update its timers to reflect
1756 * the correct timeout values for the group or sources being queried.
1758 * General queries don't trigger timer update.
1760 s_flag
= (1 << 3) & resv_s_qrv
;
1763 /* s_flag is clear */
1765 if (PIM_INADDR_IS_ANY(group_addr
)) {
1766 /* this is a general query */
1767 /* log that general query should have the s_flag set */
1769 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1770 from_str
, ifp
->name
);
1772 struct gm_group
*group
;
1774 /* this is a non-general query: perform timer updates */
1776 group
= find_group_by_addr(igmp
, group_addr
);
1778 int recv_num_sources
= ntohs(*(
1781 + IGMP_V3_NUMSOURCES_OFFSET
));
1784 * RFC 3376: 6.6.1. Timer Updates
1785 * Query Q(G,A): Source Timer for sources in A
1786 * are lowered to LMQT
1787 * Query Q(G): Group Timer is lowered to LMQT
1789 if (recv_num_sources
< 1) {
1790 /* Query Q(G): Group Timer is lowered to
1793 igmp_group_timer_lower_to_lmqt(group
);
1795 /* Query Q(G,A): Source Timer for
1796 * sources in A are lowered to LMQT */
1798 /* Scan sources in query and lower their
1800 struct in_addr
*sources
=
1803 + IGMP_V3_SOURCES_OFFSET
);
1804 for (i
= 0; i
< recv_num_sources
; ++i
) {
1805 struct in_addr src_addr
;
1806 struct gm_source
*src
;
1807 memcpy(&src_addr
, sources
+ i
,
1808 sizeof(struct in_addr
));
1809 src
= igmp_find_source_by_addr(
1812 igmp_source_timer_lower_to_lmqt(
1818 char group_str
[INET_ADDRSTRLEN
];
1819 pim_inet4_dump("<group?>", group_addr
,
1820 group_str
, sizeof(group_str
));
1822 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1823 from_str
, ifp
->name
, group_str
);
1826 } /* s_flag is clear: timer updates */
1829 int igmp_v3_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
1830 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1833 uint8_t *group_record
;
1834 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1835 struct interface
*ifp
= igmp
->interface
;
1838 struct pim_interface
*pim_ifp
;
1840 if (igmp
->mtrace_only
)
1843 pim_ifp
= igmp
->interface
->info
;
1845 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1847 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1848 from_str
, ifp
->name
, igmp_msg_len
,
1849 IGMP_V3_MSG_MIN_SIZE
);
1853 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
1855 "Recv IGMPv3 report from %s on %s with invalid checksum",
1856 from_str
, ifp
->name
);
1860 /* Collecting IGMP Rx stats */
1861 igmp
->rx_stats
.report_v3
++;
1864 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1865 if (num_groups
< 1) {
1867 "Recv IGMP report v3 from %s on %s: missing group records",
1868 from_str
, ifp
->name
);
1872 if (PIM_DEBUG_IGMP_PACKETS
) {
1874 "Recv IGMP report v3 from %s on %s: size=%d groups=%d",
1875 from_str
, ifp
->name
, igmp_msg_len
, num_groups
);
1878 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1881 for (i
= 0; i
< num_groups
; ++i
) {
1882 struct in_addr rec_group
;
1887 int rec_num_sources
;
1891 bool filtered
= false;
1893 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1896 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1897 from_str
, ifp
->name
);
1901 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1903 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1904 rec_num_sources
= ntohs(*(
1905 uint16_t *)(group_record
1906 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1909 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1910 sizeof(struct in_addr
));
1912 if (PIM_DEBUG_IGMP_PACKETS
) {
1914 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
1915 from_str
, ifp
->name
, i
, rec_type
,
1916 rec_auxdatalen
, rec_num_sources
,
1922 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1924 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1926 if ((src
+ 4) > report_pastend
) {
1928 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1929 from_str
, ifp
->name
);
1933 if (PIM_DEBUG_IGMP_PACKETS
) {
1936 if (!inet_ntop(AF_INET
, src
, src_str
,
1938 snprintf(src_str
, sizeof(src_str
),
1942 " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s",
1943 from_str
, ifp
->name
, i
,
1944 &rec_group
, src_str
);
1946 } /* for (sources) */
1949 lncb
.family
= AF_INET
;
1950 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1951 lncb
.prefixlen
= 24;
1954 g
.u
.prefix4
= rec_group
;
1955 g
.prefixlen
= IPV4_MAX_BITLEN
;
1957 /* determine filtering status for group */
1958 filtered
= pim_is_group_filtered(ifp
->info
, &rec_group
);
1960 if (PIM_DEBUG_IGMP_PACKETS
&& filtered
)
1962 "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
1963 &rec_group
, from_str
, ifp
->name
,
1964 pim_ifp
->boundary_oil_plist
);
1967 * If we receive a igmp report with the group in 224.0.0.0/24
1968 * then we should ignore it
1970 if (prefix_match(&lncb
, &g
))
1973 if (!local_ncb
&& !filtered
)
1975 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1976 igmpv3_report_isin(igmp
, from
, rec_group
,
1978 (struct in_addr
*)sources
);
1980 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1982 igmp
, from
, rec_group
, rec_num_sources
,
1983 (struct in_addr
*)sources
, 0);
1985 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
1986 igmpv3_report_toin(igmp
, from
, rec_group
,
1988 (struct in_addr
*)sources
);
1990 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
1991 igmpv3_report_toex(igmp
, from
, rec_group
,
1993 (struct in_addr
*)sources
);
1995 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
1996 igmpv3_report_allow(igmp
, from
, rec_group
,
1998 (struct in_addr
*)sources
);
2000 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
2001 igmpv3_report_block(igmp
, from
, rec_group
,
2003 (struct in_addr
*)sources
);
2007 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2008 from_str
, ifp
->name
, rec_type
);
2012 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2015 } /* for (group records) */