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_instance.h"
28 #include "pim_iface.h"
30 #include "pim_igmpv3.h"
34 #include "pim_zebra.h"
38 static void group_retransmit_timer_on(struct gm_group
*group
);
39 static long igmp_group_timer_remain_msec(struct gm_group
*group
);
40 static long igmp_source_timer_remain_msec(struct gm_source
*source
);
41 static void group_query_send(struct gm_group
*group
);
42 static void source_query_send_by_flag(struct gm_group
*group
,
43 int num_sources_tosend
);
45 static void on_trace(const char *label
, struct interface
*ifp
,
46 struct in_addr from
, struct in_addr group_addr
,
47 int num_sources
, struct in_addr
*sources
)
49 if (PIM_DEBUG_IGMP_TRACE
) {
50 char from_str
[INET_ADDRSTRLEN
];
51 char group_str
[INET_ADDRSTRLEN
];
53 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
54 pim_inet4_dump("<group?>", group_addr
, group_str
,
57 zlog_debug("%s: from %s on %s: group=%s sources=%d", label
,
58 from_str
, ifp
->name
, group_str
, num_sources
);
62 static inline long igmp_gmi_msec(struct gm_group
*group
)
64 struct pim_interface
*pim_ifp
= group
->interface
->info
;
66 struct listnode
*sock_node
;
68 long qrv
= 0, qqi
= 0;
70 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
71 qrv
= MAX(qrv
, igmp
->querier_robustness_variable
);
72 qqi
= MAX(qqi
, igmp
->querier_query_interval
);
74 return PIM_IGMP_GMI_MSEC(qrv
, qqi
,
75 pim_ifp
->gm_query_max_response_time_dsec
);
78 void igmp_group_reset_gmi(struct gm_group
*group
)
80 long group_membership_interval_msec
;
81 struct interface
*ifp
;
83 ifp
= group
->interface
;
86 RFC 3376: 8.4. Group Membership Interval
88 The Group Membership Interval is the amount of time that must pass
89 before a multicast router decides there are no more members of a
90 group or a particular source on a network.
92 This value MUST be ((the Robustness Variable) times (the Query
93 Interval)) plus (one Query Response Interval).
95 group_membership_interval_msec = querier_robustness_variable *
96 (1000 * querier_query_interval) +
97 100 * query_response_interval_dsec;
99 group_membership_interval_msec
= igmp_gmi_msec(group
);
101 if (PIM_DEBUG_IGMP_TRACE
) {
102 char group_str
[INET_ADDRSTRLEN
];
103 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
106 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
107 group_str
, group_membership_interval_msec
/ 1000,
108 group_membership_interval_msec
% 1000, ifp
->name
);
112 RFC 3376: 6.2.2. Definition of Group Timers
114 The group timer is only used when a group is in EXCLUDE mode and
115 it represents the time for the *filter-mode* of the group to
116 expire and switch to INCLUDE mode.
118 assert(group
->group_filtermode_isexcl
);
120 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
123 static void igmp_source_timer(struct thread
*t
)
125 struct gm_source
*source
;
126 struct gm_group
*group
;
128 source
= THREAD_ARG(t
);
130 group
= source
->source_group
;
132 if (PIM_DEBUG_IGMP_TRACE
) {
133 char group_str
[INET_ADDRSTRLEN
];
134 char source_str
[INET_ADDRSTRLEN
];
135 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
137 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
140 "%s: Source timer expired for group %s source %s on %s",
141 __func__
, group_str
, source_str
,
142 group
->interface
->name
);
146 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
149 Filter-Mode Source Timer Value Action
150 ----------- ------------------ ------
151 INCLUDE TIMER == 0 Suggest to stop forwarding
152 traffic from source and
153 remove source record. If
154 there are no more source
155 records for the group, delete
158 EXCLUDE TIMER == 0 Suggest to not forward
160 (DO NOT remove record)
162 Source timer switched from (T > 0) to (T == 0): disable forwarding.
165 if (group
->group_filtermode_isexcl
) {
168 igmp_source_forward_stop(source
);
172 /* igmp_source_delete() will stop forwarding source */
173 igmp_source_delete(source
);
176 If there are no more source records for the group, delete
180 if (!listcount(group
->group_source_list
)) {
181 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_GM_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 forwarding, and delete the source
345 igmp_source_forward_stop: stop forwarding, 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
));
472 src
->t_source_timer
= NULL
;
473 src
->source_group
= group
; /* back pointer */
474 src
->source_addr
= src_addr
;
475 src
->source_creation
= pim_time_monotonic_sec();
476 src
->source_flags
= 0;
477 src
->source_query_retransmit_count
= 0;
478 src
->source_channel_oil
= NULL
;
480 listnode_add(group
->group_source_list
, src
);
482 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
483 igmp_anysource_forward_stop(group
);
487 static void allow(struct gm_sock
*igmp
, struct in_addr from
,
488 struct in_addr group_addr
, int num_sources
,
489 struct in_addr
*sources
)
491 struct gm_source
*source
;
492 struct gm_group
*group
;
495 if (num_sources
== 0) {
497 RFC 3376: 3.1. Socket-State
498 If the requested filter mode is INCLUDE *and* the requested
499 source list is empty, then the entry corresponding to the
500 requested interface and multicast address is deleted if
501 present. If no such entry is present, the request is ignored.
502 So, deleting the group present.
504 group
= find_group_by_addr(igmp
, group_addr
);
508 if (group
->group_filtermode_isexcl
) {
509 if (listcount(group
->group_source_list
) == 1) {
510 struct in_addr star
= {.s_addr
= INADDR_ANY
};
512 source
= igmp_find_source_by_addr(group
, star
);
514 igmp_source_reset_gmi(group
, source
);
517 igmp_group_delete(group
);
523 /* non-existent group is created as INCLUDE {empty} */
524 group
= igmp_add_group_by_addr(igmp
, group_addr
);
529 /* scan received sources */
530 for (i
= 0; i
< num_sources
; ++i
) {
531 struct in_addr
*src_addr
;
533 src_addr
= sources
+ i
;
535 source
= igmp_get_source_by_addr(group
, *src_addr
, NULL
);
540 RFC 3376: 6.4.1. Reception of Current-State Records
542 When receiving IS_IN reports for groups in EXCLUDE mode is
543 sources should be moved from set with (timers = 0) to set with
546 igmp_source_reset_gmi() below, resetting the source timers to
547 GMI, accomplishes this.
549 igmp_source_reset_gmi(group
, source
);
551 } /* scan received sources */
554 void igmpv3_report_isin(struct gm_sock
*igmp
, struct in_addr from
,
555 struct in_addr group_addr
, int num_sources
,
556 struct in_addr
*sources
)
558 on_trace(__func__
, igmp
->interface
, from
, group_addr
, num_sources
,
561 allow(igmp
, from
, group_addr
, num_sources
, sources
);
564 static void isex_excl(struct gm_group
*group
, int num_sources
,
565 struct in_addr
*sources
)
567 struct gm_source
*source
;
571 assert(group
->group_filtermode_isexcl
);
573 /* E.1: set deletion flag for known sources (X,Y) */
574 source_mark_delete_flag(group
);
576 /* scan received sources (A) */
577 for (i
= 0; i
< num_sources
; ++i
) {
578 struct in_addr
*src_addr
;
581 src_addr
= sources
+ i
;
583 /* E.2: lookup reported source from (A) in (X,Y) */
584 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
589 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
590 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
592 /* E.4: if not found, create source with timer=GMI:
594 assert(!source
->t_source_timer
); /* timer == 0 */
595 igmp_source_reset_gmi(group
, source
);
596 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
599 } /* scan received sources */
602 * If we are in isexcl mode and num_sources == 0
603 * than that means we have a *,g entry that
604 * needs to be handled
606 if (group
->group_filtermode_isexcl
&& num_sources
== 0) {
607 struct in_addr star
= {.s_addr
= INADDR_ANY
};
608 source
= igmp_find_source_by_addr(group
, star
);
610 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
611 igmp_source_reset_gmi(group
, source
);
615 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
616 source_delete_by_flag(group
->group_source_list
);
619 static void isex_incl(struct gm_group
*group
, int num_sources
,
620 struct in_addr
*sources
)
625 assert(!group
->group_filtermode_isexcl
);
627 /* I.1: set deletion flag for known sources (A) */
628 source_mark_delete_flag(group
);
630 /* scan received sources (B) */
631 for (i
= 0; i
< num_sources
; ++i
) {
632 struct gm_source
*source
;
633 struct in_addr
*src_addr
;
636 src_addr
= sources
+ i
;
638 /* I.2: lookup reported source (B) */
639 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
644 /* I.3: if found, clear deletion flag (A*B) */
645 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
647 /* I.4: if not found, create source with timer=0 (B-A)
649 assert(!source
->t_source_timer
); /* (B-A) timer=0 */
652 } /* scan received sources */
654 /* I.5: delete all sources marked with deletion flag (A-B) */
655 source_delete_by_flag(group
->group_source_list
);
657 group
->group_filtermode_isexcl
= 1; /* boolean=true */
659 assert(group
->group_filtermode_isexcl
);
661 group_exclude_fwd_anysrc_ifempty(group
);
664 void igmpv3_report_isex(struct gm_sock
*igmp
, struct in_addr from
,
665 struct in_addr group_addr
, int num_sources
,
666 struct in_addr
*sources
, int from_igmp_v2_report
)
668 struct interface
*ifp
= igmp
->interface
;
669 struct gm_group
*group
;
671 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
673 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
676 /* non-existent group is created as INCLUDE {empty} */
677 group
= igmp_add_group_by_addr(igmp
, group_addr
);
682 /* So we can display how we learned the group in our show command output
684 if (from_igmp_v2_report
)
685 group
->igmp_version
= 2;
687 if (group
->group_filtermode_isexcl
) {
689 isex_excl(group
, num_sources
, sources
);
692 isex_incl(group
, num_sources
, sources
);
693 assert(group
->group_filtermode_isexcl
);
696 assert(group
->group_filtermode_isexcl
);
698 igmp_group_reset_gmi(group
);
701 static void toin_incl(struct gm_group
*group
, int num_sources
,
702 struct in_addr
*sources
)
704 int num_sources_tosend
= listcount(group
->group_source_list
);
707 /* Set SEND flag for all known sources (A) */
708 source_mark_send_flag(group
);
710 /* Scan received sources (B) */
711 for (i
= 0; i
< num_sources
; ++i
) {
712 struct gm_source
*source
;
713 struct in_addr
*src_addr
;
716 src_addr
= sources
+ i
;
718 /* Lookup reported source (B) */
719 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
724 /* If found, clear SEND flag (A*B) */
725 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
726 --num_sources_tosend
;
730 igmp_source_reset_gmi(group
, source
);
733 /* Send sources marked with SEND flag: Q(G,A-B) */
734 if (num_sources_tosend
> 0) {
735 source_query_send_by_flag(group
, num_sources_tosend
);
739 static void toin_excl(struct gm_group
*group
, int num_sources
,
740 struct in_addr
*sources
)
742 int num_sources_tosend
;
745 /* Set SEND flag for X (sources with timer > 0) */
746 num_sources_tosend
= source_mark_send_flag_by_timer(group
);
748 /* Scan received sources (A) */
749 for (i
= 0; i
< num_sources
; ++i
) {
750 struct gm_source
*source
;
751 struct in_addr
*src_addr
;
754 src_addr
= sources
+ i
;
756 /* Lookup reported source (A) */
757 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
761 if (source
->t_source_timer
) {
762 /* If found and timer running, clear SEND flag
764 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
765 --num_sources_tosend
;
769 igmp_source_reset_gmi(group
, source
);
772 /* Send sources marked with SEND flag: Q(G,X-A) */
773 if (num_sources_tosend
> 0) {
774 source_query_send_by_flag(group
, num_sources_tosend
);
778 group_query_send(group
);
781 void igmpv3_report_toin(struct gm_sock
*igmp
, struct in_addr from
,
782 struct in_addr group_addr
, int num_sources
,
783 struct in_addr
*sources
)
785 struct interface
*ifp
= igmp
->interface
;
786 struct gm_group
*group
;
788 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
791 * If the requested filter mode is INCLUDE *and* the requested source
792 * list is empty, then the entry corresponding to the requested
793 * interface and multicast address is deleted if present. If no such
794 * entry is present, the request is ignored.
797 /* non-existent group is created as INCLUDE {empty} */
798 group
= igmp_add_group_by_addr(igmp
, group_addr
);
803 group
= find_group_by_addr(igmp
, group_addr
);
808 if (group
->group_filtermode_isexcl
) {
810 toin_excl(group
, num_sources
, sources
);
813 toin_incl(group
, num_sources
, sources
);
817 static void toex_incl(struct gm_group
*group
, int num_sources
,
818 struct in_addr
*sources
)
820 int num_sources_tosend
= 0;
823 assert(!group
->group_filtermode_isexcl
);
825 /* Set DELETE flag for all known sources (A) */
826 source_mark_delete_flag(group
);
828 /* Clear off SEND flag from all known sources (A) */
829 source_clear_send_flag(group
->group_source_list
);
831 /* Scan received sources (B) */
832 for (i
= 0; i
< num_sources
; ++i
) {
833 struct gm_source
*source
;
834 struct in_addr
*src_addr
;
837 src_addr
= sources
+ i
;
839 /* Lookup reported source (B) */
840 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
842 /* If found, clear deletion flag: (A*B) */
843 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
844 /* and set SEND flag (A*B) */
845 IGMP_SOURCE_DO_SEND(source
->source_flags
);
846 ++num_sources_tosend
;
849 } /* Scan received sources (B) */
851 group
->group_filtermode_isexcl
= 1; /* boolean=true */
853 /* Delete all sources marked with DELETE flag (A-B) */
854 source_delete_by_flag(group
->group_source_list
);
856 /* Send sources marked with SEND flag: Q(G,A*B) */
857 if (num_sources_tosend
> 0) {
858 source_query_send_by_flag(group
, num_sources_tosend
);
861 assert(group
->group_filtermode_isexcl
);
863 group_exclude_fwd_anysrc_ifempty(group
);
866 static void toex_excl(struct gm_group
*group
, int num_sources
,
867 struct in_addr
*sources
)
869 int num_sources_tosend
= 0;
872 /* set DELETE flag for all known sources (X,Y) */
873 source_mark_delete_flag(group
);
875 /* clear off SEND flag from all known sources (X,Y) */
876 source_clear_send_flag(group
->group_source_list
);
878 if (num_sources
== 0) {
879 struct gm_source
*source
;
880 struct in_addr any
= {.s_addr
= INADDR_ANY
};
882 source
= igmp_find_source_by_addr(group
, any
);
884 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
887 /* scan received sources (A) */
888 for (i
= 0; i
< num_sources
; ++i
) {
889 struct gm_source
*source
;
890 struct in_addr
*src_addr
;
893 src_addr
= sources
+ i
;
895 /* lookup reported source (A) in known sources (X,Y) */
896 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
901 /* if found, clear off DELETE flag from reported source
903 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
905 /* if not found, create source with Group Timer:
906 * (A-X-Y)=Group Timer */
907 long group_timer_msec
;
909 assert(!source
->t_source_timer
); /* timer == 0 */
910 group_timer_msec
= igmp_group_timer_remain_msec(group
);
911 igmp_source_timer_on(group
, source
, group_timer_msec
);
912 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
914 /* make sure source is created with DELETE flag unset */
915 assert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
918 /* make sure reported source has DELETE flag unset */
919 assert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
921 if (source
->t_source_timer
) {
922 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
923 IGMP_SOURCE_DO_SEND(source
->source_flags
);
924 ++num_sources_tosend
;
927 } /* scan received sources (A) */
930 delete all sources marked with DELETE flag:
934 source_delete_by_flag(group
->group_source_list
);
936 /* send sources marked with SEND flag: Q(G,A-Y) */
937 if (num_sources_tosend
> 0) {
938 source_query_send_by_flag(group
, num_sources_tosend
);
942 void igmpv3_report_toex(struct gm_sock
*igmp
, struct in_addr from
,
943 struct in_addr group_addr
, int num_sources
,
944 struct in_addr
*sources
)
946 struct interface
*ifp
= igmp
->interface
;
947 struct gm_group
*group
;
949 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
951 /* non-existent group is created as INCLUDE {empty} */
952 group
= igmp_add_group_by_addr(igmp
, group_addr
);
957 if (group
->group_filtermode_isexcl
) {
959 toex_excl(group
, num_sources
, sources
);
962 toex_incl(group
, num_sources
, sources
);
963 assert(group
->group_filtermode_isexcl
);
965 assert(group
->group_filtermode_isexcl
);
967 /* Group Timer=GMI */
968 igmp_group_reset_gmi(group
);
971 void igmpv3_report_allow(struct gm_sock
*igmp
, struct in_addr from
,
972 struct in_addr group_addr
, int num_sources
,
973 struct in_addr
*sources
)
975 on_trace(__func__
, igmp
->interface
, from
, group_addr
, num_sources
,
978 allow(igmp
, from
, group_addr
, num_sources
, sources
);
981 static void igmp_send_query_group(struct gm_group
*group
, char *query_buf
,
982 size_t query_buf_size
, int num_sources
,
985 struct interface
*ifp
= group
->interface
;
986 struct pim_interface
*pim_ifp
= ifp
->info
;
987 struct gm_sock
*igmp
;
988 struct listnode
*sock_node
;
990 for (ALL_LIST_ELEMENTS_RO(pim_ifp
->gm_socket_list
, sock_node
, igmp
)) {
992 pim_ifp
->igmp_version
, group
, query_buf
, query_buf_size
,
993 num_sources
, group
->group_addr
, group
->group_addr
,
994 pim_ifp
->gm_specific_query_max_response_time_dsec
,
1000 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1002 When transmitting a group specific query, if the group timer is
1003 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1004 in the query message.
1006 static void group_retransmit_group(struct gm_group
*group
)
1008 struct pim_interface
*pim_ifp
;
1009 long lmqc
; /* Last Member Query Count */
1010 long lmqi_msec
; /* Last Member Query Interval */
1011 long lmqt_msec
; /* Last Member Query Time */
1015 pim_ifp
= group
->interface
->info
;
1017 if (pim_ifp
->igmp_version
== 3) {
1018 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1020 query_buf_size
= IGMP_V12_MSG_SIZE
;
1023 char query_buf
[query_buf_size
];
1025 lmqc
= pim_ifp
->gm_last_member_query_count
;
1026 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1027 lmqt_msec
= lmqc
* lmqi_msec
;
1030 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1032 When transmitting a group specific query, if the group timer is
1033 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1034 in the query message.
1036 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1038 if (PIM_DEBUG_IGMP_TRACE
) {
1039 char group_str
[INET_ADDRSTRLEN
];
1040 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1043 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1044 group_str
, group
->interface
->name
, s_flag
,
1045 group
->group_specific_query_retransmit_count
);
1049 RFC3376: 4.1.12. IP Destination Addresses for Queries
1051 Group-Specific and Group-and-Source-Specific Queries are sent with
1052 an IP destination address equal to the multicast address of
1056 igmp_send_query_group(group
, query_buf
, sizeof(query_buf
), 0, s_flag
);
1060 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1062 When building a group and source specific query for a group G, two
1063 separate query messages are sent for the group. The first one has
1064 the "Suppress Router-Side Processing" bit set and contains all the
1065 sources with retransmission state and timers greater than LMQT. The
1066 second has the "Suppress Router-Side Processing" bit clear and
1067 contains all the sources with retransmission state and timers lower
1068 or equal to LMQT. If either of the two calculated messages does not
1069 contain any sources, then its transmission is suppressed.
1071 static int group_retransmit_sources(struct gm_group
*group
,
1072 int send_with_sflag_set
)
1074 struct pim_interface
*pim_ifp
;
1075 long lmqc
; /* Last Member Query Count */
1076 long lmqi_msec
; /* Last Member Query Interval */
1077 long lmqt_msec
; /* Last Member Query Time */
1078 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1079 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1080 int query_buf1_max_sources
;
1081 int query_buf2_max_sources
;
1082 struct in_addr
*source_addr1
;
1083 struct in_addr
*source_addr2
;
1084 int num_sources_tosend1
;
1085 int num_sources_tosend2
;
1086 struct listnode
*src_node
;
1087 struct gm_source
*src
;
1088 int num_retransmit_sources_left
= 0;
1090 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1091 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1093 pim_ifp
= group
->interface
->info
;
1095 lmqc
= pim_ifp
->gm_last_member_query_count
;
1096 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1097 lmqt_msec
= lmqc
* lmqi_msec
;
1099 /* Scan all group sources */
1100 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1102 /* Source has retransmission state? */
1103 if (src
->source_query_retransmit_count
< 1)
1106 if (--src
->source_query_retransmit_count
> 0) {
1107 ++num_retransmit_sources_left
;
1110 /* Copy source address into appropriate query buffer */
1111 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1112 *source_addr1
= src
->source_addr
;
1115 *source_addr2
= src
->source_addr
;
1120 num_sources_tosend1
=
1122 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1123 num_sources_tosend2
=
1125 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1127 if (PIM_DEBUG_IGMP_TRACE
) {
1128 char group_str
[INET_ADDRSTRLEN
];
1129 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1132 "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",
1133 group_str
, group
->interface
->name
, num_sources_tosend1
,
1134 num_sources_tosend2
, send_with_sflag_set
,
1135 num_retransmit_sources_left
);
1138 if (num_sources_tosend1
> 0) {
1140 Send group-and-source-specific query with s_flag set and all
1141 sources with timers greater than LMQT.
1144 if (send_with_sflag_set
) {
1146 query_buf1_max_sources
=
1147 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1149 if (num_sources_tosend1
> query_buf1_max_sources
) {
1150 char group_str
[INET_ADDRSTRLEN
];
1151 pim_inet4_dump("<group?>", group
->group_addr
,
1152 group_str
, sizeof(group_str
));
1154 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1155 __func__
, group_str
,
1156 group
->interface
->name
,
1157 num_sources_tosend1
, sizeof(query_buf1
),
1158 query_buf1_max_sources
);
1161 RFC3376: 4.1.12. IP Destination Addresses for
1164 Group-Specific and Group-and-Source-Specific
1165 Queries are sent with
1166 an IP destination address equal to the
1167 multicast address of
1171 igmp_send_query_group(
1172 group
, query_buf1
, sizeof(query_buf1
),
1173 num_sources_tosend1
, 1 /* s_flag */);
1176 } /* send_with_sflag_set */
1179 if (num_sources_tosend2
> 0) {
1181 Send group-and-source-specific query with s_flag clear and all
1182 sources with timers lower or equal to LMQT.
1185 query_buf2_max_sources
=
1186 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1187 if (num_sources_tosend2
> query_buf2_max_sources
) {
1188 char group_str
[INET_ADDRSTRLEN
];
1189 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1192 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1193 __func__
, group_str
, group
->interface
->name
,
1194 num_sources_tosend2
, sizeof(query_buf2
),
1195 query_buf2_max_sources
);
1198 RFC3376: 4.1.12. IP Destination Addresses for Queries
1200 Group-Specific and Group-and-Source-Specific Queries
1202 an IP destination address equal to the multicast
1207 igmp_send_query_group(
1208 group
, query_buf2
, sizeof(query_buf2
),
1209 num_sources_tosend2
, 0 /* s_flag */);
1213 return num_retransmit_sources_left
;
1216 static void igmp_group_retransmit(struct thread
*t
)
1218 struct gm_group
*group
;
1219 int num_retransmit_sources_left
;
1220 int send_with_sflag_set
; /* boolean */
1222 group
= THREAD_ARG(t
);
1224 if (PIM_DEBUG_IGMP_TRACE
) {
1225 char group_str
[INET_ADDRSTRLEN
];
1226 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1228 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1229 group
->interface
->name
);
1232 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1233 if (group
->group_specific_query_retransmit_count
> 0) {
1235 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1236 group_retransmit_group(group
);
1237 --group
->group_specific_query_retransmit_count
;
1241 If a group specific query is scheduled to be transmitted at
1243 same time as a group and source specific query for the same
1245 then transmission of the group and source specific message
1247 "Suppress Router-Side Processing" bit set may be suppressed.
1249 send_with_sflag_set
= 0; /* boolean=false */
1251 send_with_sflag_set
= 1; /* boolean=true */
1254 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1255 num_retransmit_sources_left
=
1256 group_retransmit_sources(group
, send_with_sflag_set
);
1259 Keep group retransmit timer running if there is any retransmit
1262 if ((num_retransmit_sources_left
> 0)
1263 || (group
->group_specific_query_retransmit_count
> 0)) {
1264 group_retransmit_timer_on(group
);
1269 group_retransmit_timer_on:
1270 if group retransmit timer isn't running, starts it;
1271 otherwise, do nothing
1273 static void group_retransmit_timer_on(struct gm_group
*group
)
1275 struct pim_interface
*pim_ifp
;
1276 long lmqi_msec
; /* Last Member Query Interval */
1278 /* if group retransmit timer is running, do nothing */
1279 if (group
->t_group_query_retransmit_timer
) {
1283 pim_ifp
= group
->interface
->info
;
1285 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1287 if (PIM_DEBUG_IGMP_TRACE
) {
1288 char group_str
[INET_ADDRSTRLEN
];
1289 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1292 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1293 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1294 group
->interface
->name
);
1297 thread_add_timer_msec(router
->master
, igmp_group_retransmit
, group
,
1299 &group
->t_group_query_retransmit_timer
);
1302 static long igmp_group_timer_remain_msec(struct gm_group
*group
)
1304 return pim_time_timer_remain_msec(group
->t_group_timer
);
1307 static long igmp_source_timer_remain_msec(struct gm_source
*source
)
1309 return pim_time_timer_remain_msec(source
->t_source_timer
);
1313 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1315 static void group_query_send(struct gm_group
*group
)
1317 struct pim_interface
*pim_ifp
;
1318 long lmqc
; /* Last Member Query Count */
1320 pim_ifp
= group
->interface
->info
;
1321 lmqc
= pim_ifp
->gm_last_member_query_count
;
1323 /* lower group timer to lmqt */
1324 igmp_group_timer_lower_to_lmqt(group
);
1326 /* reset retransmission counter */
1327 group
->group_specific_query_retransmit_count
= lmqc
;
1329 /* immediately send group specific query (decrease retransmit counter by
1331 group_retransmit_group(group
);
1333 /* make sure group retransmit timer is running */
1334 group_retransmit_timer_on(group
);
1338 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1340 static void source_query_send_by_flag(struct gm_group
*group
,
1341 int num_sources_tosend
)
1343 struct pim_interface
*pim_ifp
;
1344 struct listnode
*src_node
;
1345 struct gm_source
*src
;
1346 long lmqc
; /* Last Member Query Count */
1347 long lmqi_msec
; /* Last Member Query Interval */
1348 long lmqt_msec
; /* Last Member Query Time */
1350 assert(num_sources_tosend
> 0);
1352 pim_ifp
= group
->interface
->info
;
1354 lmqc
= pim_ifp
->gm_last_member_query_count
;
1355 lmqi_msec
= 100 * pim_ifp
->gm_specific_query_max_response_time_dsec
;
1356 lmqt_msec
= lmqc
* lmqi_msec
;
1359 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1362 (...) for each of the sources in X of group G, with source timer
1365 o Set number of retransmissions for each source to [Last Member
1367 o Lower source timer to LMQT.
1369 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1370 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1371 /* source "src" in X of group G */
1372 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1373 src
->source_query_retransmit_count
= lmqc
;
1374 igmp_source_timer_lower_to_lmqt(src
);
1379 /* send group-and-source specific queries */
1380 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1382 /* make sure group retransmit timer is running */
1383 group_retransmit_timer_on(group
);
1386 static void block_excl(struct gm_group
*group
, int num_sources
,
1387 struct in_addr
*sources
)
1389 int num_sources_tosend
= 0;
1392 /* 1. clear off SEND flag from all known sources (X,Y) */
1393 source_clear_send_flag(group
->group_source_list
);
1395 /* 2. scan received sources (A) */
1396 for (i
= 0; i
< num_sources
; ++i
) {
1397 struct gm_source
*source
;
1398 struct in_addr
*src_addr
;
1401 src_addr
= sources
+ i
;
1403 /* lookup reported source (A) in known sources (X,Y) */
1404 source
= igmp_get_source_by_addr(group
, *src_addr
, &new);
1409 /* 3: if not found, create source with Group Timer:
1410 * (A-X-Y)=Group Timer */
1411 long group_timer_msec
;
1413 assert(!source
->t_source_timer
); /* timer == 0 */
1414 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1415 igmp_source_timer_on(group
, source
, group_timer_msec
);
1416 assert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1419 if (source
->t_source_timer
) {
1420 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1421 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1422 ++num_sources_tosend
;
1426 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1427 if (num_sources_tosend
> 0) {
1428 source_query_send_by_flag(group
, num_sources_tosend
);
1432 static void block_incl(struct gm_group
*group
, int num_sources
,
1433 struct in_addr
*sources
)
1435 int num_sources_tosend
= 0;
1438 /* 1. clear off SEND flag from all known sources (B) */
1439 source_clear_send_flag(group
->group_source_list
);
1441 /* 2. scan received sources (A) */
1442 for (i
= 0; i
< num_sources
; ++i
) {
1443 struct gm_source
*source
;
1444 struct in_addr
*src_addr
;
1446 src_addr
= sources
+ i
;
1448 /* lookup reported source (A) in known sources (B) */
1449 source
= igmp_find_source_by_addr(group
, *src_addr
);
1451 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1452 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1453 ++num_sources_tosend
;
1457 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1458 if (num_sources_tosend
> 0) {
1459 source_query_send_by_flag(group
, num_sources_tosend
);
1463 void igmpv3_report_block(struct gm_sock
*igmp
, struct in_addr from
,
1464 struct in_addr group_addr
, int num_sources
,
1465 struct in_addr
*sources
)
1467 struct interface
*ifp
= igmp
->interface
;
1468 struct gm_group
*group
;
1470 on_trace(__func__
, ifp
, from
, group_addr
, num_sources
, sources
);
1472 /* non-existent group is created as INCLUDE {empty} */
1473 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1478 if (group
->group_filtermode_isexcl
) {
1480 block_excl(group
, num_sources
, sources
);
1483 block_incl(group
, num_sources
, sources
);
1487 void igmp_group_timer_lower_to_lmqt(struct gm_group
*group
)
1489 struct interface
*ifp
;
1490 struct pim_interface
*pim_ifp
;
1492 int lmqi_dsec
; /* Last Member Query Interval */
1493 int lmqc
; /* Last Member Query Count */
1494 int lmqt_msec
; /* Last Member Query Time */
1497 RFC 3376: 6.2.2. Definition of Group Timers
1499 The group timer is only used when a group is in EXCLUDE mode and
1500 it represents the time for the *filter-mode* of the group to
1501 expire and switch to INCLUDE mode.
1503 if (!group
->group_filtermode_isexcl
) {
1507 ifp
= group
->interface
;
1508 pim_ifp
= ifp
->info
;
1511 lmqi_dsec
= pim_ifp
->gm_specific_query_max_response_time_dsec
;
1512 lmqc
= pim_ifp
->gm_last_member_query_count
;
1513 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1514 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1516 if (PIM_DEBUG_IGMP_TRACE
) {
1517 char group_str
[INET_ADDRSTRLEN
];
1518 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1521 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1522 __func__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1526 assert(group
->group_filtermode_isexcl
);
1528 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1531 void igmp_source_timer_lower_to_lmqt(struct gm_source
*source
)
1533 struct gm_group
*group
;
1534 struct interface
*ifp
;
1535 struct pim_interface
*pim_ifp
;
1537 int lmqi_dsec
; /* Last Member Query Interval */
1538 int lmqc
; /* Last Member Query Count */
1539 int lmqt_msec
; /* Last Member Query Time */
1541 group
= source
->source_group
;
1542 ifp
= group
->interface
;
1543 pim_ifp
= ifp
->info
;
1546 lmqi_dsec
= pim_ifp
->gm_specific_query_max_response_time_dsec
;
1547 lmqc
= pim_ifp
->gm_last_member_query_count
;
1548 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1549 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1551 if (PIM_DEBUG_IGMP_TRACE
) {
1552 char group_str
[INET_ADDRSTRLEN
];
1553 char source_str
[INET_ADDRSTRLEN
];
1554 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1556 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
1557 sizeof(source_str
));
1559 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1560 __func__
, group_str
, source_str
, ifname
, lmqc
,
1561 lmqi_dsec
, lmqt_msec
);
1564 igmp_source_timer_on(group
, source
, lmqt_msec
);
1567 void igmp_v3_send_query(struct gm_group
*group
, int fd
, const char *ifname
,
1568 char *query_buf
, int query_buf_size
, int num_sources
,
1569 struct in_addr dst_addr
, struct in_addr group_addr
,
1570 int query_max_response_time_dsec
, uint8_t s_flag
,
1571 uint8_t querier_robustness_variable
,
1572 uint16_t querier_query_interval
)
1575 uint8_t max_resp_code
;
1578 struct sockaddr_in to
;
1582 assert(num_sources
>= 0);
1584 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1585 if (msg_size
> query_buf_size
) {
1588 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1589 __FILE__
, __func__
, msg_size
, query_buf_size
);
1593 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1594 assert((s_flag
== 0) || (s_flag
== 1));
1596 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1597 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1600 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1602 If non-zero, the QRV field contains the [Robustness Variable]
1603 value used by the querier, i.e., the sender of the Query. If the
1604 querier's [Robustness Variable] exceeds 7, the maximum value of
1605 the QRV field, the QRV is set to zero.
1607 if (querier_robustness_variable
> 7) {
1608 querier_robustness_variable
= 0;
1611 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1612 query_buf
[1] = max_resp_code
;
1613 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) =
1614 0; /* for computing checksum */
1615 memcpy(query_buf
+ 4, &group_addr
, sizeof(struct in_addr
));
1617 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1618 query_buf
[9] = qqic
;
1619 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) =
1622 checksum
= in_cksum(query_buf
, msg_size
);
1623 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1625 if (PIM_DEBUG_GM_PACKETS
) {
1626 char dst_str
[INET_ADDRSTRLEN
];
1627 char group_str
[INET_ADDRSTRLEN
];
1628 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1629 pim_inet4_dump("<group?>", group_addr
, group_str
,
1632 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1633 dst_str
, ifname
, group_str
, num_sources
, msg_size
,
1634 s_flag
, querier_robustness_variable
,
1635 querier_query_interval
, qqic
);
1638 memset(&to
, 0, sizeof(to
));
1639 to
.sin_family
= AF_INET
;
1640 to
.sin_addr
= dst_addr
;
1643 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1644 (struct sockaddr
*)&to
, tolen
);
1645 if (sent
!= (ssize_t
)msg_size
) {
1646 char dst_str
[INET_ADDRSTRLEN
];
1647 char group_str
[INET_ADDRSTRLEN
];
1648 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1649 pim_inet4_dump("<group?>", group_addr
, group_str
,
1653 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1654 dst_str
, ifname
, group_str
, msg_size
, errno
,
1655 safe_strerror(errno
));
1658 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1659 dst_str
, ifname
, group_str
, msg_size
, sent
);
1665 s_flag sanity test: s_flag must be set for general queries
1667 RFC 3376: 6.6.1. Timer Updates
1669 When a router sends or receives a query with a clear Suppress
1670 Router-Side Processing flag, it must update its timers to reflect
1671 the correct timeout values for the group or sources being queried.
1673 General queries don't trigger timer update.
1676 /* general query? */
1677 if (group_addr
.s_addr
== INADDR_ANY
) {
1678 char dst_str
[INET_ADDRSTRLEN
];
1679 char group_str
[INET_ADDRSTRLEN
];
1680 pim_inet4_dump("<dst?>", dst_addr
, dst_str
,
1682 pim_inet4_dump("<group?>", group_addr
, group_str
,
1685 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1686 __func__
, dst_str
, ifname
, group_str
,
1692 void igmp_v3_recv_query(struct gm_sock
*igmp
, const char *from_str
,
1695 struct interface
*ifp
;
1696 struct pim_interface
*pim_ifp
;
1697 struct in_addr group_addr
;
1698 uint8_t resv_s_qrv
= 0;
1703 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1704 ifp
= igmp
->interface
;
1705 pim_ifp
= ifp
->info
;
1708 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1710 * Routers adopt the QRV value from the most recently received Query
1711 * as their own [Robustness Variable] value, unless that most
1712 * recently received QRV was zero, in which case the receivers use
1713 * the default [Robustness Variable] value specified in section 8.1
1714 * or a statically configured value.
1716 resv_s_qrv
= igmp_msg
[8];
1717 qrv
= 7 & resv_s_qrv
;
1718 igmp
->querier_robustness_variable
=
1719 qrv
? qrv
: pim_ifp
->gm_default_robustness_variable
;
1722 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1724 * Multicast routers that are not the current querier adopt the QQI
1725 * value from the most recently received Query as their own [Query
1726 * Interval] value, unless that most recently received QQI was zero,
1727 * in which case the receiving routers use the default.
1729 if (igmp
->t_other_querier_timer
) {
1730 /* other querier present */
1734 qqi
= igmp_msg_decode8to16(qqic
);
1735 igmp
->querier_query_interval
=
1736 qqi
? qqi
: pim_ifp
->gm_default_query_interval
;
1738 if (PIM_DEBUG_IGMP_TRACE
) {
1739 char ifaddr_str
[INET_ADDRSTRLEN
];
1740 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
,
1741 sizeof(ifaddr_str
));
1743 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1745 qqi
? "recv-non-default" : "default",
1746 igmp
->querier_query_interval
, qqic
, from_str
);
1751 * RFC 3376: 6.6.1. Timer Updates
1753 * When a router sends or receives a query with a clear Suppress
1754 * Router-Side Processing flag, it must update its timers to reflect
1755 * the correct timeout values for the group or sources being queried.
1757 * General queries don't trigger timer update.
1759 s_flag
= (1 << 3) & resv_s_qrv
;
1762 /* s_flag is clear */
1764 if (group_addr
.s_addr
== INADDR_ANY
) {
1765 /* this is a general query */
1766 /* log that general query should have the s_flag set */
1768 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1769 from_str
, ifp
->name
);
1771 struct gm_group
*group
;
1773 /* this is a non-general query: perform timer updates */
1775 group
= find_group_by_addr(igmp
, group_addr
);
1777 int recv_num_sources
= ntohs(*(
1780 + IGMP_V3_NUMSOURCES_OFFSET
));
1783 * RFC 3376: 6.6.1. Timer Updates
1784 * Query Q(G,A): Source Timer for sources in A
1785 * are lowered to LMQT
1786 * Query Q(G): Group Timer is lowered to LMQT
1788 if (recv_num_sources
< 1) {
1789 /* Query Q(G): Group Timer is lowered to
1792 igmp_group_timer_lower_to_lmqt(group
);
1794 /* Query Q(G,A): Source Timer for
1795 * sources in A are lowered to LMQT */
1797 /* Scan sources in query and lower their
1799 struct in_addr
*sources
=
1802 + IGMP_V3_SOURCES_OFFSET
);
1803 for (i
= 0; i
< recv_num_sources
; ++i
) {
1804 struct in_addr src_addr
;
1805 struct gm_source
*src
;
1806 memcpy(&src_addr
, sources
+ i
,
1807 sizeof(struct in_addr
));
1808 src
= igmp_find_source_by_addr(
1811 igmp_source_timer_lower_to_lmqt(
1817 char group_str
[INET_ADDRSTRLEN
];
1818 pim_inet4_dump("<group?>", group_addr
,
1819 group_str
, sizeof(group_str
));
1821 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1822 from_str
, ifp
->name
, group_str
);
1825 } /* s_flag is clear: timer updates */
1828 static bool igmp_pkt_grp_addr_ok(struct interface
*ifp
, const char *from_str
,
1829 struct in_addr grp
, int rec_type
)
1831 struct pim_interface
*pim_ifp
;
1832 struct in_addr grp_addr
;
1834 pim_ifp
= ifp
->info
;
1836 /* determine filtering status for group */
1837 if (pim_is_group_filtered(pim_ifp
, &grp
)) {
1838 if (PIM_DEBUG_GM_PACKETS
) {
1840 "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
1841 &grp
.s_addr
, from_str
, ifp
->name
,
1842 pim_ifp
->boundary_oil_plist
);
1848 * If we receive a igmp report with the group in 224.0.0.0/24
1849 * then we should ignore it
1852 grp_addr
.s_addr
= ntohl(grp
.s_addr
);
1854 if (pim_is_group_224_0_0_0_24(grp_addr
)) {
1855 if (PIM_DEBUG_GM_PACKETS
) {
1857 "Ignoring IGMPv3 group record %pI4 from %s on %s group range falls in 224.0.0.0/24",
1858 &grp
.s_addr
, from_str
, ifp
->name
);
1866 * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
1867 * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
1870 if (pim_is_grp_ssm(pim_ifp
->pim
, grp
)) {
1872 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1873 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
1874 if (PIM_DEBUG_GM_PACKETS
) {
1876 "Ignoring IGMPv3 group record %pI4 from %s on %s exclude mode in SSM range",
1877 &grp
.s_addr
, from_str
, ifp
->name
);
1886 int igmp_v3_recv_report(struct gm_sock
*igmp
, struct in_addr from
,
1887 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1890 uint8_t *group_record
;
1891 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1892 struct interface
*ifp
= igmp
->interface
;
1893 struct pim_interface
*pim_ifp
= ifp
->info
;
1896 if (igmp
->mtrace_only
)
1899 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1901 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1902 from_str
, ifp
->name
, igmp_msg_len
,
1903 IGMP_V3_MSG_MIN_SIZE
);
1907 if (igmp_validate_checksum(igmp_msg
, igmp_msg_len
) == -1) {
1909 "Recv IGMPv3 report from %s on %s with invalid checksum",
1910 from_str
, ifp
->name
);
1914 /* Collecting IGMP Rx stats */
1915 igmp
->igmp_stats
.report_v3
++;
1917 if (pim_ifp
->igmp_version
== 2) {
1919 "Received Version 3 packet but interface: %s is configured for version 2",
1925 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1926 if (num_groups
< 1) {
1928 "Recv IGMP report v3 from %s on %s: missing group records",
1929 from_str
, ifp
->name
);
1933 if (PIM_DEBUG_GM_PACKETS
) {
1935 "Recv IGMP report v3 from %s on %s: size=%d groups=%d",
1936 from_str
, ifp
->name
, igmp_msg_len
, num_groups
);
1939 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1942 for (i
= 0; i
< num_groups
; ++i
) {
1943 struct in_addr rec_group
;
1948 int rec_num_sources
;
1951 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1954 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1955 from_str
, ifp
->name
);
1959 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1961 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1962 rec_num_sources
= ntohs(*(
1963 uint16_t *)(group_record
1964 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1967 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1968 sizeof(struct in_addr
));
1970 if (PIM_DEBUG_GM_PACKETS
) {
1972 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
1973 from_str
, ifp
->name
, i
, rec_type
,
1974 rec_auxdatalen
, rec_num_sources
,
1980 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1982 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1984 if ((src
+ 4) > report_pastend
) {
1986 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1987 from_str
, ifp
->name
);
1991 if (PIM_DEBUG_GM_PACKETS
) {
1994 if (!inet_ntop(AF_INET
, src
, src_str
,
1996 snprintf(src_str
, sizeof(src_str
),
2000 " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s",
2001 from_str
, ifp
->name
, i
,
2002 &rec_group
, src_str
);
2004 } /* for (sources) */
2007 if (igmp_pkt_grp_addr_ok(ifp
, from_str
, rec_group
, rec_type
))
2009 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
2010 igmpv3_report_isin(igmp
, from
, rec_group
,
2012 (struct in_addr
*)sources
);
2014 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
2016 igmp
, from
, rec_group
, rec_num_sources
,
2017 (struct in_addr
*)sources
, 0);
2019 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
2020 igmpv3_report_toin(igmp
, from
, rec_group
,
2022 (struct in_addr
*)sources
);
2024 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
2025 igmpv3_report_toex(igmp
, from
, rec_group
,
2027 (struct in_addr
*)sources
);
2029 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
2030 igmpv3_report_allow(igmp
, from
, rec_group
,
2032 (struct in_addr
*)sources
);
2034 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
2035 igmpv3_report_block(igmp
, from
, rec_group
,
2037 (struct in_addr
*)sources
);
2041 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2042 from_str
, ifp
->name
, rec_type
);
2046 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2048 } /* for (group records) */