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 void 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
);
184 static void source_timer_off(struct gm_group
*group
, struct gm_source
*source
)
186 if (!source
->t_source_timer
)
189 if (PIM_DEBUG_IGMP_TRACE
) {
190 char group_str
[INET_ADDRSTRLEN
];
191 char source_str
[INET_ADDRSTRLEN
];
192 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
194 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
197 "Cancelling TIMER event for group %s source %s on %s",
198 group_str
, source_str
, group
->interface
->name
);
201 THREAD_OFF(source
->t_source_timer
);
204 static void igmp_source_timer_on(struct gm_group
*group
,
205 struct gm_source
*source
, long interval_msec
)
207 source_timer_off(group
, source
);
208 struct pim_interface
*pim_ifp
= group
->interface
->info
;
210 if (PIM_DEBUG_IGMP_EVENTS
) {
211 char group_str
[INET_ADDRSTRLEN
];
212 char source_str
[INET_ADDRSTRLEN
];
213 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
215 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
218 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
219 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
220 source_str
, group
->interface
->name
);
223 thread_add_timer_msec(router
->master
, igmp_source_timer
, source
,
224 interval_msec
, &source
->t_source_timer
);
227 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
229 Source timer switched from (T == 0) to (T > 0): enable forwarding.
231 igmp_source_forward_start(pim_ifp
->pim
, source
);
234 void igmp_source_reset_gmi(struct gm_group
*group
, struct gm_source
*source
)
236 long group_membership_interval_msec
;
237 struct interface
*ifp
;
239 ifp
= group
->interface
;
241 group_membership_interval_msec
= igmp_gmi_msec(group
);
243 if (PIM_DEBUG_IGMP_TRACE
) {
244 char group_str
[INET_ADDRSTRLEN
];
245 char source_str
[INET_ADDRSTRLEN
];
247 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
249 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
253 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
254 source_str
, group_membership_interval_msec
/ 1000,
255 group_membership_interval_msec
% 1000, group_str
,
259 igmp_source_timer_on(group
, source
, group_membership_interval_msec
);
262 static void source_mark_delete_flag(struct gm_group
*group
)
264 struct listnode
*src_node
;
265 struct gm_source
*src
;
267 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
268 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
272 static void source_mark_send_flag(struct gm_group
*group
)
274 struct listnode
*src_node
;
275 struct gm_source
*src
;
277 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
278 IGMP_SOURCE_DO_SEND(src
->source_flags
);
282 static int source_mark_send_flag_by_timer(struct gm_group
*group
)
284 struct listnode
*src_node
;
285 struct gm_source
*src
;
286 int num_marked_sources
= 0;
288 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
289 /* Is source timer running? */
290 if (src
->t_source_timer
) {
291 IGMP_SOURCE_DO_SEND(src
->source_flags
);
292 ++num_marked_sources
;
294 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
298 return num_marked_sources
;
301 static void source_clear_send_flag(struct list
*source_list
)
303 struct listnode
*src_node
;
304 struct gm_source
*src
;
306 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
307 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
312 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
314 static void group_exclude_fwd_anysrc_ifempty(struct gm_group
*group
)
316 struct pim_interface
*pim_ifp
= group
->interface
->info
;
318 assert(group
->group_filtermode_isexcl
);
320 if (listcount(group
->group_source_list
) < 1) {
321 igmp_anysource_forward_start(pim_ifp
->pim
, group
);
325 void igmp_source_free(struct gm_source
*source
)
327 /* make sure there is no source timer running */
328 assert(!source
->t_source_timer
);
330 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
333 static void source_channel_oil_detach(struct gm_source
*source
)
335 if (source
->source_channel_oil
) {
336 pim_channel_oil_del(source
->source_channel_oil
, __func__
);
337 source
->source_channel_oil
= NULL
;
342 igmp_source_delete: stop fowarding, and delete the source
343 igmp_source_forward_stop: stop fowarding, but keep the source
345 void igmp_source_delete(struct gm_source
*source
)
347 struct gm_group
*group
;
350 group
= source
->source_group
;
352 if (PIM_DEBUG_IGMP_TRACE
) {
353 char group_str
[INET_ADDRSTRLEN
];
354 char source_str
[INET_ADDRSTRLEN
];
355 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
357 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
360 "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d",
361 source_str
, group_str
, group
->interface
->name
,
362 source
->source_channel_oil
363 ? source
->source_channel_oil
->oil_ref_count
367 source_timer_off(group
, source
);
368 igmp_source_forward_stop(source
);
370 /* sanity check that forwarding has been disabled */
371 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
372 char group_str
[INET_ADDRSTRLEN
];
373 char source_str
[INET_ADDRSTRLEN
];
374 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
376 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
379 "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s",
380 __func__
, source_str
, group_str
,
381 group
->interface
->name
);
385 source_channel_oil_detach(source
);
388 notice that listnode_delete() can't be moved
389 into igmp_source_free() because the later is
390 called by list_delete_all_node()
392 listnode_delete(group
->group_source_list
, source
);
394 src
.s_addr
= source
->source_addr
.s_addr
;
395 igmp_source_free(source
);
397 /* Group source list is empty and current source is * then
398 *,G group going away so do not trigger start */
399 if (group
->group_filtermode_isexcl
400 && (listcount(group
->group_source_list
) != 0)
401 && src
.s_addr
!= INADDR_ANY
) {
402 group_exclude_fwd_anysrc_ifempty(group
);
406 static void source_delete_by_flag(struct list
*source_list
)
408 struct listnode
*src_node
;
409 struct listnode
*src_nextnode
;
410 struct gm_source
*src
;
412 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
413 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
414 igmp_source_delete(src
);
417 void igmp_source_delete_expired(struct list
*source_list
)
419 struct listnode
*src_node
;
420 struct listnode
*src_nextnode
;
421 struct gm_source
*src
;
423 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
424 if (!src
->t_source_timer
)
425 igmp_source_delete(src
);
428 struct gm_source
*igmp_find_source_by_addr(struct gm_group
*group
,
429 struct in_addr src_addr
)
431 struct listnode
*src_node
;
432 struct gm_source
*src
;
434 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
435 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
441 struct gm_source
*igmp_get_source_by_addr(struct gm_group
*group
,
442 struct in_addr src_addr
, bool *new)
444 struct gm_source
*src
;
449 src
= igmp_find_source_by_addr(group
, src_addr
);
453 if (PIM_DEBUG_IGMP_TRACE
) {
454 char group_str
[INET_ADDRSTRLEN
];
455 char source_str
[INET_ADDRSTRLEN
];
456 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
458 pim_inet4_dump("<source?>", src_addr
, source_str
,
461 "Creating new IGMP source %s for group %s on interface %s",
462 source_str
, group_str
, group
->interface
->name
);
465 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
467 src
->t_source_timer
= NULL
;
468 src
->source_group
= group
; /* back pointer */
469 src
->source_addr
= src_addr
;
470 src
->source_creation
= pim_time_monotonic_sec();
471 src
->source_flags
= 0;
472 src
->source_query_retransmit_count
= 0;
473 src
->source_channel_oil
= NULL
;
475 listnode_add(group
->group_source_list
, src
);
477 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
478 igmp_anysource_forward_stop(group
);
482 static void allow(struct gm_sock
*igmp
, struct in_addr from
,
483 struct in_addr group_addr
, int num_sources
,
484 struct in_addr
*sources
)
486 struct gm_source
*source
;
487 struct gm_group
*group
;
490 if (num_sources
== 0) {
492 RFC 3376: 3.1. Socket-State
493 If the requested filter mode is INCLUDE *and* the requested
494 source list is empty, then the entry corresponding to the
495 requested interface and multicast address is deleted if
496 present. If no such entry is present, the request is ignored.
497 So, deleting the group present.
499 group
= find_group_by_addr(igmp
, group_addr
);
503 if (group
->group_filtermode_isexcl
) {
504 if (listcount(group
->group_source_list
) == 1) {
505 struct in_addr star
= {.s_addr
= INADDR_ANY
};
507 source
= igmp_find_source_by_addr(group
, star
);
509 igmp_source_reset_gmi(group
, source
);
512 igmp_group_delete(group
);
518 /* non-existant group is created as INCLUDE {empty} */
519 group
= igmp_add_group_by_addr(igmp
, group_addr
);
524 /* scan received sources */
525 for (i
= 0; i
< num_sources
; ++i
) {
526 struct in_addr
*src_addr
;
528 src_addr
= sources
+ i
;
530 source
= igmp_get_source_by_addr(group
, *src_addr
, NULL
);
535 RFC 3376: 6.4.1. Reception of Current-State Records
537 When receiving IS_IN reports for groups in EXCLUDE mode is
538 sources should be moved from set with (timers = 0) to set with
541 igmp_source_reset_gmi() below, resetting the source timers to
542 GMI, accomplishes this.
544 igmp_source_reset_gmi(group
, source
);
546 } /* scan received sources */
549 void igmpv3_report_isin(struct gm_sock
*igmp
, struct in_addr from
,
550 struct in_addr group_addr
, int num_sources
,
551 struct in_addr
*sources
)
553 on_trace(__func__
, igmp
->interface
, from
, group_addr
, num_sources
,
556 allow(igmp
, from
, group_addr
, num_sources
, sources
);
559 static void isex_excl(struct gm_group
*group
, int num_sources
,
560 struct in_addr
*sources
)
562 struct gm_source
*source
;
566 assert(group
->group_filtermode_isexcl
);
568 /* E.1: set deletion flag for known sources (X,Y) */
569 source_mark_delete_flag(group
);
571 /* scan received sources (A) */
572 for (i
= 0; i
< num_sources
; ++i
) {
573 struct in_addr
*src_addr
;
576 src_addr
= sources
+ i
;
578 /* E.2: lookup reported source from (A) in (X,Y) */
579 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
584 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
585 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
587 /* E.4: if not found, create source with timer=GMI:
589 assert(!source
->t_source_timer
); /* timer == 0 */
590 igmp_source_reset_gmi(group
, source
);
591 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
594 } /* scan received sources */
597 * If we are in isexcl mode and num_sources == 0
598 * than that means we have a *,g entry that
599 * needs to be handled
601 if (group
->group_filtermode_isexcl
&& num_sources
== 0) {
602 struct in_addr star
= {.s_addr
= INADDR_ANY
};
603 source
= igmp_find_source_by_addr(group
, star
);
605 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
606 igmp_source_reset_gmi(group
, source
);
610 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
611 source_delete_by_flag(group
->group_source_list
);
614 static void isex_incl(struct gm_group
*group
, int num_sources
,
615 struct in_addr
*sources
)
620 assert(!group
->group_filtermode_isexcl
);
622 /* I.1: set deletion flag for known sources (A) */
623 source_mark_delete_flag(group
);
625 /* scan received sources (B) */
626 for (i
= 0; i
< num_sources
; ++i
) {
627 struct gm_source
*source
;
628 struct in_addr
*src_addr
;
631 src_addr
= sources
+ i
;
633 /* I.2: lookup reported source (B) */
634 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
639 /* I.3: if found, clear deletion flag (A*B) */
640 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
642 /* I.4: if not found, create source with timer=0 (B-A)
644 assert(!source
->t_source_timer
); /* (B-A) timer=0 */
647 } /* scan received sources */
649 /* I.5: delete all sources marked with deletion flag (A-B) */
650 source_delete_by_flag(group
->group_source_list
);
652 group
->group_filtermode_isexcl
= 1; /* boolean=true */
654 assert(group
->group_filtermode_isexcl
);
656 group_exclude_fwd_anysrc_ifempty(group
);
659 void igmpv3_report_isex(struct gm_sock
*igmp
, struct in_addr from
,
660 struct in_addr group_addr
, int num_sources
,
661 struct in_addr
*sources
, int from_igmp_v2_report
)
663 struct interface
*ifp
= igmp
->interface
;
664 struct gm_group
*group
;
666 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
668 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
671 /* non-existant group is created as INCLUDE {empty} */
672 group
= igmp_add_group_by_addr(igmp
, group_addr
);
677 /* So we can display how we learned the group in our show command output
679 if (from_igmp_v2_report
)
680 group
->igmp_version
= 2;
682 if (group
->group_filtermode_isexcl
) {
684 isex_excl(group
, num_sources
, sources
);
687 isex_incl(group
, num_sources
, sources
);
688 assert(group
->group_filtermode_isexcl
);
691 assert(group
->group_filtermode_isexcl
);
693 igmp_group_reset_gmi(group
);
696 static void toin_incl(struct gm_group
*group
, int num_sources
,
697 struct in_addr
*sources
)
699 int num_sources_tosend
= listcount(group
->group_source_list
);
702 /* Set SEND flag for all known sources (A) */
703 source_mark_send_flag(group
);
705 /* Scan received sources (B) */
706 for (i
= 0; i
< num_sources
; ++i
) {
707 struct gm_source
*source
;
708 struct in_addr
*src_addr
;
711 src_addr
= sources
+ i
;
713 /* Lookup reported source (B) */
714 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
719 /* If found, clear SEND flag (A*B) */
720 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
721 --num_sources_tosend
;
725 igmp_source_reset_gmi(group
, source
);
728 /* Send sources marked with SEND flag: Q(G,A-B) */
729 if (num_sources_tosend
> 0) {
730 source_query_send_by_flag(group
, num_sources_tosend
);
734 static void toin_excl(struct gm_group
*group
, int num_sources
,
735 struct in_addr
*sources
)
737 int num_sources_tosend
;
740 /* Set SEND flag for X (sources with timer > 0) */
741 num_sources_tosend
= source_mark_send_flag_by_timer(group
);
743 /* Scan received sources (A) */
744 for (i
= 0; i
< num_sources
; ++i
) {
745 struct gm_source
*source
;
746 struct in_addr
*src_addr
;
749 src_addr
= sources
+ i
;
751 /* Lookup reported source (A) */
752 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
756 if (source
->t_source_timer
) {
757 /* If found and timer running, clear SEND flag
759 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
760 --num_sources_tosend
;
764 igmp_source_reset_gmi(group
, source
);
767 /* Send sources marked with SEND flag: Q(G,X-A) */
768 if (num_sources_tosend
> 0) {
769 source_query_send_by_flag(group
, num_sources_tosend
);
773 group_query_send(group
);
776 void igmpv3_report_toin(struct gm_sock
*igmp
, struct in_addr from
,
777 struct in_addr group_addr
, int num_sources
,
778 struct in_addr
*sources
)
780 struct interface
*ifp
= igmp
->interface
;
781 struct gm_group
*group
;
783 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
786 * If the requested filter mode is INCLUDE *and* the requested source
787 * list is empty, then the entry corresponding to the requested
788 * interface and multicast address is deleted if present. If no such
789 * entry is present, the request is ignored.
792 /* non-existant group is created as INCLUDE {empty} */
793 group
= igmp_add_group_by_addr(igmp
, group_addr
);
798 group
= find_group_by_addr(igmp
, group_addr
);
803 if (group
->group_filtermode_isexcl
) {
805 toin_excl(group
, num_sources
, sources
);
808 toin_incl(group
, num_sources
, sources
);
812 static void toex_incl(struct gm_group
*group
, int num_sources
,
813 struct in_addr
*sources
)
815 int num_sources_tosend
= 0;
818 assert(!group
->group_filtermode_isexcl
);
820 /* Set DELETE flag for all known sources (A) */
821 source_mark_delete_flag(group
);
823 /* Clear off SEND flag from all known sources (A) */
824 source_clear_send_flag(group
->group_source_list
);
826 /* Scan received sources (B) */
827 for (i
= 0; i
< num_sources
; ++i
) {
828 struct gm_source
*source
;
829 struct in_addr
*src_addr
;
832 src_addr
= sources
+ i
;
834 /* Lookup reported source (B) */
835 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
837 /* If found, clear deletion flag: (A*B) */
838 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
839 /* and set SEND flag (A*B) */
840 IGMP_SOURCE_DO_SEND(source
->source_flags
);
841 ++num_sources_tosend
;
844 } /* Scan received sources (B) */
846 group
->group_filtermode_isexcl
= 1; /* boolean=true */
848 /* Delete all sources marked with DELETE flag (A-B) */
849 source_delete_by_flag(group
->group_source_list
);
851 /* Send sources marked with SEND flag: Q(G,A*B) */
852 if (num_sources_tosend
> 0) {
853 source_query_send_by_flag(group
, num_sources_tosend
);
856 assert(group
->group_filtermode_isexcl
);
858 group_exclude_fwd_anysrc_ifempty(group
);
861 static void toex_excl(struct gm_group
*group
, int num_sources
,
862 struct in_addr
*sources
)
864 int num_sources_tosend
= 0;
867 /* set DELETE flag for all known sources (X,Y) */
868 source_mark_delete_flag(group
);
870 /* clear off SEND flag from all known sources (X,Y) */
871 source_clear_send_flag(group
->group_source_list
);
873 if (num_sources
== 0) {
874 struct gm_source
*source
;
875 struct in_addr any
= {.s_addr
= INADDR_ANY
};
877 source
= igmp_find_source_by_addr(group
, any
);
879 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
882 /* scan received sources (A) */
883 for (i
= 0; i
< num_sources
; ++i
) {
884 struct gm_source
*source
;
885 struct in_addr
*src_addr
;
888 src_addr
= sources
+ i
;
890 /* lookup reported source (A) in known sources (X,Y) */
891 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
896 /* if found, clear off DELETE flag from reported source
898 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
900 /* if not found, create source with Group Timer:
901 * (A-X-Y)=Group Timer */
902 long group_timer_msec
;
904 assert(!source
->t_source_timer
); /* timer == 0 */
905 group_timer_msec
= igmp_group_timer_remain_msec(group
);
906 igmp_source_timer_on(group
, source
, group_timer_msec
);
907 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
909 /* make sure source is created with DELETE flag unset */
910 assert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
913 /* make sure reported source has DELETE flag unset */
914 assert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
916 if (source
->t_source_timer
) {
917 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
918 IGMP_SOURCE_DO_SEND(source
->source_flags
);
919 ++num_sources_tosend
;
922 } /* scan received sources (A) */
925 delete all sources marked with DELETE flag:
929 source_delete_by_flag(group
->group_source_list
);
931 /* send sources marked with SEND flag: Q(G,A-Y) */
932 if (num_sources_tosend
> 0) {
933 source_query_send_by_flag(group
, num_sources_tosend
);
937 void igmpv3_report_toex(struct gm_sock
*igmp
, struct in_addr from
,
938 struct in_addr group_addr
, int num_sources
,
939 struct in_addr
*sources
)
941 struct interface
*ifp
= igmp
->interface
;
942 struct gm_group
*group
;
944 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
946 /* non-existant group is created as INCLUDE {empty} */
947 group
= igmp_add_group_by_addr(igmp
, group_addr
);
952 if (group
->group_filtermode_isexcl
) {
954 toex_excl(group
, num_sources
, sources
);
957 toex_incl(group
, num_sources
, sources
);
958 assert(group
->group_filtermode_isexcl
);
960 assert(group
->group_filtermode_isexcl
);
962 /* Group Timer=GMI */
963 igmp_group_reset_gmi(group
);
966 void igmpv3_report_allow(struct gm_sock
*igmp
, struct in_addr from
,
967 struct in_addr group_addr
, int num_sources
,
968 struct in_addr
*sources
)
970 on_trace(__func__
, igmp
->interface
, from
, group_addr
, num_sources
,
973 allow(igmp
, from
, group_addr
, num_sources
, sources
);
976 static void igmp_send_query_group(struct gm_group
*group
, char *query_buf
,
977 size_t query_buf_size
, int num_sources
,
980 struct interface
*ifp
= group
->interface
;
981 struct pim_interface
*pim_ifp
= ifp
->info
;
982 struct gm_sock
*igmp
;
983 struct listnode
*sock_node
;
985 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
987 pim_ifp
->igmp_version
, group
, igmp
->fd
, ifp
->name
,
988 query_buf
, query_buf_size
, num_sources
,
989 group
->group_addr
, group
->group_addr
,
990 pim_ifp
->gm_specific_query_max_response_time_dsec
,
991 s_flag
, igmp
->querier_robustness_variable
,
992 igmp
->querier_query_interval
);
997 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
999 When transmitting a group specific query, if the group timer is
1000 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1001 in the query message.
1003 static void group_retransmit_group(struct gm_group
*group
)
1005 struct pim_interface
*pim_ifp
;
1006 long lmqc
; /* Last Member Query Count */
1007 long lmqi_msec
; /* Last Member Query Interval */
1008 long lmqt_msec
; /* Last Member Query Time */
1012 pim_ifp
= group
->interface
->info
;
1014 if (pim_ifp
->igmp_version
== 3) {
1015 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1017 query_buf_size
= IGMP_V12_MSG_SIZE
;
1020 char query_buf
[query_buf_size
];
1022 lmqc
= pim_ifp
->gm_last_member_query_count
;
1023 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1024 lmqt_msec
= lmqc
* lmqi_msec
;
1027 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1029 When transmitting a group specific query, if the group timer is
1030 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1031 in the query message.
1033 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1035 if (PIM_DEBUG_IGMP_TRACE
) {
1036 char group_str
[INET_ADDRSTRLEN
];
1037 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1040 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1041 group_str
, group
->interface
->name
, s_flag
,
1042 group
->group_specific_query_retransmit_count
);
1046 RFC3376: 4.1.12. IP Destination Addresses for Queries
1048 Group-Specific and Group-and-Source-Specific Queries are sent with
1049 an IP destination address equal to the multicast address of
1053 igmp_send_query_group(group
, query_buf
, sizeof(query_buf
), 0, s_flag
);
1057 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1059 When building a group and source specific query for a group G, two
1060 separate query messages are sent for the group. The first one has
1061 the "Suppress Router-Side Processing" bit set and contains all the
1062 sources with retransmission state and timers greater than LMQT. The
1063 second has the "Suppress Router-Side Processing" bit clear and
1064 contains all the sources with retransmission state and timers lower
1065 or equal to LMQT. If either of the two calculated messages does not
1066 contain any sources, then its transmission is suppressed.
1068 static int group_retransmit_sources(struct gm_group
*group
,
1069 int send_with_sflag_set
)
1071 struct pim_interface
*pim_ifp
;
1072 long lmqc
; /* Last Member Query Count */
1073 long lmqi_msec
; /* Last Member Query Interval */
1074 long lmqt_msec
; /* Last Member Query Time */
1075 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1076 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1077 int query_buf1_max_sources
;
1078 int query_buf2_max_sources
;
1079 struct in_addr
*source_addr1
;
1080 struct in_addr
*source_addr2
;
1081 int num_sources_tosend1
;
1082 int num_sources_tosend2
;
1083 struct listnode
*src_node
;
1084 struct gm_source
*src
;
1085 int num_retransmit_sources_left
= 0;
1087 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1088 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1090 pim_ifp
= group
->interface
->info
;
1092 lmqc
= pim_ifp
->gm_last_member_query_count
;
1093 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1094 lmqt_msec
= lmqc
* lmqi_msec
;
1096 /* Scan all group sources */
1097 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1099 /* Source has retransmission state? */
1100 if (src
->source_query_retransmit_count
< 1)
1103 if (--src
->source_query_retransmit_count
> 0) {
1104 ++num_retransmit_sources_left
;
1107 /* Copy source address into appropriate query buffer */
1108 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1109 *source_addr1
= src
->source_addr
;
1112 *source_addr2
= src
->source_addr
;
1117 num_sources_tosend1
=
1119 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1120 num_sources_tosend2
=
1122 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1124 if (PIM_DEBUG_IGMP_TRACE
) {
1125 char group_str
[INET_ADDRSTRLEN
];
1126 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1129 "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",
1130 group_str
, group
->interface
->name
, num_sources_tosend1
,
1131 num_sources_tosend2
, send_with_sflag_set
,
1132 num_retransmit_sources_left
);
1135 if (num_sources_tosend1
> 0) {
1137 Send group-and-source-specific query with s_flag set and all
1138 sources with timers greater than LMQT.
1141 if (send_with_sflag_set
) {
1143 query_buf1_max_sources
=
1144 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1146 if (num_sources_tosend1
> query_buf1_max_sources
) {
1147 char group_str
[INET_ADDRSTRLEN
];
1148 pim_inet4_dump("<group?>", group
->group_addr
,
1149 group_str
, sizeof(group_str
));
1151 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1152 __func__
, group_str
,
1153 group
->interface
->name
,
1154 num_sources_tosend1
, sizeof(query_buf1
),
1155 query_buf1_max_sources
);
1158 RFC3376: 4.1.12. IP Destination Addresses for
1161 Group-Specific and Group-and-Source-Specific
1162 Queries are sent with
1163 an IP destination address equal to the
1164 multicast address of
1168 igmp_send_query_group(
1169 group
, query_buf1
, sizeof(query_buf1
),
1170 num_sources_tosend1
, 1 /* s_flag */);
1173 } /* send_with_sflag_set */
1176 if (num_sources_tosend2
> 0) {
1178 Send group-and-source-specific query with s_flag clear and all
1179 sources with timers lower or equal to LMQT.
1182 query_buf2_max_sources
=
1183 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1184 if (num_sources_tosend2
> query_buf2_max_sources
) {
1185 char group_str
[INET_ADDRSTRLEN
];
1186 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1189 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1190 __func__
, group_str
, group
->interface
->name
,
1191 num_sources_tosend2
, sizeof(query_buf2
),
1192 query_buf2_max_sources
);
1195 RFC3376: 4.1.12. IP Destination Addresses for Queries
1197 Group-Specific and Group-and-Source-Specific Queries
1199 an IP destination address equal to the multicast
1204 igmp_send_query_group(
1205 group
, query_buf2
, sizeof(query_buf2
),
1206 num_sources_tosend2
, 0 /* s_flag */);
1210 return num_retransmit_sources_left
;
1213 static void igmp_group_retransmit(struct thread
*t
)
1215 struct gm_group
*group
;
1216 int num_retransmit_sources_left
;
1217 int send_with_sflag_set
; /* boolean */
1219 group
= THREAD_ARG(t
);
1221 if (PIM_DEBUG_IGMP_TRACE
) {
1222 char group_str
[INET_ADDRSTRLEN
];
1223 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1225 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1226 group
->interface
->name
);
1229 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1230 if (group
->group_specific_query_retransmit_count
> 0) {
1232 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1233 group_retransmit_group(group
);
1234 --group
->group_specific_query_retransmit_count
;
1238 If a group specific query is scheduled to be transmitted at
1240 same time as a group and source specific query for the same
1242 then transmission of the group and source specific message
1244 "Suppress Router-Side Processing" bit set may be suppressed.
1246 send_with_sflag_set
= 0; /* boolean=false */
1248 send_with_sflag_set
= 1; /* boolean=true */
1251 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1252 num_retransmit_sources_left
=
1253 group_retransmit_sources(group
, send_with_sflag_set
);
1256 Keep group retransmit timer running if there is any retransmit
1259 if ((num_retransmit_sources_left
> 0)
1260 || (group
->group_specific_query_retransmit_count
> 0)) {
1261 group_retransmit_timer_on(group
);
1266 group_retransmit_timer_on:
1267 if group retransmit timer isn't running, starts it;
1268 otherwise, do nothing
1270 static void group_retransmit_timer_on(struct gm_group
*group
)
1272 struct pim_interface
*pim_ifp
;
1273 long lmqi_msec
; /* Last Member Query Interval */
1275 /* if group retransmit timer is running, do nothing */
1276 if (group
->t_group_query_retransmit_timer
) {
1280 pim_ifp
= group
->interface
->info
;
1282 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1284 if (PIM_DEBUG_IGMP_TRACE
) {
1285 char group_str
[INET_ADDRSTRLEN
];
1286 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1289 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1290 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1291 group
->interface
->name
);
1294 thread_add_timer_msec(router
->master
, igmp_group_retransmit
, group
,
1296 &group
->t_group_query_retransmit_timer
);
1299 static long igmp_group_timer_remain_msec(struct gm_group
*group
)
1301 return pim_time_timer_remain_msec(group
->t_group_timer
);
1304 static long igmp_source_timer_remain_msec(struct gm_source
*source
)
1306 return pim_time_timer_remain_msec(source
->t_source_timer
);
1310 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1312 static void group_query_send(struct gm_group
*group
)
1314 struct pim_interface
*pim_ifp
;
1315 long lmqc
; /* Last Member Query Count */
1317 pim_ifp
= group
->interface
->info
;
1318 lmqc
= pim_ifp
->gm_last_member_query_count
;
1320 /* lower group timer to lmqt */
1321 igmp_group_timer_lower_to_lmqt(group
);
1323 /* reset retransmission counter */
1324 group
->group_specific_query_retransmit_count
= lmqc
;
1326 /* immediately send group specific query (decrease retransmit counter by
1328 group_retransmit_group(group
);
1330 /* make sure group retransmit timer is running */
1331 group_retransmit_timer_on(group
);
1335 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1337 static void source_query_send_by_flag(struct gm_group
*group
,
1338 int num_sources_tosend
)
1340 struct pim_interface
*pim_ifp
;
1341 struct listnode
*src_node
;
1342 struct gm_source
*src
;
1343 long lmqc
; /* Last Member Query Count */
1344 long lmqi_msec
; /* Last Member Query Interval */
1345 long lmqt_msec
; /* Last Member Query Time */
1347 assert(num_sources_tosend
> 0);
1349 pim_ifp
= group
->interface
->info
;
1351 lmqc
= pim_ifp
->gm_last_member_query_count
;
1352 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1353 lmqt_msec
= lmqc
* lmqi_msec
;
1356 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1359 (...) for each of the sources in X of group G, with source timer
1362 o Set number of retransmissions for each source to [Last Member
1364 o Lower source timer to LMQT.
1366 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1367 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1368 /* source "src" in X of group G */
1369 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1370 src
->source_query_retransmit_count
= lmqc
;
1371 igmp_source_timer_lower_to_lmqt(src
);
1376 /* send group-and-source specific queries */
1377 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1379 /* make sure group retransmit timer is running */
1380 group_retransmit_timer_on(group
);
1383 static void block_excl(struct gm_group
*group
, int num_sources
,
1384 struct in_addr
*sources
)
1386 int num_sources_tosend
= 0;
1389 /* 1. clear off SEND flag from all known sources (X,Y) */
1390 source_clear_send_flag(group
->group_source_list
);
1392 /* 2. scan received sources (A) */
1393 for (i
= 0; i
< num_sources
; ++i
) {
1394 struct gm_source
*source
;
1395 struct in_addr
*src_addr
;
1398 src_addr
= sources
+ i
;
1400 /* lookup reported source (A) in known sources (X,Y) */
1401 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
1406 /* 3: if not found, create source with Group Timer:
1407 * (A-X-Y)=Group Timer */
1408 long group_timer_msec
;
1410 assert(!source
->t_source_timer
); /* timer == 0 */
1411 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1412 igmp_source_timer_on(group
, source
, group_timer_msec
);
1413 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1416 if (source
->t_source_timer
) {
1417 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1418 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1419 ++num_sources_tosend
;
1423 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1424 if (num_sources_tosend
> 0) {
1425 source_query_send_by_flag(group
, num_sources_tosend
);
1429 static void block_incl(struct gm_group
*group
, int num_sources
,
1430 struct in_addr
*sources
)
1432 int num_sources_tosend
= 0;
1435 /* 1. clear off SEND flag from all known sources (B) */
1436 source_clear_send_flag(group
->group_source_list
);
1438 /* 2. scan received sources (A) */
1439 for (i
= 0; i
< num_sources
; ++i
) {
1440 struct gm_source
*source
;
1441 struct in_addr
*src_addr
;
1443 src_addr
= sources
+ i
;
1445 /* lookup reported source (A) in known sources (B) */
1446 source
= igmp_find_source_by_addr(group
, *src_addr
);
1448 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1449 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1450 ++num_sources_tosend
;
1454 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1455 if (num_sources_tosend
> 0) {
1456 source_query_send_by_flag(group
, num_sources_tosend
);
1460 void igmpv3_report_block(struct gm_sock
*igmp
, struct in_addr from
,
1461 struct in_addr group_addr
, int num_sources
,
1462 struct in_addr
*sources
)
1464 struct interface
*ifp
= igmp
->interface
;
1465 struct gm_group
*group
;
1467 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
1469 /* non-existant group is created as INCLUDE {empty} */
1470 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1475 if (group
->group_filtermode_isexcl
) {
1477 block_excl(group
, num_sources
, sources
);
1480 block_incl(group
, num_sources
, sources
);
1484 void igmp_group_timer_lower_to_lmqt(struct gm_group
*group
)
1486 struct interface
*ifp
;
1487 struct pim_interface
*pim_ifp
;
1489 int lmqi_dsec
; /* Last Member Query Interval */
1490 int lmqc
; /* Last Member Query Count */
1491 int lmqt_msec
; /* Last Member Query Time */
1494 RFC 3376: 6.2.2. Definition of Group Timers
1496 The group timer is only used when a group is in EXCLUDE mode and
1497 it represents the time for the *filter-mode* of the group to
1498 expire and switch to INCLUDE mode.
1500 if (!group
->group_filtermode_isexcl
) {
1504 ifp
= group
->interface
;
1505 pim_ifp
= ifp
->info
;
1508 lmqi_dsec
= pim_ifp
->gm_specific_query_max_response_time_dsec
;
1509 lmqc
= pim_ifp
->gm_last_member_query_count
;
1510 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1511 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1513 if (PIM_DEBUG_IGMP_TRACE
) {
1514 char group_str
[INET_ADDRSTRLEN
];
1515 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1518 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1519 __func__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1523 assert(group
->group_filtermode_isexcl
);
1525 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1528 void igmp_source_timer_lower_to_lmqt(struct gm_source
*source
)
1530 struct gm_group
*group
;
1531 struct interface
*ifp
;
1532 struct pim_interface
*pim_ifp
;
1534 int lmqi_dsec
; /* Last Member Query Interval */
1535 int lmqc
; /* Last Member Query Count */
1536 int lmqt_msec
; /* Last Member Query Time */
1538 group
= source
->source_group
;
1539 ifp
= group
->interface
;
1540 pim_ifp
= ifp
->info
;
1543 lmqi_dsec
= pim_ifp
->gm_specific_query_max_response_time_dsec
;
1544 lmqc
= pim_ifp
->gm_last_member_query_count
;
1545 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1546 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1548 if (PIM_DEBUG_IGMP_TRACE
) {
1549 char group_str
[INET_ADDRSTRLEN
];
1550 char source_str
[INET_ADDRSTRLEN
];
1551 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1553 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
1554 sizeof(source_str
));
1556 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1557 __func__
, group_str
, source_str
, ifname
, lmqc
,
1558 lmqi_dsec
, lmqt_msec
);
1561 igmp_source_timer_on(group
, source
, lmqt_msec
);
1564 void igmp_v3_send_query(struct gm_group
*group
, int fd
, const char *ifname
,
1565 char *query_buf
, int query_buf_size
, int num_sources
,
1566 struct in_addr dst_addr
, struct in_addr group_addr
,
1567 int query_max_response_time_dsec
, uint8_t s_flag
,
1568 uint8_t querier_robustness_variable
,
1569 uint16_t querier_query_interval
)
1572 uint8_t max_resp_code
;
1575 struct sockaddr_in to
;
1579 assert(num_sources
>= 0);
1581 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1582 if (msg_size
> query_buf_size
) {
1585 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1586 __FILE__
, __func__
, msg_size
, query_buf_size
);
1590 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1591 assert((s_flag
== 0) || (s_flag
== 1));
1593 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1594 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1597 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1599 If non-zero, the QRV field contains the [Robustness Variable]
1600 value used by the querier, i.e., the sender of the Query. If the
1601 querier's [Robustness Variable] exceeds 7, the maximum value of
1602 the QRV field, the QRV is set to zero.
1604 if (querier_robustness_variable
> 7) {
1605 querier_robustness_variable
= 0;
1608 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1609 query_buf
[1] = max_resp_code
;
1610 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
1611 0; /* for computing checksum */
1612 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
1614 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1615 query_buf
[9] = qqic
;
1616 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) =
1619 checksum
= in_cksum(query_buf
, msg_size
);
1620 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1622 if (PIM_DEBUG_IGMP_PACKETS
) {
1623 char dst_str
[INET_ADDRSTRLEN
];
1624 char group_str
[INET_ADDRSTRLEN
];
1625 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1626 pim_inet4_dump("<group?>", group_addr
, group_str
,
1629 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1630 dst_str
, ifname
, group_str
, num_sources
, msg_size
,
1631 s_flag
, querier_robustness_variable
,
1632 querier_query_interval
, qqic
);
1635 memset(&to
, 0, sizeof(to
));
1636 to
.sin_family
= AF_INET
;
1637 to
.sin_addr
= dst_addr
;
1640 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1641 (struct sockaddr
*)&to
, tolen
);
1642 if (sent
!= (ssize_t
)msg_size
) {
1643 char dst_str
[INET_ADDRSTRLEN
];
1644 char group_str
[INET_ADDRSTRLEN
];
1645 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1646 pim_inet4_dump("<group?>", group_addr
, group_str
,
1650 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1651 dst_str
, ifname
, group_str
, msg_size
, errno
,
1652 safe_strerror(errno
));
1655 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1656 dst_str
, ifname
, group_str
, msg_size
, sent
);
1662 s_flag sanity test: s_flag must be set for general queries
1664 RFC 3376: 6.6.1. Timer Updates
1666 When a router sends or receives a query with a clear Suppress
1667 Router-Side Processing flag, it must update its timers to reflect
1668 the correct timeout values for the group or sources being queried.
1670 General queries don't trigger timer update.
1673 /* general query? */
1674 if (group_addr
.s_addr
== INADDR_ANY
) {
1675 char dst_str
[INET_ADDRSTRLEN
];
1676 char group_str
[INET_ADDRSTRLEN
];
1677 pim_inet4_dump("<dst?>", dst_addr
, dst_str
,
1679 pim_inet4_dump("<group?>", group_addr
, group_str
,
1682 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1683 __func__
, dst_str
, ifname
, group_str
,
1689 void igmp_v3_recv_query(struct gm_sock
*igmp
, const char *from_str
,
1692 struct interface
*ifp
;
1693 struct pim_interface
*pim_ifp
;
1694 struct in_addr group_addr
;
1695 uint8_t resv_s_qrv
= 0;
1700 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1701 ifp
= igmp
->interface
;
1702 pim_ifp
= ifp
->info
;
1705 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1707 * Routers adopt the QRV value from the most recently received Query
1708 * as their own [Robustness Variable] value, unless that most
1709 * recently received QRV was zero, in which case the receivers use
1710 * the default [Robustness Variable] value specified in section 8.1
1711 * or a statically configured value.
1713 resv_s_qrv
= igmp_msg
[8];
1714 qrv
= 7 & resv_s_qrv
;
1715 igmp
->querier_robustness_variable
=
1716 qrv
? qrv
: pim_ifp
->gm_default_robustness_variable
;
1719 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1721 * Multicast routers that are not the current querier adopt the QQI
1722 * value from the most recently received Query as their own [Query
1723 * Interval] value, unless that most recently received QQI was zero,
1724 * in which case the receiving routers use the default.
1726 if (igmp
->t_other_querier_timer
) {
1727 /* other querier present */
1731 qqi
= igmp_msg_decode8to16(qqic
);
1732 igmp
->querier_query_interval
=
1733 qqi
? qqi
: pim_ifp
->gm_default_query_interval
;
1735 if (PIM_DEBUG_IGMP_TRACE
) {
1736 char ifaddr_str
[INET_ADDRSTRLEN
];
1737 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
1738 sizeof(ifaddr_str
));
1740 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1742 qqi
? "recv-non-default" : "default",
1743 igmp
->querier_query_interval
, qqic
, from_str
);
1748 * RFC 3376: 6.6.1. Timer Updates
1750 * When a router sends or receives a query with a clear Suppress
1751 * Router-Side Processing flag, it must update its timers to reflect
1752 * the correct timeout values for the group or sources being queried.
1754 * General queries don't trigger timer update.
1756 s_flag
= (1 << 3) & resv_s_qrv
;
1759 /* s_flag is clear */
1761 if (group_addr
.s_addr
== INADDR_ANY
) {
1762 /* this is a general query */
1763 /* log that general query should have the s_flag set */
1765 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1766 from_str
, ifp
->name
);
1768 struct gm_group
*group
;
1770 /* this is a non-general query: perform timer updates */
1772 group
= find_group_by_addr(igmp
, group_addr
);
1774 int recv_num_sources
= ntohs(*(
1777 + IGMP_V3_NUMSOURCES_OFFSET
));
1780 * RFC 3376: 6.6.1. Timer Updates
1781 * Query Q(G,A): Source Timer for sources in A
1782 * are lowered to LMQT
1783 * Query Q(G): Group Timer is lowered to LMQT
1785 if (recv_num_sources
< 1) {
1786 /* Query Q(G): Group Timer is lowered to
1789 igmp_group_timer_lower_to_lmqt(group
);
1791 /* Query Q(G,A): Source Timer for
1792 * sources in A are lowered to LMQT */
1794 /* Scan sources in query and lower their
1796 struct in_addr
*sources
=
1799 + IGMP_V3_SOURCES_OFFSET
);
1800 for (i
= 0; i
< recv_num_sources
; ++i
) {
1801 struct in_addr src_addr
;
1802 struct gm_source
*src
;
1803 memcpy(&src_addr
, sources
+ i
,
1804 sizeof(struct in_addr
));
1805 src
= igmp_find_source_by_addr(
1808 igmp_source_timer_lower_to_lmqt(
1814 char group_str
[INET_ADDRSTRLEN
];
1815 pim_inet4_dump("<group?>", group_addr
,
1816 group_str
, sizeof(group_str
));
1818 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1819 from_str
, ifp
->name
, group_str
);
1822 } /* s_flag is clear: timer updates */
1825 int igmp_v3_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
1826 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1829 uint8_t *group_record
;
1830 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1831 struct interface
*ifp
= igmp
->interface
;
1834 struct pim_interface
*pim_ifp
;
1836 if (igmp
->mtrace_only
)
1839 pim_ifp
= igmp
->interface
->info
;
1841 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1843 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1844 from_str
, ifp
->name
, igmp_msg_len
,
1845 IGMP_V3_MSG_MIN_SIZE
);
1849 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
1851 "Recv IGMPv3 report from %s on %s with invalid checksum",
1852 from_str
, ifp
->name
);
1856 /* Collecting IGMP Rx stats */
1857 igmp
->rx_stats
.report_v3
++;
1860 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1861 if (num_groups
< 1) {
1863 "Recv IGMP report v3 from %s on %s: missing group records",
1864 from_str
, ifp
->name
);
1868 if (PIM_DEBUG_IGMP_PACKETS
) {
1870 "Recv IGMP report v3 from %s on %s: size=%d groups=%d",
1871 from_str
, ifp
->name
, igmp_msg_len
, num_groups
);
1874 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1877 for (i
= 0; i
< num_groups
; ++i
) {
1878 struct in_addr rec_group
;
1883 int rec_num_sources
;
1887 bool filtered
= false;
1889 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1892 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1893 from_str
, ifp
->name
);
1897 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1899 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1900 rec_num_sources
= ntohs(*(
1901 uint16_t *)(group_record
1902 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1905 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1906 sizeof(struct in_addr
));
1908 if (PIM_DEBUG_IGMP_PACKETS
) {
1910 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
1911 from_str
, ifp
->name
, i
, rec_type
,
1912 rec_auxdatalen
, rec_num_sources
,
1918 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1920 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1922 if ((src
+ 4) > report_pastend
) {
1924 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1925 from_str
, ifp
->name
);
1929 if (PIM_DEBUG_IGMP_PACKETS
) {
1932 if (!inet_ntop(AF_INET
, src
, src_str
,
1934 snprintf(src_str
, sizeof(src_str
),
1938 " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s",
1939 from_str
, ifp
->name
, i
,
1940 &rec_group
, src_str
);
1942 } /* for (sources) */
1945 lncb
.family
= AF_INET
;
1946 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1947 lncb
.prefixlen
= 24;
1950 g
.u
.prefix4
= rec_group
;
1951 g
.prefixlen
= IPV4_MAX_BITLEN
;
1953 /* determine filtering status for group */
1954 filtered
= pim_is_group_filtered(ifp
->info
, &rec_group
);
1956 if (PIM_DEBUG_IGMP_PACKETS
&& filtered
)
1958 "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
1959 &rec_group
, from_str
, ifp
->name
,
1960 pim_ifp
->boundary_oil_plist
);
1963 * If we receive a igmp report with the group in 224.0.0.0/24
1964 * then we should ignore it
1966 if (prefix_match(&lncb
, &g
))
1969 if (!local_ncb
&& !filtered
)
1971 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1972 igmpv3_report_isin(igmp
, from
, rec_group
,
1974 (struct in_addr
*)sources
);
1976 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1978 igmp
, from
, rec_group
, rec_num_sources
,
1979 (struct in_addr
*)sources
, 0);
1981 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
1982 igmpv3_report_toin(igmp
, from
, rec_group
,
1984 (struct in_addr
*)sources
);
1986 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
1987 igmpv3_report_toex(igmp
, from
, rec_group
,
1989 (struct in_addr
*)sources
);
1991 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
1992 igmpv3_report_allow(igmp
, from
, rec_group
,
1994 (struct in_addr
*)sources
);
1996 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
1997 igmpv3_report_block(igmp
, from
, rec_group
,
1999 (struct in_addr
*)sources
);
2003 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2004 from_str
, ifp
->name
, rec_type
);
2008 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2011 } /* for (group records) */