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
26 #include "pim_iface.h"
28 #include "pim_igmpv3.h"
32 #include "pim_zebra.h"
35 static void group_retransmit_timer_on(struct igmp_group
*group
);
36 static long igmp_group_timer_remain_msec(struct igmp_group
*group
);
37 static long igmp_source_timer_remain_msec(struct igmp_source
*source
);
38 static void group_query_send(struct igmp_group
*group
);
39 static void source_query_send_by_flag(struct igmp_group
*group
,
40 int num_sources_tosend
);
42 static void on_trace(const char *label
, struct interface
*ifp
,
43 struct in_addr from
, struct in_addr group_addr
,
44 int num_sources
, struct in_addr
*sources
)
46 if (PIM_DEBUG_IGMP_TRACE
) {
47 char from_str
[INET_ADDRSTRLEN
];
48 char group_str
[INET_ADDRSTRLEN
];
50 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
51 pim_inet4_dump("<group?>", group_addr
, group_str
,
54 zlog_debug("%s: from %s on %s: group=%s sources=%d", label
,
55 from_str
, ifp
->name
, group_str
, num_sources
);
59 void igmp_group_reset_gmi(struct igmp_group
*group
)
61 long group_membership_interval_msec
;
62 struct pim_interface
*pim_ifp
;
63 struct igmp_sock
*igmp
;
64 struct interface
*ifp
;
66 igmp
= group
->group_igmp_sock
;
67 ifp
= igmp
->interface
;
71 RFC 3376: 8.4. Group Membership Interval
73 The Group Membership Interval is the amount of time that must pass
74 before a multicast router decides there are no more members of a
75 group or a particular source on a network.
77 This value MUST be ((the Robustness Variable) times (the Query
78 Interval)) plus (one Query Response Interval).
80 group_membership_interval_msec = querier_robustness_variable *
81 (1000 * querier_query_interval) +
82 100 * query_response_interval_dsec;
84 group_membership_interval_msec
= PIM_IGMP_GMI_MSEC(
85 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
86 pim_ifp
->igmp_query_max_response_time_dsec
);
88 if (PIM_DEBUG_IGMP_TRACE
) {
89 char group_str
[INET_ADDRSTRLEN
];
90 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
93 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
94 group_str
, group_membership_interval_msec
/ 1000,
95 group_membership_interval_msec
% 1000, ifp
->name
);
99 RFC 3376: 6.2.2. Definition of Group Timers
101 The group timer is only used when a group is in EXCLUDE mode and
102 it represents the time for the *filter-mode* of the group to
103 expire and switch to INCLUDE mode.
105 zassert(group
->group_filtermode_isexcl
);
107 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
110 static int igmp_source_timer(struct thread
*t
)
112 struct igmp_source
*source
;
113 struct igmp_group
*group
;
115 source
= THREAD_ARG(t
);
117 group
= source
->source_group
;
119 if (PIM_DEBUG_IGMP_TRACE
) {
120 char group_str
[INET_ADDRSTRLEN
];
121 char source_str
[INET_ADDRSTRLEN
];
122 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
124 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
127 "%s: Source timer expired for group %s source %s on %s",
128 __PRETTY_FUNCTION__
, group_str
, source_str
,
129 group
->group_igmp_sock
->interface
->name
);
133 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
136 Filter-Mode Source Timer Value Action
137 ----------- ------------------ ------
138 INCLUDE TIMER == 0 Suggest to stop forwarding
139 traffic from source and
140 remove source record. If
141 there are no more source
142 records for the group, delete
145 EXCLUDE TIMER == 0 Suggest to not forward
147 (DO NOT remove record)
149 Source timer switched from (T > 0) to (T == 0): disable forwarding.
152 if (group
->group_filtermode_isexcl
) {
155 igmp_source_forward_stop(source
);
159 /* igmp_source_delete() will stop forwarding source */
160 igmp_source_delete(source
);
163 If there are no more source records for the group, delete
167 if (!listcount(group
->group_source_list
)) {
168 igmp_group_delete_empty_include(group
);
175 static void source_timer_off(struct igmp_group
*group
,
176 struct igmp_source
*source
)
178 if (!source
->t_source_timer
)
181 if (PIM_DEBUG_IGMP_TRACE
) {
182 char group_str
[INET_ADDRSTRLEN
];
183 char source_str
[INET_ADDRSTRLEN
];
184 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
186 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
189 "Cancelling TIMER event for group %s source %s on %s",
190 group_str
, source_str
,
191 group
->group_igmp_sock
->interface
->name
);
194 THREAD_OFF(source
->t_source_timer
);
197 static void igmp_source_timer_on(struct igmp_group
*group
,
198 struct igmp_source
*source
, long interval_msec
)
200 source_timer_off(group
, source
);
201 struct pim_interface
*pim_ifp
= group
->group_igmp_sock
->interface
->info
;
203 if (PIM_DEBUG_IGMP_EVENTS
) {
204 char group_str
[INET_ADDRSTRLEN
];
205 char source_str
[INET_ADDRSTRLEN
];
206 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
208 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
211 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
212 interval_msec
/ 1000, interval_msec
% 1000, group_str
,
213 source_str
, group
->group_igmp_sock
->interface
->name
);
216 thread_add_timer_msec(master
, igmp_source_timer
, source
, interval_msec
,
217 &source
->t_source_timer
);
220 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
222 Source timer switched from (T == 0) to (T > 0): enable forwarding.
224 igmp_source_forward_start(pim_ifp
->pim
, source
);
227 void igmp_source_reset_gmi(struct igmp_sock
*igmp
, struct igmp_group
*group
,
228 struct igmp_source
*source
)
230 long group_membership_interval_msec
;
231 struct pim_interface
*pim_ifp
;
232 struct interface
*ifp
;
234 ifp
= igmp
->interface
;
237 group_membership_interval_msec
= PIM_IGMP_GMI_MSEC(
238 igmp
->querier_robustness_variable
, igmp
->querier_query_interval
,
239 pim_ifp
->igmp_query_max_response_time_dsec
);
241 if (PIM_DEBUG_IGMP_TRACE
) {
242 char group_str
[INET_ADDRSTRLEN
];
243 char source_str
[INET_ADDRSTRLEN
];
245 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
247 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
251 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
252 source_str
, group_membership_interval_msec
/ 1000,
253 group_membership_interval_msec
% 1000, group_str
,
257 igmp_source_timer_on(group
, source
, group_membership_interval_msec
);
260 static void source_mark_delete_flag(struct igmp_group
*group
)
262 struct listnode
*src_node
;
263 struct igmp_source
*src
;
265 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
266 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
270 static void source_mark_send_flag(struct igmp_group
*group
)
272 struct listnode
*src_node
;
273 struct igmp_source
*src
;
275 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
276 IGMP_SOURCE_DO_SEND(src
->source_flags
);
280 static int source_mark_send_flag_by_timer(struct igmp_group
*group
)
282 struct listnode
*src_node
;
283 struct igmp_source
*src
;
284 int num_marked_sources
= 0;
286 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
287 /* Is source timer running? */
288 if (src
->t_source_timer
) {
289 IGMP_SOURCE_DO_SEND(src
->source_flags
);
290 ++num_marked_sources
;
292 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
296 return num_marked_sources
;
299 static void source_clear_send_flag(struct list
*source_list
)
301 struct listnode
*src_node
;
302 struct igmp_source
*src
;
304 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
305 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
310 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
312 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group
*group
)
314 struct pim_interface
*pim_ifp
= group
->group_igmp_sock
->interface
->info
;
316 zassert(group
->group_filtermode_isexcl
);
318 if (listcount(group
->group_source_list
) < 1) {
319 igmp_anysource_forward_start(pim_ifp
->pim
, group
);
323 void igmp_source_free(struct igmp_source
*source
)
325 /* make sure there is no source timer running */
326 zassert(!source
->t_source_timer
);
328 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
331 static void source_channel_oil_detach(struct igmp_source
*source
)
333 if (source
->source_channel_oil
) {
334 pim_channel_oil_del(source
->source_channel_oil
);
335 source
->source_channel_oil
= NULL
;
340 igmp_source_delete: stop fowarding, and delete the source
341 igmp_source_forward_stop: stop fowarding, but keep the source
343 void igmp_source_delete(struct igmp_source
*source
)
345 struct igmp_group
*group
;
348 group
= source
->source_group
;
350 if (PIM_DEBUG_IGMP_TRACE
) {
351 char group_str
[INET_ADDRSTRLEN
];
352 char source_str
[INET_ADDRSTRLEN
];
353 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
355 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
358 "Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d",
359 source_str
, group_str
, group
->group_igmp_sock
->fd
,
360 group
->group_igmp_sock
->interface
->name
,
361 source
->source_channel_oil
362 ? source
->source_channel_oil
->oil_ref_count
366 source_timer_off(group
, source
);
367 igmp_source_forward_stop(source
);
369 /* sanity check that forwarding has been disabled */
370 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
371 char group_str
[INET_ADDRSTRLEN
];
372 char source_str
[INET_ADDRSTRLEN
];
373 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
375 pim_inet4_dump("<source?>", source
->source_addr
, source_str
,
378 "%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
379 __PRETTY_FUNCTION__
, source_str
, group_str
,
380 group
->group_igmp_sock
->fd
,
381 group
->group_igmp_sock
->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 igmp_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 igmp_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 igmp_source
*igmp_find_source_by_addr(struct igmp_group
*group
,
429 struct in_addr src_addr
)
431 struct listnode
*src_node
;
432 struct igmp_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 igmp_source
*source_new(struct igmp_group
*group
,
442 struct in_addr src_addr
)
444 struct igmp_source
*src
;
446 if (PIM_DEBUG_IGMP_TRACE
) {
447 char group_str
[INET_ADDRSTRLEN
];
448 char source_str
[INET_ADDRSTRLEN
];
449 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
451 pim_inet4_dump("<source?>", src_addr
, source_str
,
454 "Creating new IGMP source %s for group %s on socket %d interface %s",
455 source_str
, group_str
, group
->group_igmp_sock
->fd
,
456 group
->group_igmp_sock
->interface
->name
);
459 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
461 src
->t_source_timer
= NULL
;
462 src
->source_group
= group
; /* back pointer */
463 src
->source_addr
= src_addr
;
464 src
->source_creation
= pim_time_monotonic_sec();
465 src
->source_flags
= 0;
466 src
->source_query_retransmit_count
= 0;
467 src
->source_channel_oil
= NULL
;
469 listnode_add(group
->group_source_list
, src
);
471 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
472 igmp_anysource_forward_stop(group
);
477 static struct igmp_source
*add_source_by_addr(struct igmp_sock
*igmp
,
478 struct igmp_group
*group
,
479 struct in_addr src_addr
)
481 struct igmp_source
*src
;
483 src
= igmp_find_source_by_addr(group
, src_addr
);
488 src
= source_new(group
, src_addr
);
493 static void allow(struct igmp_sock
*igmp
, struct in_addr from
,
494 struct in_addr group_addr
, int num_sources
,
495 struct in_addr
*sources
)
497 struct igmp_source
*source
;
498 struct igmp_group
*group
;
501 /* non-existant group is created as INCLUDE {empty} */
502 group
= igmp_add_group_by_addr(igmp
, group_addr
);
507 /* scan received sources */
508 for (i
= 0; i
< num_sources
; ++i
) {
509 struct in_addr
*src_addr
;
511 src_addr
= sources
+ i
;
513 source
= add_source_by_addr(igmp
, group
, *src_addr
);
519 RFC 3376: 6.4.1. Reception of Current-State Records
521 When receiving IS_IN reports for groups in EXCLUDE mode is
522 sources should be moved from set with (timers = 0) to set with
525 igmp_source_reset_gmi() below, resetting the source timers to
526 GMI, accomplishes this.
528 igmp_source_reset_gmi(igmp
, group
, source
);
530 } /* scan received sources */
532 if ((num_sources
== 0) && (group
->group_filtermode_isexcl
)
533 && (listcount(group
->group_source_list
) == 1)) {
534 struct in_addr star
= {.s_addr
= INADDR_ANY
};
536 source
= igmp_find_source_by_addr(group
, star
);
538 igmp_source_reset_gmi(igmp
, group
, source
);
542 void igmpv3_report_isin(struct igmp_sock
*igmp
, struct in_addr from
,
543 struct in_addr group_addr
, int num_sources
,
544 struct in_addr
*sources
)
546 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
547 num_sources
, sources
);
549 allow(igmp
, from
, group_addr
, num_sources
, sources
);
552 static void isex_excl(struct igmp_group
*group
, int num_sources
,
553 struct in_addr
*sources
)
555 struct igmp_source
*source
;
559 zassert(group
->group_filtermode_isexcl
);
561 /* E.1: set deletion flag for known sources (X,Y) */
562 source_mark_delete_flag(group
);
564 /* scan received sources (A) */
565 for (i
= 0; i
< num_sources
; ++i
) {
566 struct in_addr
*src_addr
;
568 src_addr
= sources
+ i
;
570 /* E.2: lookup reported source from (A) in (X,Y) */
571 source
= igmp_find_source_by_addr(group
, *src_addr
);
573 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
574 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
576 /* E.4: if not found, create source with timer=GMI:
578 source
= source_new(group
, *src_addr
);
579 zassert(!source
->t_source_timer
); /* timer == 0 */
580 igmp_source_reset_gmi(group
->group_igmp_sock
, group
,
582 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
585 } /* scan received sources */
588 * If we are in isexcl mode and num_sources == 0
589 * than that means we have a *,g entry that
590 * needs to be handled
592 if (group
->group_filtermode_isexcl
&& num_sources
== 0) {
593 struct in_addr star
= {.s_addr
= INADDR_ANY
};
594 source
= igmp_find_source_by_addr(group
, star
);
596 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
597 igmp_source_reset_gmi(group
->group_igmp_sock
, group
,
602 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
603 source_delete_by_flag(group
->group_source_list
);
606 static void isex_incl(struct igmp_group
*group
, int num_sources
,
607 struct in_addr
*sources
)
612 zassert(!group
->group_filtermode_isexcl
);
614 /* I.1: set deletion flag for known sources (A) */
615 source_mark_delete_flag(group
);
617 /* scan received sources (B) */
618 for (i
= 0; i
< num_sources
; ++i
) {
619 struct igmp_source
*source
;
620 struct in_addr
*src_addr
;
622 src_addr
= sources
+ i
;
624 /* I.2: lookup reported source (B) */
625 source
= igmp_find_source_by_addr(group
, *src_addr
);
627 /* I.3: if found, clear deletion flag (A*B) */
628 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
630 /* I.4: if not found, create source with timer=0 (B-A)
632 source
= source_new(group
, *src_addr
);
633 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
636 } /* scan received sources */
638 /* I.5: delete all sources marked with deletion flag (A-B) */
639 source_delete_by_flag(group
->group_source_list
);
641 group
->group_filtermode_isexcl
= 1; /* boolean=true */
643 zassert(group
->group_filtermode_isexcl
);
645 group_exclude_fwd_anysrc_ifempty(group
);
648 void igmpv3_report_isex(struct igmp_sock
*igmp
, struct in_addr from
,
649 struct in_addr group_addr
, int num_sources
,
650 struct in_addr
*sources
, int from_igmp_v2_report
)
652 struct interface
*ifp
= igmp
->interface
;
653 struct igmp_group
*group
;
655 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
658 if (pim_is_group_filtered(ifp
->info
, &group_addr
))
661 /* non-existant group is created as INCLUDE {empty} */
662 group
= igmp_add_group_by_addr(igmp
, group_addr
);
667 /* So we can display how we learned the group in our show command output
669 if (from_igmp_v2_report
)
670 group
->igmp_version
= 2;
672 if (group
->group_filtermode_isexcl
) {
674 isex_excl(group
, num_sources
, sources
);
677 isex_incl(group
, num_sources
, sources
);
678 zassert(group
->group_filtermode_isexcl
);
681 zassert(group
->group_filtermode_isexcl
);
683 igmp_group_reset_gmi(group
);
686 static void toin_incl(struct igmp_group
*group
, int num_sources
,
687 struct in_addr
*sources
)
689 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
690 int num_sources_tosend
= listcount(group
->group_source_list
);
693 /* Set SEND flag for all known sources (A) */
694 source_mark_send_flag(group
);
696 /* Scan received sources (B) */
697 for (i
= 0; i
< num_sources
; ++i
) {
698 struct igmp_source
*source
;
699 struct in_addr
*src_addr
;
701 src_addr
= sources
+ i
;
703 /* Lookup reported source (B) */
704 source
= igmp_find_source_by_addr(group
, *src_addr
);
706 /* If found, clear SEND flag (A*B) */
707 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
708 --num_sources_tosend
;
710 /* If not found, create new source */
711 source
= source_new(group
, *src_addr
);
715 igmp_source_reset_gmi(igmp
, group
, source
);
718 /* Send sources marked with SEND flag: Q(G,A-B) */
719 if (num_sources_tosend
> 0) {
720 source_query_send_by_flag(group
, num_sources_tosend
);
724 static void toin_excl(struct igmp_group
*group
, int num_sources
,
725 struct in_addr
*sources
)
727 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
728 int num_sources_tosend
;
731 /* Set SEND flag for X (sources with timer > 0) */
732 num_sources_tosend
= source_mark_send_flag_by_timer(group
);
734 /* Scan received sources (A) */
735 for (i
= 0; i
< num_sources
; ++i
) {
736 struct igmp_source
*source
;
737 struct in_addr
*src_addr
;
739 src_addr
= sources
+ i
;
741 /* Lookup reported source (A) */
742 source
= igmp_find_source_by_addr(group
, *src_addr
);
744 if (source
->t_source_timer
) {
745 /* If found and timer running, clear SEND flag
747 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
748 --num_sources_tosend
;
751 /* If not found, create new source */
752 source
= source_new(group
, *src_addr
);
756 igmp_source_reset_gmi(igmp
, group
, source
);
759 /* Send sources marked with SEND flag: Q(G,X-A) */
760 if (num_sources_tosend
> 0) {
761 source_query_send_by_flag(group
, num_sources_tosend
);
765 group_query_send(group
);
768 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
769 struct in_addr group_addr
, int num_sources
,
770 struct in_addr
*sources
)
772 struct interface
*ifp
= igmp
->interface
;
773 struct igmp_group
*group
;
775 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
779 * If the requested filter mode is INCLUDE *and* the requested source
780 * list is empty, then the entry corresponding to the requested
781 * interface and multicast address is deleted if present. If no such
782 * entry is present, the request is ignored.
785 /* non-existant group is created as INCLUDE {empty} */
786 group
= igmp_add_group_by_addr(igmp
, group_addr
);
791 group
= find_group_by_addr(igmp
, group_addr
);
796 if (group
->group_filtermode_isexcl
) {
798 toin_excl(group
, num_sources
, sources
);
801 toin_incl(group
, num_sources
, sources
);
805 static void toex_incl(struct igmp_group
*group
, int num_sources
,
806 struct in_addr
*sources
)
808 int num_sources_tosend
= 0;
811 zassert(!group
->group_filtermode_isexcl
);
813 /* Set DELETE flag for all known sources (A) */
814 source_mark_delete_flag(group
);
816 /* Clear off SEND flag from all known sources (A) */
817 source_clear_send_flag(group
->group_source_list
);
819 /* Scan received sources (B) */
820 for (i
= 0; i
< num_sources
; ++i
) {
821 struct igmp_source
*source
;
822 struct in_addr
*src_addr
;
824 src_addr
= sources
+ i
;
826 /* Lookup reported source (B) */
827 source
= igmp_find_source_by_addr(group
, *src_addr
);
829 /* If found, clear deletion flag: (A*B) */
830 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
831 /* and set SEND flag (A*B) */
832 IGMP_SOURCE_DO_SEND(source
->source_flags
);
833 ++num_sources_tosend
;
835 /* If source not found, create source with timer=0:
837 source
= source_new(group
, *src_addr
);
838 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
841 } /* Scan received sources (B) */
843 group
->group_filtermode_isexcl
= 1; /* boolean=true */
845 /* Delete all sources marked with DELETE flag (A-B) */
846 source_delete_by_flag(group
->group_source_list
);
848 /* Send sources marked with SEND flag: Q(G,A*B) */
849 if (num_sources_tosend
> 0) {
850 source_query_send_by_flag(group
, num_sources_tosend
);
853 zassert(group
->group_filtermode_isexcl
);
855 group_exclude_fwd_anysrc_ifempty(group
);
858 static void toex_excl(struct igmp_group
*group
, int num_sources
,
859 struct in_addr
*sources
)
861 int num_sources_tosend
= 0;
864 /* set DELETE flag for all known sources (X,Y) */
865 source_mark_delete_flag(group
);
867 /* clear off SEND flag from all known sources (X,Y) */
868 source_clear_send_flag(group
->group_source_list
);
870 if (num_sources
== 0) {
871 struct igmp_source
*source
;
872 struct in_addr any
= {.s_addr
= INADDR_ANY
};
874 source
= igmp_find_source_by_addr(group
, any
);
876 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
879 /* scan received sources (A) */
880 for (i
= 0; i
< num_sources
; ++i
) {
881 struct igmp_source
*source
;
882 struct in_addr
*src_addr
;
884 src_addr
= sources
+ i
;
886 /* lookup reported source (A) in known sources (X,Y) */
887 source
= igmp_find_source_by_addr(group
, *src_addr
);
889 /* if found, clear off DELETE flag from reported source
891 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
893 /* if not found, create source with Group Timer:
894 * (A-X-Y)=Group Timer */
895 long group_timer_msec
;
896 source
= source_new(group
, *src_addr
);
898 zassert(!source
->t_source_timer
); /* timer == 0 */
899 group_timer_msec
= igmp_group_timer_remain_msec(group
);
900 igmp_source_timer_on(group
, source
, group_timer_msec
);
901 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
903 /* make sure source is created with DELETE flag unset */
904 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
907 /* make sure reported source has DELETE flag unset */
908 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
910 if (source
->t_source_timer
) {
911 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
912 IGMP_SOURCE_DO_SEND(source
->source_flags
);
913 ++num_sources_tosend
;
916 } /* scan received sources (A) */
919 delete all sources marked with DELETE flag:
923 source_delete_by_flag(group
->group_source_list
);
925 /* send sources marked with SEND flag: Q(G,A-Y) */
926 if (num_sources_tosend
> 0) {
927 source_query_send_by_flag(group
, num_sources_tosend
);
931 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
932 struct in_addr group_addr
, int num_sources
,
933 struct in_addr
*sources
)
935 struct interface
*ifp
= igmp
->interface
;
936 struct igmp_group
*group
;
938 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
941 /* non-existant group is created as INCLUDE {empty} */
942 group
= igmp_add_group_by_addr(igmp
, group_addr
);
947 if (group
->group_filtermode_isexcl
) {
949 toex_excl(group
, num_sources
, sources
);
952 toex_incl(group
, num_sources
, sources
);
953 zassert(group
->group_filtermode_isexcl
);
955 zassert(group
->group_filtermode_isexcl
);
957 /* Group Timer=GMI */
958 igmp_group_reset_gmi(group
);
961 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
962 struct in_addr group_addr
, int num_sources
,
963 struct in_addr
*sources
)
965 on_trace(__PRETTY_FUNCTION__
, igmp
->interface
, from
, group_addr
,
966 num_sources
, sources
);
968 allow(igmp
, from
, group_addr
, num_sources
, sources
);
972 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
974 When transmitting a group specific query, if the group timer is
975 larger than LMQT, the "Suppress Router-Side Processing" bit is set
976 in the query message.
978 static void group_retransmit_group(struct igmp_group
*group
)
980 struct igmp_sock
*igmp
;
981 struct pim_interface
*pim_ifp
;
982 long lmqc
; /* Last Member Query Count */
983 long lmqi_msec
; /* Last Member Query Interval */
984 long lmqt_msec
; /* Last Member Query Time */
988 igmp
= group
->group_igmp_sock
;
989 pim_ifp
= igmp
->interface
->info
;
991 if (pim_ifp
->igmp_version
== 3) {
992 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
994 query_buf_size
= IGMP_V12_MSG_SIZE
;
997 char query_buf
[query_buf_size
];
999 lmqc
= igmp
->querier_robustness_variable
;
1000 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1001 lmqt_msec
= lmqc
* lmqi_msec
;
1004 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1006 When transmitting a group specific query, if the group timer is
1007 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1008 in the query message.
1010 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1012 if (PIM_DEBUG_IGMP_TRACE
) {
1013 char group_str
[INET_ADDRSTRLEN
];
1014 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1017 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1018 group_str
, igmp
->interface
->name
, s_flag
,
1019 group
->group_specific_query_retransmit_count
);
1023 RFC3376: 4.1.12. IP Destination Addresses for Queries
1025 Group-Specific and Group-and-Source-Specific Queries are sent with
1026 an IP destination address equal to the multicast address of
1030 igmp_send_query(pim_ifp
->igmp_version
, group
, igmp
->fd
,
1031 igmp
->interface
->name
, query_buf
, sizeof(query_buf
),
1032 0 /* num_sources_tosend */,
1033 group
->group_addr
/* dst_addr */,
1034 group
->group_addr
/* group_addr */,
1035 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1036 s_flag
, igmp
->querier_robustness_variable
,
1037 igmp
->querier_query_interval
);
1041 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1043 When building a group and source specific query for a group G, two
1044 separate query messages are sent for the group. The first one has
1045 the "Suppress Router-Side Processing" bit set and contains all the
1046 sources with retransmission state and timers greater than LMQT. The
1047 second has the "Suppress Router-Side Processing" bit clear and
1048 contains all the sources with retransmission state and timers lower
1049 or equal to LMQT. If either of the two calculated messages does not
1050 contain any sources, then its transmission is suppressed.
1052 static int group_retransmit_sources(struct igmp_group
*group
,
1053 int send_with_sflag_set
)
1055 struct igmp_sock
*igmp
;
1056 struct pim_interface
*pim_ifp
;
1057 long lmqc
; /* Last Member Query Count */
1058 long lmqi_msec
; /* Last Member Query Interval */
1059 long lmqt_msec
; /* Last Member Query Time */
1060 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1061 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1062 int query_buf1_max_sources
;
1063 int query_buf2_max_sources
;
1064 struct in_addr
*source_addr1
;
1065 struct in_addr
*source_addr2
;
1066 int num_sources_tosend1
;
1067 int num_sources_tosend2
;
1068 struct listnode
*src_node
;
1069 struct igmp_source
*src
;
1070 int num_retransmit_sources_left
= 0;
1072 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1073 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1075 igmp
= group
->group_igmp_sock
;
1076 pim_ifp
= igmp
->interface
->info
;
1078 lmqc
= igmp
->querier_robustness_variable
;
1079 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1080 lmqt_msec
= lmqc
* lmqi_msec
;
1082 /* Scan all group sources */
1083 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1085 /* Source has retransmission state? */
1086 if (src
->source_query_retransmit_count
< 1)
1089 if (--src
->source_query_retransmit_count
> 0) {
1090 ++num_retransmit_sources_left
;
1093 /* Copy source address into appropriate query buffer */
1094 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1095 *source_addr1
= src
->source_addr
;
1098 *source_addr2
= src
->source_addr
;
1103 num_sources_tosend1
=
1105 - (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1106 num_sources_tosend2
=
1108 - (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1110 if (PIM_DEBUG_IGMP_TRACE
) {
1111 char group_str
[INET_ADDRSTRLEN
];
1112 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1115 "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",
1116 group_str
, igmp
->interface
->name
, num_sources_tosend1
,
1117 num_sources_tosend2
, send_with_sflag_set
,
1118 num_retransmit_sources_left
);
1121 if (num_sources_tosend1
> 0) {
1123 Send group-and-source-specific query with s_flag set and all
1124 sources with timers greater than LMQT.
1127 if (send_with_sflag_set
) {
1129 query_buf1_max_sources
=
1130 (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
)
1132 if (num_sources_tosend1
> query_buf1_max_sources
) {
1133 char group_str
[INET_ADDRSTRLEN
];
1134 pim_inet4_dump("<group?>", group
->group_addr
,
1135 group_str
, sizeof(group_str
));
1137 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1138 __PRETTY_FUNCTION__
, group_str
,
1139 igmp
->interface
->name
,
1140 num_sources_tosend1
, sizeof(query_buf1
),
1141 query_buf1_max_sources
);
1144 RFC3376: 4.1.12. IP Destination Addresses for
1147 Group-Specific and Group-and-Source-Specific
1148 Queries are sent with
1149 an IP destination address equal to the
1150 multicast address of
1155 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1156 igmp
->interface
->name
, query_buf1
,
1157 sizeof(query_buf1
), num_sources_tosend1
,
1158 group
->group_addr
, group
->group_addr
,
1159 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1161 igmp
->querier_robustness_variable
,
1162 igmp
->querier_query_interval
);
1165 } /* send_with_sflag_set */
1168 if (num_sources_tosend2
> 0) {
1170 Send group-and-source-specific query with s_flag clear and all
1171 sources with timers lower or equal to LMQT.
1174 query_buf2_max_sources
=
1175 (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1176 if (num_sources_tosend2
> query_buf2_max_sources
) {
1177 char group_str
[INET_ADDRSTRLEN
];
1178 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1181 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1182 __PRETTY_FUNCTION__
, group_str
,
1183 igmp
->interface
->name
, num_sources_tosend2
,
1184 sizeof(query_buf2
), query_buf2_max_sources
);
1187 RFC3376: 4.1.12. IP Destination Addresses for Queries
1189 Group-Specific and Group-and-Source-Specific Queries
1191 an IP destination address equal to the multicast
1197 pim_ifp
->igmp_version
, group
, igmp
->fd
,
1198 igmp
->interface
->name
, query_buf2
,
1199 sizeof(query_buf2
), num_sources_tosend2
,
1200 group
->group_addr
, group
->group_addr
,
1201 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1203 igmp
->querier_robustness_variable
,
1204 igmp
->querier_query_interval
);
1208 return num_retransmit_sources_left
;
1211 static int igmp_group_retransmit(struct thread
*t
)
1213 struct igmp_group
*group
;
1214 int num_retransmit_sources_left
;
1215 int send_with_sflag_set
; /* boolean */
1217 group
= THREAD_ARG(t
);
1219 if (PIM_DEBUG_IGMP_TRACE
) {
1220 char group_str
[INET_ADDRSTRLEN
];
1221 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1223 zlog_debug("group_retransmit_timer: group %s on %s", group_str
,
1224 group
->group_igmp_sock
->interface
->name
);
1227 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1228 if (group
->group_specific_query_retransmit_count
> 0) {
1230 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1231 group_retransmit_group(group
);
1232 --group
->group_specific_query_retransmit_count
;
1236 If a group specific query is scheduled to be transmitted at
1238 same time as a group and source specific query for the same
1240 then transmission of the group and source specific message
1242 "Suppress Router-Side Processing" bit set may be suppressed.
1244 send_with_sflag_set
= 0; /* boolean=false */
1246 send_with_sflag_set
= 1; /* boolean=true */
1249 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1250 num_retransmit_sources_left
=
1251 group_retransmit_sources(group
, send_with_sflag_set
);
1254 Keep group retransmit timer running if there is any retransmit
1257 if ((num_retransmit_sources_left
> 0)
1258 || (group
->group_specific_query_retransmit_count
> 0)) {
1259 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 igmp_group
*group
)
1272 struct igmp_sock
*igmp
;
1273 struct pim_interface
*pim_ifp
;
1274 long lmqi_msec
; /* Last Member Query Interval */
1276 /* if group retransmit timer is running, do nothing */
1277 if (group
->t_group_query_retransmit_timer
) {
1281 igmp
= group
->group_igmp_sock
;
1282 pim_ifp
= igmp
->interface
->info
;
1284 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1286 if (PIM_DEBUG_IGMP_TRACE
) {
1287 char group_str
[INET_ADDRSTRLEN
];
1288 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1291 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1292 lmqi_msec
/ 1000, lmqi_msec
% 1000, group_str
,
1293 igmp
->interface
->name
);
1296 thread_add_timer_msec(master
, igmp_group_retransmit
, group
, lmqi_msec
,
1297 &group
->t_group_query_retransmit_timer
);
1300 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1302 return pim_time_timer_remain_msec(group
->t_group_timer
);
1305 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1307 return pim_time_timer_remain_msec(source
->t_source_timer
);
1311 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1313 static void group_query_send(struct igmp_group
*group
)
1315 long lmqc
; /* Last Member Query Count */
1317 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1319 /* lower group timer to lmqt */
1320 igmp_group_timer_lower_to_lmqt(group
);
1322 /* reset retransmission counter */
1323 group
->group_specific_query_retransmit_count
= lmqc
;
1325 /* immediately send group specific query (decrease retransmit counter by
1327 group_retransmit_group(group
);
1329 /* make sure group retransmit timer is running */
1330 group_retransmit_timer_on(group
);
1334 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1336 static void source_query_send_by_flag(struct igmp_group
*group
,
1337 int num_sources_tosend
)
1339 struct igmp_sock
*igmp
;
1340 struct pim_interface
*pim_ifp
;
1341 struct listnode
*src_node
;
1342 struct igmp_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 zassert(num_sources_tosend
> 0);
1349 igmp
= group
->group_igmp_sock
;
1350 pim_ifp
= igmp
->interface
->info
;
1352 lmqc
= igmp
->querier_robustness_variable
;
1353 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1354 lmqt_msec
= lmqc
* lmqi_msec
;
1357 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1360 (...) for each of the sources in X of group G, with source timer
1363 o Set number of retransmissions for each source to [Last Member
1365 o Lower source timer to LMQT.
1367 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1368 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1369 /* source "src" in X of group G */
1370 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1371 src
->source_query_retransmit_count
= lmqc
;
1372 igmp_source_timer_lower_to_lmqt(src
);
1377 /* send group-and-source specific queries */
1378 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1380 /* make sure group retransmit timer is running */
1381 group_retransmit_timer_on(group
);
1384 static void block_excl(struct igmp_group
*group
, int num_sources
,
1385 struct in_addr
*sources
)
1387 int num_sources_tosend
= 0;
1390 /* 1. clear off SEND flag from all known sources (X,Y) */
1391 source_clear_send_flag(group
->group_source_list
);
1393 /* 2. scan received sources (A) */
1394 for (i
= 0; i
< num_sources
; ++i
) {
1395 struct igmp_source
*source
;
1396 struct in_addr
*src_addr
;
1398 src_addr
= sources
+ i
;
1400 /* lookup reported source (A) in known sources (X,Y) */
1401 source
= igmp_find_source_by_addr(group
, *src_addr
);
1403 /* 3: if not found, create source with Group Timer:
1404 * (A-X-Y)=Group Timer */
1405 long group_timer_msec
;
1406 source
= source_new(group
, *src_addr
);
1408 zassert(!source
->t_source_timer
); /* timer == 0 */
1409 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1410 igmp_source_timer_on(group
, source
, group_timer_msec
);
1411 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1414 if (source
->t_source_timer
) {
1415 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1416 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1417 ++num_sources_tosend
;
1421 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1422 if (num_sources_tosend
> 0) {
1423 source_query_send_by_flag(group
, num_sources_tosend
);
1427 static void block_incl(struct igmp_group
*group
, int num_sources
,
1428 struct in_addr
*sources
)
1430 int num_sources_tosend
= 0;
1433 /* 1. clear off SEND flag from all known sources (B) */
1434 source_clear_send_flag(group
->group_source_list
);
1436 /* 2. scan received sources (A) */
1437 for (i
= 0; i
< num_sources
; ++i
) {
1438 struct igmp_source
*source
;
1439 struct in_addr
*src_addr
;
1441 src_addr
= sources
+ i
;
1443 /* lookup reported source (A) in known sources (B) */
1444 source
= igmp_find_source_by_addr(group
, *src_addr
);
1446 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1447 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1448 ++num_sources_tosend
;
1452 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1453 if (num_sources_tosend
> 0) {
1454 source_query_send_by_flag(group
, num_sources_tosend
);
1458 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1459 struct in_addr group_addr
, int num_sources
,
1460 struct in_addr
*sources
)
1462 struct interface
*ifp
= igmp
->interface
;
1463 struct igmp_group
*group
;
1465 on_trace(__PRETTY_FUNCTION__
, ifp
, from
, group_addr
, num_sources
,
1468 /* non-existant group is created as INCLUDE {empty} */
1469 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1474 if (group
->group_filtermode_isexcl
) {
1476 block_excl(group
, num_sources
, sources
);
1479 block_incl(group
, num_sources
, sources
);
1483 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1485 struct igmp_sock
*igmp
;
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 igmp
= group
->group_igmp_sock
;
1505 ifp
= igmp
->interface
;
1506 pim_ifp
= ifp
->info
;
1509 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1510 lmqc
= igmp
->querier_robustness_variable
;
1511 lmqt_msec
= PIM_IGMP_LMQT_MSEC(
1512 lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1514 if (PIM_DEBUG_IGMP_TRACE
) {
1515 char group_str
[INET_ADDRSTRLEN
];
1516 pim_inet4_dump("<group?>", group
->group_addr
, group_str
,
1519 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1520 __PRETTY_FUNCTION__
, group_str
, ifname
, lmqc
, lmqi_dsec
,
1524 zassert(group
->group_filtermode_isexcl
);
1526 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1529 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1531 struct igmp_group
*group
;
1532 struct igmp_sock
*igmp
;
1533 struct interface
*ifp
;
1534 struct pim_interface
*pim_ifp
;
1536 int lmqi_dsec
; /* Last Member Query Interval */
1537 int lmqc
; /* Last Member Query Count */
1538 int lmqt_msec
; /* Last Member Query Time */
1540 group
= source
->source_group
;
1541 igmp
= group
->group_igmp_sock
;
1542 ifp
= igmp
->interface
;
1543 pim_ifp
= ifp
->info
;
1546 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1547 lmqc
= igmp
->querier_robustness_variable
;
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 __PRETTY_FUNCTION__
, group_str
, source_str
, ifname
,
1561 lmqc
, lmqi_dsec
, lmqt_msec
);
1564 igmp_source_timer_on(group
, source
, lmqt_msec
);
1567 void igmp_v3_send_query(struct igmp_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 zassert(num_sources
>= 0);
1584 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1585 if (msg_size
> query_buf_size
) {
1587 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1588 __FILE__
, __PRETTY_FUNCTION__
, msg_size
,
1593 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1594 zassert((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_IGMP_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 (PIM_INADDR_IS_ANY(group_addr
)) {
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 __PRETTY_FUNCTION__
, dst_str
, ifname
, group_str
,
1692 void igmp_v3_recv_query(struct igmp_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
->igmp_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
->igmp_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 (PIM_INADDR_IS_ANY(group_addr
)) {
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 igmp_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 igmp_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 int igmp_v3_recv_report(struct igmp_sock
*igmp
, struct in_addr from
,
1829 const char *from_str
, char *igmp_msg
, int igmp_msg_len
)
1831 uint16_t recv_checksum
;
1834 uint8_t *group_record
;
1835 uint8_t *report_pastend
= (uint8_t *)igmp_msg
+ igmp_msg_len
;
1836 struct interface
*ifp
= igmp
->interface
;
1839 struct pim_interface
*pim_ifp
;
1841 if (igmp
->mtrace_only
)
1844 pim_ifp
= igmp
->interface
->info
;
1846 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1848 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1849 from_str
, ifp
->name
, igmp_msg_len
,
1850 IGMP_V3_MSG_MIN_SIZE
);
1854 recv_checksum
= *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1856 /* for computing checksum */
1857 *(uint16_t *)(igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1859 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1860 if (checksum
!= recv_checksum
) {
1862 "Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1863 from_str
, ifp
->name
, recv_checksum
, checksum
);
1867 /* Collecting IGMP Rx stats */
1868 igmp
->rx_stats
.report_v3
++;
1871 *(uint16_t *)(igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1872 if (num_groups
< 1) {
1874 "Recv IGMP report v3 from %s on %s: missing group records",
1875 from_str
, ifp
->name
);
1879 if (PIM_DEBUG_IGMP_PACKETS
) {
1881 "Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1882 from_str
, ifp
->name
, igmp_msg_len
, checksum
,
1886 group_record
= (uint8_t *)igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1889 for (i
= 0; i
< num_groups
; ++i
) {
1890 struct in_addr rec_group
;
1895 int rec_num_sources
;
1899 bool filtered
= false;
1901 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
)
1904 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1905 from_str
, ifp
->name
);
1909 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1911 group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1912 rec_num_sources
= ntohs(*(
1913 uint16_t *)(group_record
1914 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1917 group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
,
1918 sizeof(struct in_addr
));
1920 if (PIM_DEBUG_IGMP_PACKETS
) {
1922 "Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1923 from_str
, ifp
->name
, i
, rec_type
,
1924 rec_auxdatalen
, rec_num_sources
,
1925 inet_ntoa(rec_group
));
1930 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1932 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1934 if ((src
+ 4) > report_pastend
) {
1936 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1937 from_str
, ifp
->name
);
1941 if (PIM_DEBUG_IGMP_PACKETS
) {
1944 if (!inet_ntop(AF_INET
, src
, src_str
,
1946 sprintf(src_str
, "<source?>");
1949 "Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1950 from_str
, ifp
->name
, i
,
1951 inet_ntoa(rec_group
), src_str
);
1953 } /* for (sources) */
1956 lncb
.family
= AF_INET
;
1957 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1958 lncb
.prefixlen
= 24;
1961 g
.u
.prefix4
= rec_group
;
1964 /* determine filtering status for group */
1965 filtered
= pim_is_group_filtered(ifp
->info
, &rec_group
);
1967 if (PIM_DEBUG_IGMP_PACKETS
&& filtered
)
1969 "Filtering IGMPv3 group record %s from %s on %s per prefix-list %s",
1970 inet_ntoa(rec_group
), from_str
, ifp
->name
,
1971 pim_ifp
->boundary_oil_plist
);
1974 * If we receive a igmp report with the group in 224.0.0.0/24
1975 * then we should ignore it
1977 if (prefix_match(&lncb
, &g
))
1980 if (!local_ncb
&& !filtered
)
1982 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1983 igmpv3_report_isin(igmp
, from
, rec_group
,
1985 (struct in_addr
*)sources
);
1987 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1989 igmp
, from
, rec_group
, rec_num_sources
,
1990 (struct in_addr
*)sources
, 0);
1992 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
1993 igmpv3_report_toin(igmp
, from
, rec_group
,
1995 (struct in_addr
*)sources
);
1997 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
1998 igmpv3_report_toex(igmp
, from
, rec_group
,
2000 (struct in_addr
*)sources
);
2002 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
2003 igmpv3_report_allow(igmp
, from
, rec_group
,
2005 (struct in_addr
*)sources
);
2007 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
2008 igmpv3_report_block(igmp
, from
, rec_group
,
2010 (struct in_addr
*)sources
);
2014 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2015 from_str
, ifp
->name
, rec_type
);
2019 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
2022 } /* for (group records) */