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
,
43 struct interface
*ifp
, struct in_addr from
,
44 struct in_addr group_addr
,
45 int num_sources
, struct in_addr
*sources
)
47 if (PIM_DEBUG_IGMP_TRACE
) {
48 char from_str
[INET_ADDRSTRLEN
];
49 char group_str
[INET_ADDRSTRLEN
];
51 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
52 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
54 zlog_debug("%s: from %s on %s: group=%s sources=%d",
55 label
, 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
=
85 PIM_IGMP_GMI_MSEC(igmp
->querier_robustness_variable
,
86 igmp
->querier_query_interval
,
87 pim_ifp
->igmp_query_max_response_time_dsec
);
89 if (PIM_DEBUG_IGMP_TRACE
) {
90 char group_str
[INET_ADDRSTRLEN
];
91 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
92 zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
94 group_membership_interval_msec
/ 1000,
95 group_membership_interval_msec
% 1000,
100 RFC 3376: 6.2.2. Definition of Group Timers
102 The group timer is only used when a group is in EXCLUDE mode and
103 it represents the time for the *filter-mode* of the group to
104 expire and switch to INCLUDE mode.
106 zassert(group
->group_filtermode_isexcl
);
108 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
111 static int igmp_source_timer(struct thread
*t
)
113 struct igmp_source
*source
;
114 struct igmp_group
*group
;
116 source
= THREAD_ARG(t
);
118 group
= source
->source_group
;
120 if (PIM_DEBUG_IGMP_TRACE
) {
121 char group_str
[INET_ADDRSTRLEN
];
122 char source_str
[INET_ADDRSTRLEN
];
123 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
124 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
125 zlog_debug("%s: Source timer expired for group %s source %s on %s",
127 group_str
, source_str
,
128 group
->group_igmp_sock
->interface
->name
);
132 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
135 Filter-Mode Source Timer Value Action
136 ----------- ------------------ ------
137 INCLUDE TIMER == 0 Suggest to stop forwarding
138 traffic from source and
139 remove source record. If
140 there are no more source
141 records for the group, delete
144 EXCLUDE TIMER == 0 Suggest to not forward
146 (DO NOT remove record)
148 Source timer switched from (T > 0) to (T == 0): disable forwarding.
151 if (group
->group_filtermode_isexcl
) {
154 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 group
166 if (!listcount(group
->group_source_list
)) {
167 igmp_group_delete_empty_include(group
);
174 static void source_timer_off(struct igmp_group
*group
,
175 struct igmp_source
*source
)
177 if (!source
->t_source_timer
)
180 if (PIM_DEBUG_IGMP_TRACE
) {
181 char group_str
[INET_ADDRSTRLEN
];
182 char source_str
[INET_ADDRSTRLEN
];
183 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
184 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
185 zlog_debug("Cancelling TIMER event for group %s source %s on %s",
186 group_str
, source_str
,
187 group
->group_igmp_sock
->interface
->name
);
190 THREAD_OFF(source
->t_source_timer
);
193 static void igmp_source_timer_on(struct igmp_group
*group
,
194 struct igmp_source
*source
,
197 source_timer_off(group
, source
);
199 if (PIM_DEBUG_IGMP_EVENTS
) {
200 char group_str
[INET_ADDRSTRLEN
];
201 char source_str
[INET_ADDRSTRLEN
];
202 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
203 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
204 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
205 interval_msec
/ 1000,
206 interval_msec
% 1000,
207 group_str
, source_str
,
208 group
->group_igmp_sock
->interface
->name
);
211 thread_add_timer_msec(master
, igmp_source_timer
, source
, interval_msec
,
212 &source
->t_source_timer
);
215 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
217 Source timer switched from (T == 0) to (T > 0): enable forwarding.
219 igmp_source_forward_start(source
);
222 void igmp_source_reset_gmi(struct igmp_sock
*igmp
,
223 struct igmp_group
*group
,
224 struct igmp_source
*source
)
226 long group_membership_interval_msec
;
227 struct pim_interface
*pim_ifp
;
228 struct interface
*ifp
;
230 ifp
= igmp
->interface
;
233 group_membership_interval_msec
=
234 PIM_IGMP_GMI_MSEC(igmp
->querier_robustness_variable
,
235 igmp
->querier_query_interval
,
236 pim_ifp
->igmp_query_max_response_time_dsec
);
238 if (PIM_DEBUG_IGMP_TRACE
) {
239 char group_str
[INET_ADDRSTRLEN
];
240 char source_str
[INET_ADDRSTRLEN
];
242 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
243 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
245 zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
247 group_membership_interval_msec
/ 1000,
248 group_membership_interval_msec
% 1000,
253 igmp_source_timer_on(group
, source
,
254 group_membership_interval_msec
);
257 static void source_mark_delete_flag(struct igmp_group
*group
)
259 struct listnode
*src_node
;
260 struct igmp_source
*src
;
262 for (ALL_LIST_ELEMENTS_RO (group
->group_source_list
, src_node
, src
)) {
263 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
267 static void source_mark_send_flag (struct igmp_group
*group
)
269 struct listnode
*src_node
;
270 struct igmp_source
*src
;
272 for (ALL_LIST_ELEMENTS_RO (group
->group_source_list
, src_node
, src
)) {
273 IGMP_SOURCE_DO_SEND(src
->source_flags
);
277 static int source_mark_send_flag_by_timer (struct igmp_group
*group
)
279 struct listnode
*src_node
;
280 struct igmp_source
*src
;
281 int num_marked_sources
= 0;
283 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
284 /* Is source timer running? */
285 if (src
->t_source_timer
) {
286 IGMP_SOURCE_DO_SEND(src
->source_flags
);
287 ++num_marked_sources
;
290 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
294 return num_marked_sources
;
297 static void source_clear_send_flag(struct list
*source_list
)
299 struct listnode
*src_node
;
300 struct igmp_source
*src
;
302 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
303 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
308 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
310 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group
*group
)
312 zassert(group
->group_filtermode_isexcl
);
314 if (listcount(group
->group_source_list
) < 1) {
315 igmp_anysource_forward_start(group
);
319 void igmp_source_free(struct igmp_source
*source
)
321 /* make sure there is no source timer running */
322 zassert(!source
->t_source_timer
);
324 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
327 static void source_channel_oil_detach(struct igmp_source
*source
)
329 if (source
->source_channel_oil
) {
330 pim_channel_oil_del(source
->source_channel_oil
);
331 source
->source_channel_oil
= NULL
;
336 igmp_source_delete: stop fowarding, and delete the source
337 igmp_source_forward_stop: stop fowarding, but keep the source
339 void igmp_source_delete(struct igmp_source
*source
)
341 struct igmp_group
*group
;
344 group
= source
->source_group
;
346 if (PIM_DEBUG_IGMP_TRACE
) {
347 char group_str
[INET_ADDRSTRLEN
];
348 char source_str
[INET_ADDRSTRLEN
];
349 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
350 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
351 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d",
352 source_str
, group_str
,
353 group
->group_igmp_sock
->fd
,
354 group
->group_igmp_sock
->interface
->name
,
355 source
->source_channel_oil
? source
->source_channel_oil
->oil_ref_count
: 0);
358 source_timer_off(group
, source
);
359 igmp_source_forward_stop(source
);
361 /* sanity check that forwarding has been disabled */
362 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
363 char group_str
[INET_ADDRSTRLEN
];
364 char source_str
[INET_ADDRSTRLEN
];
365 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
366 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
367 zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
369 source_str
, group_str
,
370 group
->group_igmp_sock
->fd
,
371 group
->group_igmp_sock
->interface
->name
);
375 source_channel_oil_detach(source
);
378 notice that listnode_delete() can't be moved
379 into igmp_source_free() because the later is
380 called by list_delete_all_node()
382 listnode_delete(group
->group_source_list
, source
);
384 src
.s_addr
= source
->source_addr
.s_addr
;
385 igmp_source_free(source
);
387 /* Group source list is empty and current source is * then
388 *,G group going away so do not trigger start */
389 if (group
->group_filtermode_isexcl
&&
390 (listcount (group
->group_source_list
) != 0) &&
391 src
.s_addr
!= INADDR_ANY
)
393 group_exclude_fwd_anysrc_ifempty (group
);
397 static void source_delete_by_flag(struct list
*source_list
)
399 struct listnode
*src_node
;
400 struct listnode
*src_nextnode
;
401 struct igmp_source
*src
;
403 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
404 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
405 igmp_source_delete(src
);
408 void igmp_source_delete_expired(struct list
*source_list
)
410 struct listnode
*src_node
;
411 struct listnode
*src_nextnode
;
412 struct igmp_source
*src
;
414 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
415 if (!src
->t_source_timer
)
416 igmp_source_delete(src
);
419 struct igmp_source
*igmp_find_source_by_addr(struct igmp_group
*group
,
420 struct in_addr src_addr
)
422 struct listnode
*src_node
;
423 struct igmp_source
*src
;
425 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
426 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
433 source_new (struct igmp_group
*group
,
434 struct in_addr src_addr
)
436 struct igmp_source
*src
;
438 if (PIM_DEBUG_IGMP_TRACE
) {
439 char group_str
[INET_ADDRSTRLEN
];
440 char source_str
[INET_ADDRSTRLEN
];
441 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
442 pim_inet4_dump("<source?>", src_addr
, source_str
, sizeof(source_str
));
443 zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
444 source_str
, group_str
,
445 group
->group_igmp_sock
->fd
,
446 group
->group_igmp_sock
->interface
->name
);
449 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
451 zlog_warn("%s %s: XCALLOC() failure",
452 __FILE__
, __PRETTY_FUNCTION__
);
453 return 0; /* error, not found, could not create */
456 src
->t_source_timer
= NULL
;
457 src
->source_group
= group
; /* back pointer */
458 src
->source_addr
= src_addr
;
459 src
->source_creation
= pim_time_monotonic_sec();
460 src
->source_flags
= 0;
461 src
->source_query_retransmit_count
= 0;
462 src
->source_channel_oil
= NULL
;
464 listnode_add(group
->group_source_list
, src
);
466 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
467 igmp_anysource_forward_stop(group
);
472 static struct igmp_source
*add_source_by_addr(struct igmp_sock
*igmp
,
473 struct igmp_group
*group
,
474 struct in_addr src_addr
)
476 struct igmp_source
*src
;
478 src
= igmp_find_source_by_addr(group
, src_addr
);
483 src
= source_new(group
, src_addr
);
491 static void allow(struct igmp_sock
*igmp
, struct in_addr from
,
492 struct in_addr group_addr
,
493 int num_sources
, struct in_addr
*sources
)
495 struct igmp_source
*source
;
496 struct igmp_group
*group
;
499 /* non-existant group is created as INCLUDE {empty} */
500 group
= igmp_add_group_by_addr(igmp
, group_addr
);
505 /* scan received sources */
506 for (i
= 0; i
< num_sources
; ++i
) {
507 struct in_addr
*src_addr
;
509 src_addr
= sources
+ i
;
511 source
= add_source_by_addr(igmp
, group
, *src_addr
);
517 RFC 3376: 6.4.1. Reception of Current-State Records
519 When receiving IS_IN reports for groups in EXCLUDE mode is
520 sources should be moved from set with (timers = 0) to set with
523 igmp_source_reset_gmi() below, resetting the source timers to
524 GMI, accomplishes this.
526 igmp_source_reset_gmi(igmp
, group
, source
);
528 } /* scan received sources */
530 if ((num_sources
== 0) &&
531 (group
->group_filtermode_isexcl
) &&
532 (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
,
544 int num_sources
, struct in_addr
*sources
)
546 on_trace(__PRETTY_FUNCTION__
,
547 igmp
->interface
, from
, group_addr
, num_sources
, sources
);
549 allow(igmp
, from
, group_addr
, num_sources
, sources
);
552 static void isex_excl(struct igmp_group
*group
,
553 int num_sources
, 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
);
577 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
578 source
= source_new(group
, *src_addr
);
580 /* ugh, internal malloc failure, skip source */
583 zassert(!source
->t_source_timer
); /* timer == 0 */
584 igmp_source_reset_gmi(group
->group_igmp_sock
, group
, source
);
585 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
588 } /* scan received sources */
591 * If we are in isexcl mode and num_sources == 0
592 * than that means we have a *,g entry that
593 * needs to be handled
595 if (group
->group_filtermode_isexcl
&& num_sources
== 0)
597 struct in_addr star
= { .s_addr
= INADDR_ANY
};
598 source
= igmp_find_source_by_addr (group
, star
);
601 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
602 igmp_source_reset_gmi (group
->group_igmp_sock
, group
, source
);
606 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
607 source_delete_by_flag(group
->group_source_list
);
610 static void isex_incl(struct igmp_group
*group
,
611 int num_sources
, struct in_addr
*sources
)
616 zassert(!group
->group_filtermode_isexcl
);
618 /* I.1: set deletion flag for known sources (A) */
619 source_mark_delete_flag (group
);
621 /* scan received sources (B) */
622 for (i
= 0; i
< num_sources
; ++i
) {
623 struct igmp_source
*source
;
624 struct in_addr
*src_addr
;
626 src_addr
= sources
+ i
;
628 /* I.2: lookup reported source (B) */
629 source
= igmp_find_source_by_addr(group
, *src_addr
);
631 /* I.3: if found, clear deletion flag (A*B) */
632 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
635 /* I.4: if not found, create source with timer=0 (B-A) */
636 source
= source_new(group
, *src_addr
);
638 /* ugh, internal malloc failure, skip source */
641 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
644 } /* scan received sources */
646 /* I.5: delete all sources marked with deletion flag (A-B) */
647 source_delete_by_flag(group
->group_source_list
);
649 group
->group_filtermode_isexcl
= 1; /* boolean=true */
651 zassert(group
->group_filtermode_isexcl
);
653 group_exclude_fwd_anysrc_ifempty(group
);
656 void igmpv3_report_isex(struct igmp_sock
*igmp
, struct in_addr from
,
657 struct in_addr group_addr
,
658 int num_sources
, struct in_addr
*sources
,
659 int from_igmp_v2_report
)
661 struct interface
*ifp
= igmp
->interface
;
662 struct igmp_group
*group
;
664 on_trace(__PRETTY_FUNCTION__
,
665 ifp
, from
, group_addr
, num_sources
, sources
);
667 /* non-existant group is created as INCLUDE {empty} */
668 group
= igmp_add_group_by_addr(igmp
, group_addr
);
673 /* So we can display how we learned the group in our show command output */
674 if (from_igmp_v2_report
)
675 group
->igmp_version
= 2;
677 if (group
->group_filtermode_isexcl
) {
679 isex_excl(group
, num_sources
, sources
);
683 isex_incl(group
, num_sources
, sources
);
684 zassert(group
->group_filtermode_isexcl
);
687 zassert(group
->group_filtermode_isexcl
);
689 igmp_group_reset_gmi(group
);
692 static void toin_incl(struct igmp_group
*group
,
693 int num_sources
, struct in_addr
*sources
)
695 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
696 int num_sources_tosend
= listcount(group
->group_source_list
);
699 /* Set SEND flag for all known sources (A) */
700 source_mark_send_flag (group
);
702 /* Scan received sources (B) */
703 for (i
= 0; i
< num_sources
; ++i
) {
704 struct igmp_source
*source
;
705 struct in_addr
*src_addr
;
707 src_addr
= sources
+ i
;
709 /* Lookup reported source (B) */
710 source
= igmp_find_source_by_addr(group
, *src_addr
);
712 /* If found, clear SEND flag (A*B) */
713 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
714 --num_sources_tosend
;
717 /* If not found, create new source */
718 source
= source_new(group
, *src_addr
);
720 /* ugh, internal malloc failure, skip source */
726 igmp_source_reset_gmi(igmp
, group
, source
);
729 /* Send sources marked with SEND flag: Q(G,A-B) */
730 if (num_sources_tosend
> 0) {
731 source_query_send_by_flag(group
, num_sources_tosend
);
735 static void toin_excl(struct igmp_group
*group
,
736 int num_sources
, struct in_addr
*sources
)
738 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
739 int num_sources_tosend
;
742 /* Set SEND flag for X (sources with timer > 0) */
743 num_sources_tosend
= source_mark_send_flag_by_timer (group
);
745 /* Scan received sources (A) */
746 for (i
= 0; i
< num_sources
; ++i
) {
747 struct igmp_source
*source
;
748 struct in_addr
*src_addr
;
750 src_addr
= sources
+ i
;
752 /* Lookup reported source (A) */
753 source
= igmp_find_source_by_addr(group
, *src_addr
);
755 if (source
->t_source_timer
) {
756 /* If found and timer running, clear SEND flag (X*A) */
757 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
758 --num_sources_tosend
;
762 /* If not found, create new source */
763 source
= source_new(group
, *src_addr
);
765 /* ugh, internal malloc failure, skip source */
771 igmp_source_reset_gmi(igmp
, group
, source
);
774 /* Send sources marked with SEND flag: Q(G,X-A) */
775 if (num_sources_tosend
> 0) {
776 source_query_send_by_flag(group
, num_sources_tosend
);
780 group_query_send(group
);
783 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
784 struct in_addr group_addr
,
785 int num_sources
, struct in_addr
*sources
)
787 struct interface
*ifp
= igmp
->interface
;
788 struct igmp_group
*group
;
790 on_trace(__PRETTY_FUNCTION__
,
791 ifp
, from
, group_addr
, num_sources
, sources
);
794 * If the requested filter mode is INCLUDE *and* the requested source
795 * list is empty, then the entry corresponding to the requested
796 * interface and multicast address is deleted if present. If no such
797 * entry is present, the request is ignored.
801 /* non-existant group is created as INCLUDE {empty} */
802 group
= igmp_add_group_by_addr(igmp
, group_addr
);
809 group
= find_group_by_addr (igmp
, group_addr
);
814 if (group
->group_filtermode_isexcl
) {
816 toin_excl(group
, num_sources
, sources
);
820 toin_incl(group
, num_sources
, sources
);
824 static void toex_incl(struct igmp_group
*group
,
825 int num_sources
, struct in_addr
*sources
)
827 int num_sources_tosend
= 0;
830 zassert(!group
->group_filtermode_isexcl
);
832 /* Set DELETE flag for all known sources (A) */
833 source_mark_delete_flag (group
);
835 /* Clear off SEND flag from all known sources (A) */
836 source_clear_send_flag(group
->group_source_list
);
838 /* Scan received sources (B) */
839 for (i
= 0; i
< num_sources
; ++i
) {
840 struct igmp_source
*source
;
841 struct in_addr
*src_addr
;
843 src_addr
= sources
+ i
;
845 /* Lookup reported source (B) */
846 source
= igmp_find_source_by_addr(group
, *src_addr
);
848 /* If found, clear deletion flag: (A*B) */
849 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
850 /* and set SEND flag (A*B) */
851 IGMP_SOURCE_DO_SEND(source
->source_flags
);
852 ++num_sources_tosend
;
855 /* If source not found, create source with timer=0: (B-A)=0 */
856 source
= source_new(group
, *src_addr
);
858 /* ugh, internal malloc failure, skip source */
861 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
864 } /* Scan received sources (B) */
866 group
->group_filtermode_isexcl
= 1; /* boolean=true */
868 /* Delete all sources marked with DELETE flag (A-B) */
869 source_delete_by_flag(group
->group_source_list
);
871 /* Send sources marked with SEND flag: Q(G,A*B) */
872 if (num_sources_tosend
> 0) {
873 source_query_send_by_flag(group
, num_sources_tosend
);
876 zassert(group
->group_filtermode_isexcl
);
878 group_exclude_fwd_anysrc_ifempty(group
);
881 static void toex_excl(struct igmp_group
*group
,
882 int num_sources
, struct in_addr
*sources
)
884 int num_sources_tosend
= 0;
887 /* set DELETE flag for all known sources (X,Y) */
888 source_mark_delete_flag (group
);
890 /* clear off SEND flag from all known sources (X,Y) */
891 source_clear_send_flag(group
->group_source_list
);
893 if (num_sources
== 0)
895 struct igmp_source
*source
;
896 struct in_addr any
= { .s_addr
= INADDR_ANY
};
898 source
= igmp_find_source_by_addr (group
, any
);
900 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
903 /* scan received sources (A) */
904 for (i
= 0; i
< num_sources
; ++i
) {
905 struct igmp_source
*source
;
906 struct in_addr
*src_addr
;
908 src_addr
= sources
+ i
;
910 /* lookup reported source (A) in known sources (X,Y) */
911 source
= igmp_find_source_by_addr(group
, *src_addr
);
913 /* if found, clear off DELETE flag from reported source (A) */
914 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
917 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
918 long group_timer_msec
;
919 source
= source_new(group
, *src_addr
);
921 /* ugh, internal malloc failure, skip source */
925 zassert(!source
->t_source_timer
); /* timer == 0 */
926 group_timer_msec
= igmp_group_timer_remain_msec(group
);
927 igmp_source_timer_on(group
, source
, group_timer_msec
);
928 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
930 /* make sure source is created with DELETE flag unset */
931 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
934 /* make sure reported source has DELETE flag unset */
935 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
937 if (source
->t_source_timer
) {
938 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
939 IGMP_SOURCE_DO_SEND(source
->source_flags
);
940 ++num_sources_tosend
;
943 } /* scan received sources (A) */
946 delete all sources marked with DELETE flag:
950 source_delete_by_flag(group
->group_source_list
);
952 /* send sources marked with SEND flag: Q(G,A-Y) */
953 if (num_sources_tosend
> 0) {
954 source_query_send_by_flag(group
, num_sources_tosend
);
958 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
959 struct in_addr group_addr
,
960 int num_sources
, struct in_addr
*sources
)
962 struct interface
*ifp
= igmp
->interface
;
963 struct igmp_group
*group
;
965 on_trace(__PRETTY_FUNCTION__
,
966 ifp
, from
, group_addr
, num_sources
, sources
);
968 /* non-existant group is created as INCLUDE {empty} */
969 group
= igmp_add_group_by_addr(igmp
, group_addr
);
974 if (group
->group_filtermode_isexcl
) {
976 toex_excl(group
, num_sources
, sources
);
980 toex_incl(group
, num_sources
, sources
);
981 zassert(group
->group_filtermode_isexcl
);
983 zassert(group
->group_filtermode_isexcl
);
985 /* Group Timer=GMI */
986 igmp_group_reset_gmi(group
);
989 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
990 struct in_addr group_addr
,
991 int num_sources
, struct in_addr
*sources
)
993 on_trace(__PRETTY_FUNCTION__
,
994 igmp
->interface
, from
, group_addr
, num_sources
, sources
);
996 allow(igmp
, from
, group_addr
, num_sources
, sources
);
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 igmp_group
*group
)
1008 struct igmp_sock
*igmp
;
1009 struct pim_interface
*pim_ifp
;
1010 long lmqc
; /* Last Member Query Count */
1011 long lmqi_msec
; /* Last Member Query Interval */
1012 long lmqt_msec
; /* Last Member Query Time */
1016 igmp
= group
->group_igmp_sock
;
1017 pim_ifp
= igmp
->interface
->info
;
1019 if (pim_ifp
->igmp_version
== 3) {
1020 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1022 query_buf_size
= IGMP_V12_MSG_SIZE
;
1025 char query_buf
[query_buf_size
];
1027 lmqc
= igmp
->querier_robustness_variable
;
1028 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1029 lmqt_msec
= lmqc
* lmqi_msec
;
1032 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1034 When transmitting a group specific query, if the group timer is
1035 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1036 in the query message.
1038 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1040 if (PIM_DEBUG_IGMP_TRACE
) {
1041 char group_str
[INET_ADDRSTRLEN
];
1042 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1043 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1044 group_str
, igmp
->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(pim_ifp
->igmp_version
,
1059 igmp
->interface
->name
,
1062 0 /* num_sources_tosend */,
1063 group
->group_addr
/* dst_addr */,
1064 group
->group_addr
/* group_addr */,
1065 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1067 igmp
->querier_robustness_variable
,
1068 igmp
->querier_query_interval
);
1072 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1074 When building a group and source specific query for a group G, two
1075 separate query messages are sent for the group. The first one has
1076 the "Suppress Router-Side Processing" bit set and contains all the
1077 sources with retransmission state and timers greater than LMQT. The
1078 second has the "Suppress Router-Side Processing" bit clear and
1079 contains all the sources with retransmission state and timers lower
1080 or equal to LMQT. If either of the two calculated messages does not
1081 contain any sources, then its transmission is suppressed.
1083 static int group_retransmit_sources(struct igmp_group
*group
,
1084 int send_with_sflag_set
)
1086 struct igmp_sock
*igmp
;
1087 struct pim_interface
*pim_ifp
;
1088 long lmqc
; /* Last Member Query Count */
1089 long lmqi_msec
; /* Last Member Query Interval */
1090 long lmqt_msec
; /* Last Member Query Time */
1091 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1092 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1093 int query_buf1_max_sources
;
1094 int query_buf2_max_sources
;
1095 struct in_addr
*source_addr1
;
1096 struct in_addr
*source_addr2
;
1097 int num_sources_tosend1
;
1098 int num_sources_tosend2
;
1099 struct listnode
*src_node
;
1100 struct igmp_source
*src
;
1101 int num_retransmit_sources_left
= 0;
1103 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1104 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1106 igmp
= group
->group_igmp_sock
;
1107 pim_ifp
= igmp
->interface
->info
;
1109 lmqc
= igmp
->querier_robustness_variable
;
1110 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1111 lmqt_msec
= lmqc
* lmqi_msec
;
1113 /* Scan all group sources */
1114 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1116 /* Source has retransmission state? */
1117 if (src
->source_query_retransmit_count
< 1)
1120 if (--src
->source_query_retransmit_count
> 0) {
1121 ++num_retransmit_sources_left
;
1124 /* Copy source address into appropriate query buffer */
1125 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1126 *source_addr1
= src
->source_addr
;
1130 *source_addr2
= src
->source_addr
;
1136 num_sources_tosend1
= source_addr1
- (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1137 num_sources_tosend2
= source_addr2
- (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1139 if (PIM_DEBUG_IGMP_TRACE
) {
1140 char group_str
[INET_ADDRSTRLEN
];
1141 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1142 zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
1143 group_str
, igmp
->interface
->name
,
1144 num_sources_tosend1
,
1145 num_sources_tosend2
,
1146 send_with_sflag_set
,
1147 num_retransmit_sources_left
);
1150 if (num_sources_tosend1
> 0) {
1152 Send group-and-source-specific query with s_flag set and all
1153 sources with timers greater than LMQT.
1156 if (send_with_sflag_set
) {
1158 query_buf1_max_sources
= (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1159 if (num_sources_tosend1
> query_buf1_max_sources
) {
1160 char group_str
[INET_ADDRSTRLEN
];
1161 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1162 zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1163 __PRETTY_FUNCTION__
, group_str
, igmp
->interface
->name
,
1164 num_sources_tosend1
, sizeof(query_buf1
), query_buf1_max_sources
);
1168 RFC3376: 4.1.12. IP Destination Addresses for Queries
1170 Group-Specific and Group-and-Source-Specific Queries are sent with
1171 an IP destination address equal to the multicast address of
1175 igmp_send_query(pim_ifp
->igmp_version
,
1178 igmp
->interface
->name
,
1181 num_sources_tosend1
,
1184 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1186 igmp
->querier_robustness_variable
,
1187 igmp
->querier_query_interval
);
1190 } /* send_with_sflag_set */
1194 if (num_sources_tosend2
> 0) {
1196 Send group-and-source-specific query with s_flag clear and all
1197 sources with timers lower or equal to LMQT.
1200 query_buf2_max_sources
= (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1201 if (num_sources_tosend2
> query_buf2_max_sources
) {
1202 char group_str
[INET_ADDRSTRLEN
];
1203 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1204 zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1205 __PRETTY_FUNCTION__
, group_str
, igmp
->interface
->name
,
1206 num_sources_tosend2
, sizeof(query_buf2
), query_buf2_max_sources
);
1210 RFC3376: 4.1.12. IP Destination Addresses for Queries
1212 Group-Specific and Group-and-Source-Specific Queries are sent with
1213 an IP destination address equal to the multicast address of
1217 igmp_send_query(pim_ifp
->igmp_version
,
1220 igmp
->interface
->name
,
1223 num_sources_tosend2
,
1226 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1228 igmp
->querier_robustness_variable
,
1229 igmp
->querier_query_interval
);
1233 return num_retransmit_sources_left
;
1236 static int igmp_group_retransmit(struct thread
*t
)
1238 struct igmp_group
*group
;
1239 int num_retransmit_sources_left
;
1240 int send_with_sflag_set
; /* boolean */
1242 group
= THREAD_ARG(t
);
1244 if (PIM_DEBUG_IGMP_TRACE
) {
1245 char group_str
[INET_ADDRSTRLEN
];
1246 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1247 zlog_debug("group_retransmit_timer: group %s on %s",
1248 group_str
, group
->group_igmp_sock
->interface
->name
);
1251 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1252 if (group
->group_specific_query_retransmit_count
> 0) {
1254 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1255 group_retransmit_group(group
);
1256 --group
->group_specific_query_retransmit_count
;
1260 If a group specific query is scheduled to be transmitted at the
1261 same time as a group and source specific query for the same group,
1262 then transmission of the group and source specific message with the
1263 "Suppress Router-Side Processing" bit set may be suppressed.
1265 send_with_sflag_set
= 0; /* boolean=false */
1268 send_with_sflag_set
= 1; /* boolean=true */
1271 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1272 num_retransmit_sources_left
= group_retransmit_sources(group
,
1273 send_with_sflag_set
);
1276 Keep group retransmit timer running if there is any retransmit
1279 if ((num_retransmit_sources_left
> 0) ||
1280 (group
->group_specific_query_retransmit_count
> 0)) {
1281 group_retransmit_timer_on(group
);
1288 group_retransmit_timer_on:
1289 if group retransmit timer isn't running, starts it;
1290 otherwise, do nothing
1292 static void group_retransmit_timer_on(struct igmp_group
*group
)
1294 struct igmp_sock
*igmp
;
1295 struct pim_interface
*pim_ifp
;
1296 long lmqi_msec
; /* Last Member Query Interval */
1298 /* if group retransmit timer is running, do nothing */
1299 if (group
->t_group_query_retransmit_timer
) {
1303 igmp
= group
->group_igmp_sock
;
1304 pim_ifp
= igmp
->interface
->info
;
1306 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1308 if (PIM_DEBUG_IGMP_TRACE
) {
1309 char group_str
[INET_ADDRSTRLEN
];
1310 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1311 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1315 igmp
->interface
->name
);
1318 thread_add_timer_msec(master
, igmp_group_retransmit
, group
, lmqi_msec
,
1319 &group
->t_group_query_retransmit_timer
);
1322 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1324 return pim_time_timer_remain_msec(group
->t_group_timer
);
1327 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1329 return pim_time_timer_remain_msec(source
->t_source_timer
);
1333 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1335 static void group_query_send(struct igmp_group
*group
)
1337 long lmqc
; /* Last Member Query Count */
1339 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1341 /* lower group timer to lmqt */
1342 igmp_group_timer_lower_to_lmqt(group
);
1344 /* reset retransmission counter */
1345 group
->group_specific_query_retransmit_count
= lmqc
;
1347 /* immediately send group specific query (decrease retransmit counter by 1)*/
1348 group_retransmit_group(group
);
1350 /* make sure group retransmit timer is running */
1351 group_retransmit_timer_on(group
);
1355 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1357 static void source_query_send_by_flag(struct igmp_group
*group
,
1358 int num_sources_tosend
)
1360 struct igmp_sock
*igmp
;
1361 struct pim_interface
*pim_ifp
;
1362 struct listnode
*src_node
;
1363 struct igmp_source
*src
;
1364 long lmqc
; /* Last Member Query Count */
1365 long lmqi_msec
; /* Last Member Query Interval */
1366 long lmqt_msec
; /* Last Member Query Time */
1368 zassert(num_sources_tosend
> 0);
1370 igmp
= group
->group_igmp_sock
;
1371 pim_ifp
= igmp
->interface
->info
;
1373 lmqc
= igmp
->querier_robustness_variable
;
1374 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1375 lmqt_msec
= lmqc
* lmqi_msec
;
1378 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1380 (...) for each of the sources in X of group G, with source timer larger
1382 o Set number of retransmissions for each source to [Last Member
1384 o Lower source timer to LMQT.
1386 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1387 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1388 /* source "src" in X of group G */
1389 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1390 src
->source_query_retransmit_count
= lmqc
;
1391 igmp_source_timer_lower_to_lmqt(src
);
1396 /* send group-and-source specific queries */
1397 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1399 /* make sure group retransmit timer is running */
1400 group_retransmit_timer_on(group
);
1403 static void block_excl(struct igmp_group
*group
,
1404 int num_sources
, struct in_addr
*sources
)
1406 int num_sources_tosend
= 0;
1409 /* 1. clear off SEND flag from all known sources (X,Y) */
1410 source_clear_send_flag(group
->group_source_list
);
1412 /* 2. scan received sources (A) */
1413 for (i
= 0; i
< num_sources
; ++i
) {
1414 struct igmp_source
*source
;
1415 struct in_addr
*src_addr
;
1417 src_addr
= sources
+ i
;
1419 /* lookup reported source (A) in known sources (X,Y) */
1420 source
= igmp_find_source_by_addr(group
, *src_addr
);
1422 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1423 long group_timer_msec
;
1424 source
= source_new(group
, *src_addr
);
1426 /* ugh, internal malloc failure, skip source */
1430 zassert(!source
->t_source_timer
); /* timer == 0 */
1431 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1432 igmp_source_timer_on(group
, source
, group_timer_msec
);
1433 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1436 if (source
->t_source_timer
) {
1437 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1438 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1439 ++num_sources_tosend
;
1443 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1444 if (num_sources_tosend
> 0) {
1445 source_query_send_by_flag(group
, num_sources_tosend
);
1449 static void block_incl(struct igmp_group
*group
,
1450 int num_sources
, struct in_addr
*sources
)
1452 int num_sources_tosend
= 0;
1455 /* 1. clear off SEND flag from all known sources (B) */
1456 source_clear_send_flag(group
->group_source_list
);
1458 /* 2. scan received sources (A) */
1459 for (i
= 0; i
< num_sources
; ++i
) {
1460 struct igmp_source
*source
;
1461 struct in_addr
*src_addr
;
1463 src_addr
= sources
+ i
;
1465 /* lookup reported source (A) in known sources (B) */
1466 source
= igmp_find_source_by_addr(group
, *src_addr
);
1468 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1469 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1470 ++num_sources_tosend
;
1474 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1475 if (num_sources_tosend
> 0) {
1476 source_query_send_by_flag(group
, num_sources_tosend
);
1480 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1481 struct in_addr group_addr
,
1482 int num_sources
, struct in_addr
*sources
)
1484 struct interface
*ifp
= igmp
->interface
;
1485 struct igmp_group
*group
;
1487 on_trace(__PRETTY_FUNCTION__
,
1488 ifp
, from
, group_addr
, num_sources
, sources
);
1490 /* non-existant group is created as INCLUDE {empty} */
1491 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1496 if (group
->group_filtermode_isexcl
) {
1498 block_excl(group
, num_sources
, sources
);
1502 block_incl(group
, num_sources
, sources
);
1506 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1508 struct igmp_sock
*igmp
;
1509 struct interface
*ifp
;
1510 struct pim_interface
*pim_ifp
;
1512 int lmqi_dsec
; /* Last Member Query Interval */
1513 int lmqc
; /* Last Member Query Count */
1514 int lmqt_msec
; /* Last Member Query Time */
1517 RFC 3376: 6.2.2. Definition of Group Timers
1519 The group timer is only used when a group is in EXCLUDE mode and
1520 it represents the time for the *filter-mode* of the group to
1521 expire and switch to INCLUDE mode.
1523 if (!group
->group_filtermode_isexcl
) {
1527 igmp
= group
->group_igmp_sock
;
1528 ifp
= igmp
->interface
;
1529 pim_ifp
= ifp
->info
;
1532 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1533 lmqc
= igmp
->querier_robustness_variable
;
1534 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1536 if (PIM_DEBUG_IGMP_TRACE
) {
1537 char group_str
[INET_ADDRSTRLEN
];
1538 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1539 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1540 __PRETTY_FUNCTION__
,
1542 lmqc
, lmqi_dsec
, lmqt_msec
);
1545 zassert(group
->group_filtermode_isexcl
);
1547 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1550 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1552 struct igmp_group
*group
;
1553 struct igmp_sock
*igmp
;
1554 struct interface
*ifp
;
1555 struct pim_interface
*pim_ifp
;
1557 int lmqi_dsec
; /* Last Member Query Interval */
1558 int lmqc
; /* Last Member Query Count */
1559 int lmqt_msec
; /* Last Member Query Time */
1561 group
= source
->source_group
;
1562 igmp
= group
->group_igmp_sock
;
1563 ifp
= igmp
->interface
;
1564 pim_ifp
= ifp
->info
;
1567 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1568 lmqc
= igmp
->querier_robustness_variable
;
1569 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1571 if (PIM_DEBUG_IGMP_TRACE
) {
1572 char group_str
[INET_ADDRSTRLEN
];
1573 char source_str
[INET_ADDRSTRLEN
];
1574 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1575 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
1576 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1577 __PRETTY_FUNCTION__
,
1578 group_str
, source_str
, ifname
,
1579 lmqc
, lmqi_dsec
, lmqt_msec
);
1582 igmp_source_timer_on(group
, source
, lmqt_msec
);
1586 igmp_v3_send_query (struct igmp_group
*group
,
1592 struct in_addr dst_addr
,
1593 struct in_addr group_addr
,
1594 int query_max_response_time_dsec
,
1596 uint8_t querier_robustness_variable
,
1597 uint16_t querier_query_interval
)
1600 uint8_t max_resp_code
;
1603 struct sockaddr_in to
;
1607 zassert(num_sources
>= 0);
1609 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1610 if (msg_size
> query_buf_size
) {
1611 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1612 __FILE__
, __PRETTY_FUNCTION__
,
1613 msg_size
, query_buf_size
);
1617 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1618 zassert((s_flag
== 0) || (s_flag
== 1));
1620 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1621 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1624 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1626 If non-zero, the QRV field contains the [Robustness Variable]
1627 value used by the querier, i.e., the sender of the Query. If the
1628 querier's [Robustness Variable] exceeds 7, the maximum value of
1629 the QRV field, the QRV is set to zero.
1631 if (querier_robustness_variable
> 7) {
1632 querier_robustness_variable
= 0;
1635 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1636 query_buf
[1] = max_resp_code
;
1637 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = 0; /* for computing checksum */
1638 memcpy(query_buf
+4, &group_addr
, sizeof(struct in_addr
));
1640 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1641 query_buf
[9] = qqic
;
1642 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) = htons(num_sources
);
1644 checksum
= in_cksum(query_buf
, msg_size
);
1645 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1647 if (PIM_DEBUG_IGMP_PACKETS
) {
1648 char dst_str
[INET_ADDRSTRLEN
];
1649 char group_str
[INET_ADDRSTRLEN
];
1650 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1651 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1652 zlog_debug("Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1653 dst_str
, ifname
, group_str
,
1654 num_sources
, msg_size
, s_flag
, querier_robustness_variable
,
1655 querier_query_interval
, qqic
);
1658 memset(&to
, 0, sizeof(to
));
1659 to
.sin_family
= AF_INET
;
1660 to
.sin_addr
= dst_addr
;
1663 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1664 (struct sockaddr
*)&to
, tolen
);
1665 if (sent
!= (ssize_t
) msg_size
) {
1666 char dst_str
[INET_ADDRSTRLEN
];
1667 char group_str
[INET_ADDRSTRLEN
];
1668 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1669 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1671 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1672 dst_str
, ifname
, group_str
, msg_size
, errno
, safe_strerror(errno
));
1675 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1676 dst_str
, ifname
, group_str
, msg_size
, sent
);
1682 s_flag sanity test: s_flag must be set for general queries
1684 RFC 3376: 6.6.1. Timer Updates
1686 When a router sends or receives a query with a clear Suppress
1687 Router-Side Processing flag, it must update its timers to reflect
1688 the correct timeout values for the group or sources being queried.
1690 General queries don't trigger timer update.
1693 /* general query? */
1694 if (PIM_INADDR_IS_ANY(group_addr
)) {
1695 char dst_str
[INET_ADDRSTRLEN
];
1696 char group_str
[INET_ADDRSTRLEN
];
1697 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1698 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1699 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1700 __PRETTY_FUNCTION__
,
1701 dst_str
, ifname
, group_str
, num_sources
);
1707 igmp_v3_recv_query (struct igmp_sock
*igmp
, const char *from_str
, char *igmp_msg
)
1709 struct interface
*ifp
;
1710 struct pim_interface
*pim_ifp
;
1711 struct in_addr group_addr
;
1712 uint8_t resv_s_qrv
= 0;
1717 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1718 ifp
= igmp
->interface
;
1719 pim_ifp
= ifp
->info
;
1722 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1724 * Routers adopt the QRV value from the most recently received Query
1725 * as their own [Robustness Variable] value, unless that most
1726 * recently received QRV was zero, in which case the receivers use
1727 * the default [Robustness Variable] value specified in section 8.1
1728 * or a statically configured value.
1730 resv_s_qrv
= igmp_msg
[8];
1731 qrv
= 7 & resv_s_qrv
;
1732 igmp
->querier_robustness_variable
= qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
1735 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1737 * Multicast routers that are not the current querier adopt the QQI
1738 * value from the most recently received Query as their own [Query
1739 * Interval] value, unless that most recently received QQI was zero,
1740 * in which case the receiving routers use the default.
1742 if (igmp
->t_other_querier_timer
) {
1743 /* other querier present */
1747 qqi
= igmp_msg_decode8to16(qqic
);
1748 igmp
->querier_query_interval
= qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
1750 if (PIM_DEBUG_IGMP_TRACE
) {
1751 char ifaddr_str
[INET_ADDRSTRLEN
];
1752 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
1753 zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1755 qqi
? "recv-non-default" : "default",
1756 igmp
->querier_query_interval
,
1763 * RFC 3376: 6.6.1. Timer Updates
1765 * When a router sends or receives a query with a clear Suppress
1766 * Router-Side Processing flag, it must update its timers to reflect
1767 * the correct timeout values for the group or sources being queried.
1769 * General queries don't trigger timer update.
1771 s_flag
= (1 << 3) & resv_s_qrv
;
1774 /* s_flag is clear */
1776 if (PIM_INADDR_IS_ANY(group_addr
)) {
1777 /* this is a general query */
1778 /* log that general query should have the s_flag set */
1779 zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1780 from_str
, ifp
->name
);
1782 struct igmp_group
*group
;
1784 /* this is a non-general query: perform timer updates */
1786 group
= find_group_by_addr(igmp
, group_addr
);
1788 int recv_num_sources
= ntohs(*(uint16_t *)(igmp_msg
+ IGMP_V3_NUMSOURCES_OFFSET
));
1791 * RFC 3376: 6.6.1. Timer Updates
1792 * Query Q(G,A): Source Timer for sources in A are lowered to LMQT
1793 * Query Q(G): Group Timer is lowered to LMQT
1795 if (recv_num_sources
< 1) {
1796 /* Query Q(G): Group Timer is lowered to LMQT */
1798 igmp_group_timer_lower_to_lmqt(group
);
1800 /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
1802 /* Scan sources in query and lower their timers to LMQT */
1803 struct in_addr
*sources
= (struct in_addr
*)(igmp_msg
+ IGMP_V3_SOURCES_OFFSET
);
1804 for (i
= 0; i
< recv_num_sources
; ++i
) {
1805 struct in_addr src_addr
;
1806 struct igmp_source
*src
;
1807 memcpy(&src_addr
, sources
+ i
, sizeof(struct in_addr
));
1808 src
= igmp_find_source_by_addr(group
, src_addr
);
1810 igmp_source_timer_lower_to_lmqt(src
);
1815 char group_str
[INET_ADDRSTRLEN
];
1816 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1817 zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update",
1818 from_str
, ifp
->name
, group_str
);
1821 } /* s_flag is clear: timer updates */
1825 igmp_v3_recv_report (struct igmp_sock
*igmp
,
1826 struct in_addr from
, const char *from_str
,
1827 char *igmp_msg
, int igmp_msg_len
)
1829 uint16_t recv_checksum
;
1832 uint8_t *group_record
;
1833 uint8_t *report_pastend
= (uint8_t *) igmp_msg
+ igmp_msg_len
;
1834 struct interface
*ifp
= igmp
->interface
;
1838 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1839 zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1840 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V3_MSG_MIN_SIZE
);
1844 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1846 /* for computing checksum */
1847 *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1849 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1850 if (checksum
!= recv_checksum
) {
1851 zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1852 from_str
, ifp
->name
, recv_checksum
, checksum
);
1856 num_groups
= ntohs(*(uint16_t *) (igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1857 if (num_groups
< 1) {
1858 zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
1859 from_str
, ifp
->name
);
1863 if (PIM_DEBUG_IGMP_PACKETS
) {
1864 zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1865 from_str
, ifp
->name
, igmp_msg_len
, checksum
, num_groups
);
1868 group_record
= (uint8_t *) igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1871 for (i
= 0; i
< num_groups
; ++i
) {
1872 struct in_addr rec_group
;
1877 int rec_num_sources
;
1882 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
) > report_pastend
) {
1883 zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
1884 from_str
, ifp
->name
);
1888 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1889 rec_auxdatalen
= group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1890 rec_num_sources
= ntohs(* (uint16_t *) (group_record
+ IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1892 memcpy(&rec_group
, group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
, sizeof(struct in_addr
));
1894 if (PIM_DEBUG_IGMP_PACKETS
) {
1895 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1896 from_str
, ifp
->name
, i
, rec_type
, rec_auxdatalen
, rec_num_sources
, inet_ntoa(rec_group
));
1901 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1903 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1905 if ((src
+ 4) > report_pastend
) {
1906 zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
1907 from_str
, ifp
->name
);
1911 if (PIM_DEBUG_IGMP_PACKETS
) {
1914 if (!inet_ntop(AF_INET
, src
, src_str
, sizeof(src_str
)))
1915 sprintf(src_str
, "<source?>");
1917 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1918 from_str
, ifp
->name
, i
, inet_ntoa(rec_group
), src_str
);
1920 } /* for (sources) */
1923 lncb
.family
= AF_INET
;
1924 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1925 lncb
.prefixlen
= 24;
1928 g
.u
.prefix4
= rec_group
;
1931 * If we receive a igmp report with the group in 224.0.0.0/24
1932 * then we should ignore it
1934 if (prefix_match(&lncb
, &g
))
1939 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1940 igmpv3_report_isin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1942 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1943 igmpv3_report_isex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
, 0);
1945 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
1946 igmpv3_report_toin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1948 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
1949 igmpv3_report_toex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1951 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
1952 igmpv3_report_allow(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1954 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
1955 igmpv3_report_block(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1958 zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
1959 from_str
, ifp
->name
, rec_type
);
1962 group_record
+= 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
1965 } /* for (group records) */