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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
27 #include "pim_iface.h"
29 #include "pim_igmpv3.h"
33 #include "pim_zebra.h"
36 static void group_retransmit_timer_on(struct igmp_group
*group
);
37 static long igmp_group_timer_remain_msec(struct igmp_group
*group
);
38 static long igmp_source_timer_remain_msec(struct igmp_source
*source
);
39 static void group_query_send(struct igmp_group
*group
);
40 static void source_query_send_by_flag(struct igmp_group
*group
,
41 int num_sources_tosend
);
43 static void on_trace(const char *label
,
44 struct interface
*ifp
, struct in_addr from
,
45 struct in_addr group_addr
,
46 int num_sources
, struct in_addr
*sources
)
48 if (PIM_DEBUG_IGMP_TRACE
) {
49 char from_str
[INET_ADDRSTRLEN
];
50 char group_str
[INET_ADDRSTRLEN
];
52 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
53 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
55 zlog_debug("%s: from %s on %s: group=%s sources=%d",
56 label
, from_str
, ifp
->name
, group_str
, num_sources
);
60 void igmp_group_reset_gmi(struct igmp_group
*group
)
62 long group_membership_interval_msec
;
63 struct pim_interface
*pim_ifp
;
64 struct igmp_sock
*igmp
;
65 struct interface
*ifp
;
67 igmp
= group
->group_igmp_sock
;
68 ifp
= igmp
->interface
;
72 RFC 3376: 8.4. Group Membership Interval
74 The Group Membership Interval is the amount of time that must pass
75 before a multicast router decides there are no more members of a
76 group or a particular source on a network.
78 This value MUST be ((the Robustness Variable) times (the Query
79 Interval)) plus (one Query Response Interval).
81 group_membership_interval_msec = querier_robustness_variable *
82 (1000 * querier_query_interval) +
83 100 * query_response_interval_dsec;
85 group_membership_interval_msec
=
86 PIM_IGMP_GMI_MSEC(igmp
->querier_robustness_variable
,
87 igmp
->querier_query_interval
,
88 pim_ifp
->igmp_query_max_response_time_dsec
);
90 if (PIM_DEBUG_IGMP_TRACE
) {
91 char group_str
[INET_ADDRSTRLEN
];
92 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
93 zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
95 group_membership_interval_msec
/ 1000,
96 group_membership_interval_msec
% 1000,
101 RFC 3376: 6.2.2. Definition of Group Timers
103 The group timer is only used when a group is in EXCLUDE mode and
104 it represents the time for the *filter-mode* of the group to
105 expire and switch to INCLUDE mode.
107 zassert(group
->group_filtermode_isexcl
);
109 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
112 static int igmp_source_timer(struct thread
*t
)
114 struct igmp_source
*source
;
115 struct igmp_group
*group
;
117 source
= THREAD_ARG(t
);
119 group
= source
->source_group
;
121 if (PIM_DEBUG_IGMP_TRACE
) {
122 char group_str
[INET_ADDRSTRLEN
];
123 char source_str
[INET_ADDRSTRLEN
];
124 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
125 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
126 zlog_debug("%s: Source timer expired for group %s source %s on %s",
128 group_str
, source_str
,
129 group
->group_igmp_sock
->interface
->name
);
132 zassert(source
->t_source_timer
);
133 source
->t_source_timer
= NULL
;
136 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
139 Filter-Mode Source Timer Value Action
140 ----------- ------------------ ------
141 INCLUDE TIMER == 0 Suggest to stop forwarding
142 traffic from source and
143 remove source record. If
144 there are no more source
145 records for the group, delete
148 EXCLUDE TIMER == 0 Suggest to not forward
150 (DO NOT remove record)
152 Source timer switched from (T > 0) to (T == 0): disable forwarding.
155 zassert(!source
->t_source_timer
);
157 if (group
->group_filtermode_isexcl
) {
160 igmp_source_forward_stop(source
);
165 /* igmp_source_delete() will stop forwarding source */
166 igmp_source_delete(source
);
169 If there are no more source records for the group, delete group
172 if (!listcount(group
->group_source_list
)) {
173 igmp_group_delete_empty_include(group
);
180 static void source_timer_off(struct igmp_group
*group
,
181 struct igmp_source
*source
)
183 if (!source
->t_source_timer
)
186 if (PIM_DEBUG_IGMP_TRACE
) {
187 char group_str
[INET_ADDRSTRLEN
];
188 char source_str
[INET_ADDRSTRLEN
];
189 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
190 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
191 zlog_debug("Cancelling TIMER event for group %s source %s on %s",
192 group_str
, source_str
,
193 group
->group_igmp_sock
->interface
->name
);
196 THREAD_OFF(source
->t_source_timer
);
197 zassert(!source
->t_source_timer
);
200 static void igmp_source_timer_on(struct igmp_group
*group
,
201 struct igmp_source
*source
,
204 source_timer_off(group
, source
);
206 if (PIM_DEBUG_IGMP_EVENTS
) {
207 char group_str
[INET_ADDRSTRLEN
];
208 char source_str
[INET_ADDRSTRLEN
];
209 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
210 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
211 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
212 interval_msec
/ 1000,
213 interval_msec
% 1000,
214 group_str
, source_str
,
215 group
->group_igmp_sock
->interface
->name
);
218 thread_add_timer_msec(master
, igmp_source_timer
, source
, interval_msec
,
219 &source
->t_source_timer
);
220 zassert(source
->t_source_timer
);
223 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
225 Source timer switched from (T == 0) to (T > 0): enable forwarding.
227 igmp_source_forward_start(source
);
230 void igmp_source_reset_gmi(struct igmp_sock
*igmp
,
231 struct igmp_group
*group
,
232 struct igmp_source
*source
)
234 long group_membership_interval_msec
;
235 struct pim_interface
*pim_ifp
;
236 struct interface
*ifp
;
238 ifp
= igmp
->interface
;
241 group_membership_interval_msec
=
242 PIM_IGMP_GMI_MSEC(igmp
->querier_robustness_variable
,
243 igmp
->querier_query_interval
,
244 pim_ifp
->igmp_query_max_response_time_dsec
);
246 if (PIM_DEBUG_IGMP_TRACE
) {
247 char group_str
[INET_ADDRSTRLEN
];
248 char source_str
[INET_ADDRSTRLEN
];
250 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
251 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
253 zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
255 group_membership_interval_msec
/ 1000,
256 group_membership_interval_msec
% 1000,
261 igmp_source_timer_on(group
, source
,
262 group_membership_interval_msec
);
265 static void source_mark_delete_flag(struct igmp_group
*group
)
267 struct listnode
*src_node
;
268 struct igmp_source
*src
;
270 for (ALL_LIST_ELEMENTS_RO (group
->group_source_list
, src_node
, src
)) {
271 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
275 static void source_mark_send_flag (struct igmp_group
*group
)
277 struct listnode
*src_node
;
278 struct igmp_source
*src
;
280 for (ALL_LIST_ELEMENTS_RO (group
->group_source_list
, src_node
, src
)) {
281 IGMP_SOURCE_DO_SEND(src
->source_flags
);
285 static int source_mark_send_flag_by_timer (struct igmp_group
*group
)
287 struct listnode
*src_node
;
288 struct igmp_source
*src
;
289 int num_marked_sources
= 0;
291 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
292 /* Is source timer running? */
293 if (src
->t_source_timer
) {
294 IGMP_SOURCE_DO_SEND(src
->source_flags
);
295 ++num_marked_sources
;
298 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
302 return num_marked_sources
;
305 static void source_clear_send_flag(struct list
*source_list
)
307 struct listnode
*src_node
;
308 struct igmp_source
*src
;
310 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
311 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
316 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
318 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group
*group
)
320 zassert(group
->group_filtermode_isexcl
);
322 if (listcount(group
->group_source_list
) < 1) {
323 igmp_anysource_forward_start(group
);
327 void igmp_source_free(struct igmp_source
*source
)
329 /* make sure there is no source timer running */
330 zassert(!source
->t_source_timer
);
332 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
335 static void source_channel_oil_detach(struct igmp_source
*source
)
337 if (source
->source_channel_oil
) {
338 pim_channel_oil_del(source
->source_channel_oil
);
339 source
->source_channel_oil
= NULL
;
344 igmp_source_delete: stop fowarding, and delete the source
345 igmp_source_forward_stop: stop fowarding, but keep the source
347 void igmp_source_delete(struct igmp_source
*source
)
349 struct igmp_group
*group
;
352 group
= source
->source_group
;
354 if (PIM_DEBUG_IGMP_TRACE
) {
355 char group_str
[INET_ADDRSTRLEN
];
356 char source_str
[INET_ADDRSTRLEN
];
357 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
358 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
359 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s c_oil ref_count %d",
360 source_str
, group_str
,
361 group
->group_igmp_sock
->fd
,
362 group
->group_igmp_sock
->interface
->name
,
363 source
->source_channel_oil
? source
->source_channel_oil
->oil_ref_count
: 0);
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
, sizeof(group_str
));
374 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
375 zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
377 source_str
, group_str
,
378 group
->group_igmp_sock
->fd
,
379 group
->group_igmp_sock
->interface
->name
);
383 source_channel_oil_detach(source
);
386 notice that listnode_delete() can't be moved
387 into igmp_source_free() because the later is
388 called by list_delete_all_node()
390 listnode_delete(group
->group_source_list
, source
);
392 src
.s_addr
= source
->source_addr
.s_addr
;
393 igmp_source_free(source
);
395 /* Group source list is empty and current source is * then
396 *,G group going away so do not trigger start */
397 if (group
->group_filtermode_isexcl
&&
398 (listcount (group
->group_source_list
) != 0) &&
399 src
.s_addr
!= INADDR_ANY
)
401 group_exclude_fwd_anysrc_ifempty (group
);
405 static void source_delete_by_flag(struct list
*source_list
)
407 struct listnode
*src_node
;
408 struct listnode
*src_nextnode
;
409 struct igmp_source
*src
;
411 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
412 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
413 igmp_source_delete(src
);
416 void igmp_source_delete_expired(struct list
*source_list
)
418 struct listnode
*src_node
;
419 struct listnode
*src_nextnode
;
420 struct igmp_source
*src
;
422 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
423 if (!src
->t_source_timer
)
424 igmp_source_delete(src
);
427 struct igmp_source
*igmp_find_source_by_addr(struct igmp_group
*group
,
428 struct in_addr src_addr
)
430 struct listnode
*src_node
;
431 struct igmp_source
*src
;
433 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
434 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
441 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
, sizeof(group_str
));
450 pim_inet4_dump("<source?>", src_addr
, source_str
, sizeof(source_str
));
451 zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
452 source_str
, group_str
,
453 group
->group_igmp_sock
->fd
,
454 group
->group_igmp_sock
->interface
->name
);
457 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
459 zlog_warn("%s %s: XCALLOC() failure",
460 __FILE__
, __PRETTY_FUNCTION__
);
461 return 0; /* error, not found, could not create */
464 src
->t_source_timer
= NULL
;
465 src
->source_group
= group
; /* back pointer */
466 src
->source_addr
= src_addr
;
467 src
->source_creation
= pim_time_monotonic_sec();
468 src
->source_flags
= 0;
469 src
->source_query_retransmit_count
= 0;
470 src
->source_channel_oil
= NULL
;
472 listnode_add(group
->group_source_list
, src
);
474 zassert(!src
->t_source_timer
); /* source timer == 0 */
476 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
477 igmp_anysource_forward_stop(group
);
482 static struct igmp_source
*add_source_by_addr(struct igmp_sock
*igmp
,
483 struct igmp_group
*group
,
484 struct in_addr src_addr
)
486 struct igmp_source
*src
;
488 src
= igmp_find_source_by_addr(group
, src_addr
);
493 src
= source_new(group
, src_addr
);
501 static void allow(struct igmp_sock
*igmp
, struct in_addr from
,
502 struct in_addr group_addr
,
503 int num_sources
, struct in_addr
*sources
)
505 struct igmp_source
*source
;
506 struct igmp_group
*group
;
509 /* non-existant group is created as INCLUDE {empty} */
510 group
= igmp_add_group_by_addr(igmp
, group_addr
);
515 /* scan received sources */
516 for (i
= 0; i
< num_sources
; ++i
) {
517 struct in_addr
*src_addr
;
519 src_addr
= sources
+ i
;
521 source
= add_source_by_addr(igmp
, group
, *src_addr
);
527 RFC 3376: 6.4.1. Reception of Current-State Records
529 When receiving IS_IN reports for groups in EXCLUDE mode is
530 sources should be moved from set with (timers = 0) to set with
533 igmp_source_reset_gmi() below, resetting the source timers to
534 GMI, accomplishes this.
536 igmp_source_reset_gmi(igmp
, group
, source
);
538 } /* scan received sources */
540 if ((num_sources
== 0) &&
541 (group
->group_filtermode_isexcl
) &&
542 (listcount (group
->group_source_list
) == 1))
544 struct in_addr star
= { .s_addr
= INADDR_ANY
};
546 source
= igmp_find_source_by_addr (group
, star
);
548 igmp_source_reset_gmi (igmp
, group
, source
);
552 void igmpv3_report_isin(struct igmp_sock
*igmp
, struct in_addr from
,
553 struct in_addr group_addr
,
554 int num_sources
, struct in_addr
*sources
)
556 on_trace(__PRETTY_FUNCTION__
,
557 igmp
->interface
, from
, group_addr
, num_sources
, sources
);
559 allow(igmp
, from
, group_addr
, num_sources
, sources
);
562 static void isex_excl(struct igmp_group
*group
,
563 int num_sources
, struct in_addr
*sources
)
565 struct igmp_source
*source
;
569 zassert(group
->group_filtermode_isexcl
);
571 /* E.1: set deletion flag for known sources (X,Y) */
572 source_mark_delete_flag (group
);
574 /* scan received sources (A) */
575 for (i
= 0; i
< num_sources
; ++i
) {
576 struct in_addr
*src_addr
;
578 src_addr
= sources
+ i
;
580 /* E.2: lookup reported source from (A) in (X,Y) */
581 source
= igmp_find_source_by_addr(group
, *src_addr
);
583 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
584 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
587 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
588 source
= source_new(group
, *src_addr
);
590 /* ugh, internal malloc failure, skip source */
593 zassert(!source
->t_source_timer
); /* timer == 0 */
594 igmp_source_reset_gmi(group
->group_igmp_sock
, group
, source
);
595 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
598 } /* scan received sources */
601 * If we are in isexcl mode and num_sources == 0
602 * than that means we have a *,g entry that
603 * needs to be handled
605 if (group
->group_filtermode_isexcl
&& num_sources
== 0)
607 struct in_addr star
= { .s_addr
= INADDR_ANY
};
608 source
= igmp_find_source_by_addr (group
, star
);
611 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
612 igmp_source_reset_gmi (group
->group_igmp_sock
, group
, source
);
616 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
617 source_delete_by_flag(group
->group_source_list
);
620 static void isex_incl(struct igmp_group
*group
,
621 int num_sources
, struct in_addr
*sources
)
626 zassert(!group
->group_filtermode_isexcl
);
628 /* I.1: set deletion flag for known sources (A) */
629 source_mark_delete_flag (group
);
631 /* scan received sources (B) */
632 for (i
= 0; i
< num_sources
; ++i
) {
633 struct igmp_source
*source
;
634 struct in_addr
*src_addr
;
636 src_addr
= sources
+ i
;
638 /* I.2: lookup reported source (B) */
639 source
= igmp_find_source_by_addr(group
, *src_addr
);
641 /* I.3: if found, clear deletion flag (A*B) */
642 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
645 /* I.4: if not found, create source with timer=0 (B-A) */
646 source
= source_new(group
, *src_addr
);
648 /* ugh, internal malloc failure, skip source */
651 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
654 } /* scan received sources */
656 /* I.5: delete all sources marked with deletion flag (A-B) */
657 source_delete_by_flag(group
->group_source_list
);
659 group
->group_filtermode_isexcl
= 1; /* boolean=true */
661 zassert(group
->group_filtermode_isexcl
);
663 group_exclude_fwd_anysrc_ifempty(group
);
666 void igmpv3_report_isex(struct igmp_sock
*igmp
, struct in_addr from
,
667 struct in_addr group_addr
,
668 int num_sources
, struct in_addr
*sources
,
669 int from_igmp_v2_report
)
671 struct interface
*ifp
= igmp
->interface
;
672 struct igmp_group
*group
;
674 on_trace(__PRETTY_FUNCTION__
,
675 ifp
, from
, group_addr
, num_sources
, sources
);
677 /* non-existant group is created as INCLUDE {empty} */
678 group
= igmp_add_group_by_addr(igmp
, group_addr
);
683 /* So we can display how we learned the group in our show command output */
684 if (from_igmp_v2_report
)
685 group
->igmp_version
= 2;
687 if (group
->group_filtermode_isexcl
) {
689 isex_excl(group
, num_sources
, sources
);
693 isex_incl(group
, num_sources
, sources
);
694 zassert(group
->group_filtermode_isexcl
);
697 zassert(group
->group_filtermode_isexcl
);
699 igmp_group_reset_gmi(group
);
702 static void toin_incl(struct igmp_group
*group
,
703 int num_sources
, struct in_addr
*sources
)
705 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
706 int num_sources_tosend
= listcount(group
->group_source_list
);
709 /* Set SEND flag for all known sources (A) */
710 source_mark_send_flag (group
);
712 /* Scan received sources (B) */
713 for (i
= 0; i
< num_sources
; ++i
) {
714 struct igmp_source
*source
;
715 struct in_addr
*src_addr
;
717 src_addr
= sources
+ i
;
719 /* Lookup reported source (B) */
720 source
= igmp_find_source_by_addr(group
, *src_addr
);
722 /* If found, clear SEND flag (A*B) */
723 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
724 --num_sources_tosend
;
727 /* If not found, create new source */
728 source
= source_new(group
, *src_addr
);
730 /* ugh, internal malloc failure, skip source */
736 igmp_source_reset_gmi(igmp
, group
, source
);
739 /* Send sources marked with SEND flag: Q(G,A-B) */
740 if (num_sources_tosend
> 0) {
741 source_query_send_by_flag(group
, num_sources_tosend
);
745 static void toin_excl(struct igmp_group
*group
,
746 int num_sources
, struct in_addr
*sources
)
748 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
749 int num_sources_tosend
;
752 /* Set SEND flag for X (sources with timer > 0) */
753 num_sources_tosend
= source_mark_send_flag_by_timer (group
);
755 /* Scan received sources (A) */
756 for (i
= 0; i
< num_sources
; ++i
) {
757 struct igmp_source
*source
;
758 struct in_addr
*src_addr
;
760 src_addr
= sources
+ i
;
762 /* Lookup reported source (A) */
763 source
= igmp_find_source_by_addr(group
, *src_addr
);
765 if (source
->t_source_timer
) {
766 /* If found and timer running, clear SEND flag (X*A) */
767 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
768 --num_sources_tosend
;
772 /* If not found, create new source */
773 source
= source_new(group
, *src_addr
);
775 /* ugh, internal malloc failure, skip source */
781 igmp_source_reset_gmi(igmp
, group
, source
);
784 /* Send sources marked with SEND flag: Q(G,X-A) */
785 if (num_sources_tosend
> 0) {
786 source_query_send_by_flag(group
, num_sources_tosend
);
790 group_query_send(group
);
793 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
794 struct in_addr group_addr
,
795 int num_sources
, struct in_addr
*sources
)
797 struct interface
*ifp
= igmp
->interface
;
798 struct igmp_group
*group
;
800 on_trace(__PRETTY_FUNCTION__
,
801 ifp
, from
, group_addr
, num_sources
, sources
);
804 * If the requested filter mode is INCLUDE *and* the requested source
805 * list is empty, then the entry corresponding to the requested
806 * interface and multicast address is deleted if present. If no such
807 * entry is present, the request is ignored.
811 /* non-existant group is created as INCLUDE {empty} */
812 group
= igmp_add_group_by_addr(igmp
, group_addr
);
819 group
= find_group_by_addr (igmp
, group_addr
);
824 if (group
->group_filtermode_isexcl
) {
826 toin_excl(group
, num_sources
, sources
);
830 toin_incl(group
, num_sources
, sources
);
834 static void toex_incl(struct igmp_group
*group
,
835 int num_sources
, struct in_addr
*sources
)
837 int num_sources_tosend
= 0;
840 zassert(!group
->group_filtermode_isexcl
);
842 /* Set DELETE flag for all known sources (A) */
843 source_mark_delete_flag (group
);
845 /* Clear off SEND flag from all known sources (A) */
846 source_clear_send_flag(group
->group_source_list
);
848 /* Scan received sources (B) */
849 for (i
= 0; i
< num_sources
; ++i
) {
850 struct igmp_source
*source
;
851 struct in_addr
*src_addr
;
853 src_addr
= sources
+ i
;
855 /* Lookup reported source (B) */
856 source
= igmp_find_source_by_addr(group
, *src_addr
);
858 /* If found, clear deletion flag: (A*B) */
859 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
860 /* and set SEND flag (A*B) */
861 IGMP_SOURCE_DO_SEND(source
->source_flags
);
862 ++num_sources_tosend
;
865 /* If source not found, create source with timer=0: (B-A)=0 */
866 source
= source_new(group
, *src_addr
);
868 /* ugh, internal malloc failure, skip source */
871 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
874 } /* Scan received sources (B) */
876 group
->group_filtermode_isexcl
= 1; /* boolean=true */
878 /* Delete all sources marked with DELETE flag (A-B) */
879 source_delete_by_flag(group
->group_source_list
);
881 /* Send sources marked with SEND flag: Q(G,A*B) */
882 if (num_sources_tosend
> 0) {
883 source_query_send_by_flag(group
, num_sources_tosend
);
886 zassert(group
->group_filtermode_isexcl
);
888 group_exclude_fwd_anysrc_ifempty(group
);
891 static void toex_excl(struct igmp_group
*group
,
892 int num_sources
, struct in_addr
*sources
)
894 int num_sources_tosend
= 0;
897 /* set DELETE flag for all known sources (X,Y) */
898 source_mark_delete_flag (group
);
900 /* clear off SEND flag from all known sources (X,Y) */
901 source_clear_send_flag(group
->group_source_list
);
903 if (num_sources
== 0)
905 struct igmp_source
*source
;
906 struct in_addr any
= { .s_addr
= INADDR_ANY
};
908 source
= igmp_find_source_by_addr (group
, any
);
910 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
913 /* scan received sources (A) */
914 for (i
= 0; i
< num_sources
; ++i
) {
915 struct igmp_source
*source
;
916 struct in_addr
*src_addr
;
918 src_addr
= sources
+ i
;
920 /* lookup reported source (A) in known sources (X,Y) */
921 source
= igmp_find_source_by_addr(group
, *src_addr
);
923 /* if found, clear off DELETE flag from reported source (A) */
924 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
927 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
928 long group_timer_msec
;
929 source
= source_new(group
, *src_addr
);
931 /* ugh, internal malloc failure, skip source */
935 zassert(!source
->t_source_timer
); /* timer == 0 */
936 group_timer_msec
= igmp_group_timer_remain_msec(group
);
937 igmp_source_timer_on(group
, source
, group_timer_msec
);
938 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
940 /* make sure source is created with DELETE flag unset */
941 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
944 /* make sure reported source has DELETE flag unset */
945 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
947 if (source
->t_source_timer
) {
948 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
949 IGMP_SOURCE_DO_SEND(source
->source_flags
);
950 ++num_sources_tosend
;
953 } /* scan received sources (A) */
956 delete all sources marked with DELETE flag:
960 source_delete_by_flag(group
->group_source_list
);
962 /* send sources marked with SEND flag: Q(G,A-Y) */
963 if (num_sources_tosend
> 0) {
964 source_query_send_by_flag(group
, num_sources_tosend
);
968 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
969 struct in_addr group_addr
,
970 int num_sources
, struct in_addr
*sources
)
972 struct interface
*ifp
= igmp
->interface
;
973 struct igmp_group
*group
;
975 on_trace(__PRETTY_FUNCTION__
,
976 ifp
, from
, group_addr
, num_sources
, sources
);
978 /* non-existant group is created as INCLUDE {empty} */
979 group
= igmp_add_group_by_addr(igmp
, group_addr
);
984 if (group
->group_filtermode_isexcl
) {
986 toex_excl(group
, num_sources
, sources
);
990 toex_incl(group
, num_sources
, sources
);
991 zassert(group
->group_filtermode_isexcl
);
993 zassert(group
->group_filtermode_isexcl
);
995 /* Group Timer=GMI */
996 igmp_group_reset_gmi(group
);
999 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
1000 struct in_addr group_addr
,
1001 int num_sources
, struct in_addr
*sources
)
1003 on_trace(__PRETTY_FUNCTION__
,
1004 igmp
->interface
, from
, group_addr
, num_sources
, sources
);
1006 allow(igmp
, from
, group_addr
, num_sources
, sources
);
1010 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1012 When transmitting a group specific query, if the group timer is
1013 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1014 in the query message.
1016 static void group_retransmit_group(struct igmp_group
*group
)
1018 struct igmp_sock
*igmp
;
1019 struct pim_interface
*pim_ifp
;
1020 long lmqc
; /* Last Member Query Count */
1021 long lmqi_msec
; /* Last Member Query Interval */
1022 long lmqt_msec
; /* Last Member Query Time */
1026 igmp
= group
->group_igmp_sock
;
1027 pim_ifp
= igmp
->interface
->info
;
1029 if (pim_ifp
->igmp_version
== 3) {
1030 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1032 query_buf_size
= IGMP_V12_MSG_SIZE
;
1035 char query_buf
[query_buf_size
];
1037 lmqc
= igmp
->querier_robustness_variable
;
1038 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1039 lmqt_msec
= lmqc
* lmqi_msec
;
1042 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1044 When transmitting a group specific query, if the group timer is
1045 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1046 in the query message.
1048 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1050 if (PIM_DEBUG_IGMP_TRACE
) {
1051 char group_str
[INET_ADDRSTRLEN
];
1052 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1053 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1054 group_str
, igmp
->interface
->name
, s_flag
,
1055 group
->group_specific_query_retransmit_count
);
1059 RFC3376: 4.1.12. IP Destination Addresses for Queries
1061 Group-Specific and Group-and-Source-Specific Queries are sent with
1062 an IP destination address equal to the multicast address of
1066 igmp_send_query(pim_ifp
->igmp_version
,
1069 igmp
->interface
->name
,
1072 0 /* num_sources_tosend */,
1073 group
->group_addr
/* dst_addr */,
1074 group
->group_addr
/* group_addr */,
1075 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1077 igmp
->querier_robustness_variable
,
1078 igmp
->querier_query_interval
);
1082 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1084 When building a group and source specific query for a group G, two
1085 separate query messages are sent for the group. The first one has
1086 the "Suppress Router-Side Processing" bit set and contains all the
1087 sources with retransmission state and timers greater than LMQT. The
1088 second has the "Suppress Router-Side Processing" bit clear and
1089 contains all the sources with retransmission state and timers lower
1090 or equal to LMQT. If either of the two calculated messages does not
1091 contain any sources, then its transmission is suppressed.
1093 static int group_retransmit_sources(struct igmp_group
*group
,
1094 int send_with_sflag_set
)
1096 struct igmp_sock
*igmp
;
1097 struct pim_interface
*pim_ifp
;
1098 long lmqc
; /* Last Member Query Count */
1099 long lmqi_msec
; /* Last Member Query Interval */
1100 long lmqt_msec
; /* Last Member Query Time */
1101 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1102 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1103 int query_buf1_max_sources
;
1104 int query_buf2_max_sources
;
1105 struct in_addr
*source_addr1
;
1106 struct in_addr
*source_addr2
;
1107 int num_sources_tosend1
;
1108 int num_sources_tosend2
;
1109 struct listnode
*src_node
;
1110 struct igmp_source
*src
;
1111 int num_retransmit_sources_left
= 0;
1113 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1114 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1116 igmp
= group
->group_igmp_sock
;
1117 pim_ifp
= igmp
->interface
->info
;
1119 lmqc
= igmp
->querier_robustness_variable
;
1120 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1121 lmqt_msec
= lmqc
* lmqi_msec
;
1123 /* Scan all group sources */
1124 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1126 /* Source has retransmission state? */
1127 if (src
->source_query_retransmit_count
< 1)
1130 if (--src
->source_query_retransmit_count
> 0) {
1131 ++num_retransmit_sources_left
;
1134 /* Copy source address into appropriate query buffer */
1135 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1136 *source_addr1
= src
->source_addr
;
1140 *source_addr2
= src
->source_addr
;
1146 num_sources_tosend1
= source_addr1
- (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1147 num_sources_tosend2
= source_addr2
- (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1149 if (PIM_DEBUG_IGMP_TRACE
) {
1150 char group_str
[INET_ADDRSTRLEN
];
1151 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1152 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",
1153 group_str
, igmp
->interface
->name
,
1154 num_sources_tosend1
,
1155 num_sources_tosend2
,
1156 send_with_sflag_set
,
1157 num_retransmit_sources_left
);
1160 if (num_sources_tosend1
> 0) {
1162 Send group-and-source-specific query with s_flag set and all
1163 sources with timers greater than LMQT.
1166 if (send_with_sflag_set
) {
1168 query_buf1_max_sources
= (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1169 if (num_sources_tosend1
> query_buf1_max_sources
) {
1170 char group_str
[INET_ADDRSTRLEN
];
1171 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1172 zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1173 __PRETTY_FUNCTION__
, group_str
, igmp
->interface
->name
,
1174 num_sources_tosend1
, sizeof(query_buf1
), query_buf1_max_sources
);
1178 RFC3376: 4.1.12. IP Destination Addresses for Queries
1180 Group-Specific and Group-and-Source-Specific Queries are sent with
1181 an IP destination address equal to the multicast address of
1185 igmp_send_query(pim_ifp
->igmp_version
,
1188 igmp
->interface
->name
,
1191 num_sources_tosend1
,
1194 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1196 igmp
->querier_robustness_variable
,
1197 igmp
->querier_query_interval
);
1200 } /* send_with_sflag_set */
1204 if (num_sources_tosend2
> 0) {
1206 Send group-and-source-specific query with s_flag clear and all
1207 sources with timers lower or equal to LMQT.
1210 query_buf2_max_sources
= (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1211 if (num_sources_tosend2
> query_buf2_max_sources
) {
1212 char group_str
[INET_ADDRSTRLEN
];
1213 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1214 zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1215 __PRETTY_FUNCTION__
, group_str
, igmp
->interface
->name
,
1216 num_sources_tosend2
, sizeof(query_buf2
), query_buf2_max_sources
);
1220 RFC3376: 4.1.12. IP Destination Addresses for Queries
1222 Group-Specific and Group-and-Source-Specific Queries are sent with
1223 an IP destination address equal to the multicast address of
1227 igmp_send_query(pim_ifp
->igmp_version
,
1230 igmp
->interface
->name
,
1233 num_sources_tosend2
,
1236 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1238 igmp
->querier_robustness_variable
,
1239 igmp
->querier_query_interval
);
1243 return num_retransmit_sources_left
;
1246 static int igmp_group_retransmit(struct thread
*t
)
1248 struct igmp_group
*group
;
1249 int num_retransmit_sources_left
;
1250 int send_with_sflag_set
; /* boolean */
1252 group
= THREAD_ARG(t
);
1254 if (PIM_DEBUG_IGMP_TRACE
) {
1255 char group_str
[INET_ADDRSTRLEN
];
1256 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1257 zlog_debug("group_retransmit_timer: group %s on %s",
1258 group_str
, group
->group_igmp_sock
->interface
->name
);
1261 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1262 if (group
->group_specific_query_retransmit_count
> 0) {
1264 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1265 group_retransmit_group(group
);
1266 --group
->group_specific_query_retransmit_count
;
1270 If a group specific query is scheduled to be transmitted at the
1271 same time as a group and source specific query for the same group,
1272 then transmission of the group and source specific message with the
1273 "Suppress Router-Side Processing" bit set may be suppressed.
1275 send_with_sflag_set
= 0; /* boolean=false */
1278 send_with_sflag_set
= 1; /* boolean=true */
1281 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1282 num_retransmit_sources_left
= group_retransmit_sources(group
,
1283 send_with_sflag_set
);
1285 group
->t_group_query_retransmit_timer
= NULL
;
1288 Keep group retransmit timer running if there is any retransmit
1291 if ((num_retransmit_sources_left
> 0) ||
1292 (group
->group_specific_query_retransmit_count
> 0)) {
1293 group_retransmit_timer_on(group
);
1300 group_retransmit_timer_on:
1301 if group retransmit timer isn't running, starts it;
1302 otherwise, do nothing
1304 static void group_retransmit_timer_on(struct igmp_group
*group
)
1306 struct igmp_sock
*igmp
;
1307 struct pim_interface
*pim_ifp
;
1308 long lmqi_msec
; /* Last Member Query Interval */
1310 /* if group retransmit timer is running, do nothing */
1311 if (group
->t_group_query_retransmit_timer
) {
1315 igmp
= group
->group_igmp_sock
;
1316 pim_ifp
= igmp
->interface
->info
;
1318 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1320 if (PIM_DEBUG_IGMP_TRACE
) {
1321 char group_str
[INET_ADDRSTRLEN
];
1322 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1323 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1327 igmp
->interface
->name
);
1330 thread_add_timer_msec(master
, igmp_group_retransmit
, group
, lmqi_msec
,
1331 &group
->t_group_query_retransmit_timer
);
1334 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1336 return pim_time_timer_remain_msec(group
->t_group_timer
);
1339 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1341 return pim_time_timer_remain_msec(source
->t_source_timer
);
1345 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1347 static void group_query_send(struct igmp_group
*group
)
1349 long lmqc
; /* Last Member Query Count */
1351 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1353 /* lower group timer to lmqt */
1354 igmp_group_timer_lower_to_lmqt(group
);
1356 /* reset retransmission counter */
1357 group
->group_specific_query_retransmit_count
= lmqc
;
1359 /* immediately send group specific query (decrease retransmit counter by 1)*/
1360 group_retransmit_group(group
);
1362 /* make sure group retransmit timer is running */
1363 group_retransmit_timer_on(group
);
1367 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1369 static void source_query_send_by_flag(struct igmp_group
*group
,
1370 int num_sources_tosend
)
1372 struct igmp_sock
*igmp
;
1373 struct pim_interface
*pim_ifp
;
1374 struct listnode
*src_node
;
1375 struct igmp_source
*src
;
1376 long lmqc
; /* Last Member Query Count */
1377 long lmqi_msec
; /* Last Member Query Interval */
1378 long lmqt_msec
; /* Last Member Query Time */
1380 zassert(num_sources_tosend
> 0);
1382 igmp
= group
->group_igmp_sock
;
1383 pim_ifp
= igmp
->interface
->info
;
1385 lmqc
= igmp
->querier_robustness_variable
;
1386 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1387 lmqt_msec
= lmqc
* lmqi_msec
;
1390 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1392 (...) for each of the sources in X of group G, with source timer larger
1394 o Set number of retransmissions for each source to [Last Member
1396 o Lower source timer to LMQT.
1398 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1399 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1400 /* source "src" in X of group G */
1401 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1402 src
->source_query_retransmit_count
= lmqc
;
1403 igmp_source_timer_lower_to_lmqt(src
);
1408 /* send group-and-source specific queries */
1409 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1411 /* make sure group retransmit timer is running */
1412 group_retransmit_timer_on(group
);
1415 static void block_excl(struct igmp_group
*group
,
1416 int num_sources
, struct in_addr
*sources
)
1418 int num_sources_tosend
= 0;
1421 /* 1. clear off SEND flag from all known sources (X,Y) */
1422 source_clear_send_flag(group
->group_source_list
);
1424 /* 2. scan received sources (A) */
1425 for (i
= 0; i
< num_sources
; ++i
) {
1426 struct igmp_source
*source
;
1427 struct in_addr
*src_addr
;
1429 src_addr
= sources
+ i
;
1431 /* lookup reported source (A) in known sources (X,Y) */
1432 source
= igmp_find_source_by_addr(group
, *src_addr
);
1434 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1435 long group_timer_msec
;
1436 source
= source_new(group
, *src_addr
);
1438 /* ugh, internal malloc failure, skip source */
1442 zassert(!source
->t_source_timer
); /* timer == 0 */
1443 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1444 igmp_source_timer_on(group
, source
, group_timer_msec
);
1445 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1448 if (source
->t_source_timer
) {
1449 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1450 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1451 ++num_sources_tosend
;
1455 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1456 if (num_sources_tosend
> 0) {
1457 source_query_send_by_flag(group
, num_sources_tosend
);
1461 static void block_incl(struct igmp_group
*group
,
1462 int num_sources
, struct in_addr
*sources
)
1464 int num_sources_tosend
= 0;
1467 /* 1. clear off SEND flag from all known sources (B) */
1468 source_clear_send_flag(group
->group_source_list
);
1470 /* 2. scan received sources (A) */
1471 for (i
= 0; i
< num_sources
; ++i
) {
1472 struct igmp_source
*source
;
1473 struct in_addr
*src_addr
;
1475 src_addr
= sources
+ i
;
1477 /* lookup reported source (A) in known sources (B) */
1478 source
= igmp_find_source_by_addr(group
, *src_addr
);
1480 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1481 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1482 ++num_sources_tosend
;
1486 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1487 if (num_sources_tosend
> 0) {
1488 source_query_send_by_flag(group
, num_sources_tosend
);
1492 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1493 struct in_addr group_addr
,
1494 int num_sources
, struct in_addr
*sources
)
1496 struct interface
*ifp
= igmp
->interface
;
1497 struct igmp_group
*group
;
1499 on_trace(__PRETTY_FUNCTION__
,
1500 ifp
, from
, group_addr
, num_sources
, sources
);
1502 /* non-existant group is created as INCLUDE {empty} */
1503 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1508 if (group
->group_filtermode_isexcl
) {
1510 block_excl(group
, num_sources
, sources
);
1514 block_incl(group
, num_sources
, sources
);
1518 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1520 struct igmp_sock
*igmp
;
1521 struct interface
*ifp
;
1522 struct pim_interface
*pim_ifp
;
1524 int lmqi_dsec
; /* Last Member Query Interval */
1525 int lmqc
; /* Last Member Query Count */
1526 int lmqt_msec
; /* Last Member Query Time */
1529 RFC 3376: 6.2.2. Definition of Group Timers
1531 The group timer is only used when a group is in EXCLUDE mode and
1532 it represents the time for the *filter-mode* of the group to
1533 expire and switch to INCLUDE mode.
1535 if (!group
->group_filtermode_isexcl
) {
1539 igmp
= group
->group_igmp_sock
;
1540 ifp
= igmp
->interface
;
1541 pim_ifp
= ifp
->info
;
1544 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1545 lmqc
= igmp
->querier_robustness_variable
;
1546 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1548 if (PIM_DEBUG_IGMP_TRACE
) {
1549 char group_str
[INET_ADDRSTRLEN
];
1550 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1551 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1552 __PRETTY_FUNCTION__
,
1554 lmqc
, lmqi_dsec
, lmqt_msec
);
1557 zassert(group
->group_filtermode_isexcl
);
1559 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1562 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1564 struct igmp_group
*group
;
1565 struct igmp_sock
*igmp
;
1566 struct interface
*ifp
;
1567 struct pim_interface
*pim_ifp
;
1569 int lmqi_dsec
; /* Last Member Query Interval */
1570 int lmqc
; /* Last Member Query Count */
1571 int lmqt_msec
; /* Last Member Query Time */
1573 group
= source
->source_group
;
1574 igmp
= group
->group_igmp_sock
;
1575 ifp
= igmp
->interface
;
1576 pim_ifp
= ifp
->info
;
1579 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1580 lmqc
= igmp
->querier_robustness_variable
;
1581 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1583 if (PIM_DEBUG_IGMP_TRACE
) {
1584 char group_str
[INET_ADDRSTRLEN
];
1585 char source_str
[INET_ADDRSTRLEN
];
1586 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1587 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
1588 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1589 __PRETTY_FUNCTION__
,
1590 group_str
, source_str
, ifname
,
1591 lmqc
, lmqi_dsec
, lmqt_msec
);
1594 igmp_source_timer_on(group
, source
, lmqt_msec
);
1598 igmp_v3_send_query (struct igmp_group
*group
,
1604 struct in_addr dst_addr
,
1605 struct in_addr group_addr
,
1606 int query_max_response_time_dsec
,
1608 uint8_t querier_robustness_variable
,
1609 uint16_t querier_query_interval
)
1612 uint8_t max_resp_code
;
1615 struct sockaddr_in to
;
1619 zassert(num_sources
>= 0);
1621 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1622 if (msg_size
> query_buf_size
) {
1623 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1624 __FILE__
, __PRETTY_FUNCTION__
,
1625 msg_size
, query_buf_size
);
1629 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1630 zassert((s_flag
== 0) || (s_flag
== 1));
1632 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1633 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1636 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1638 If non-zero, the QRV field contains the [Robustness Variable]
1639 value used by the querier, i.e., the sender of the Query. If the
1640 querier's [Robustness Variable] exceeds 7, the maximum value of
1641 the QRV field, the QRV is set to zero.
1643 if (querier_robustness_variable
> 7) {
1644 querier_robustness_variable
= 0;
1647 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1648 query_buf
[1] = max_resp_code
;
1649 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = 0; /* for computing checksum */
1650 memcpy(query_buf
+4, &group_addr
, sizeof(struct in_addr
));
1652 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1653 query_buf
[9] = qqic
;
1654 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) = htons(num_sources
);
1656 checksum
= in_cksum(query_buf
, msg_size
);
1657 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1659 if (PIM_DEBUG_IGMP_PACKETS
) {
1660 char dst_str
[INET_ADDRSTRLEN
];
1661 char group_str
[INET_ADDRSTRLEN
];
1662 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1663 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1664 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",
1665 dst_str
, ifname
, group_str
,
1666 num_sources
, msg_size
, s_flag
, querier_robustness_variable
,
1667 querier_query_interval
, qqic
);
1670 memset(&to
, 0, sizeof(to
));
1671 to
.sin_family
= AF_INET
;
1672 to
.sin_addr
= dst_addr
;
1675 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1676 (struct sockaddr
*)&to
, tolen
);
1677 if (sent
!= (ssize_t
) msg_size
) {
1678 char dst_str
[INET_ADDRSTRLEN
];
1679 char group_str
[INET_ADDRSTRLEN
];
1680 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1681 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1683 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1684 dst_str
, ifname
, group_str
, msg_size
, errno
, safe_strerror(errno
));
1687 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1688 dst_str
, ifname
, group_str
, msg_size
, sent
);
1694 s_flag sanity test: s_flag must be set for general queries
1696 RFC 3376: 6.6.1. Timer Updates
1698 When a router sends or receives a query with a clear Suppress
1699 Router-Side Processing flag, it must update its timers to reflect
1700 the correct timeout values for the group or sources being queried.
1702 General queries don't trigger timer update.
1705 /* general query? */
1706 if (PIM_INADDR_IS_ANY(group_addr
)) {
1707 char dst_str
[INET_ADDRSTRLEN
];
1708 char group_str
[INET_ADDRSTRLEN
];
1709 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1710 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1711 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1712 __PRETTY_FUNCTION__
,
1713 dst_str
, ifname
, group_str
, num_sources
);
1719 igmp_v3_recv_query (struct igmp_sock
*igmp
, const char *from_str
, char *igmp_msg
)
1721 struct interface
*ifp
;
1722 struct pim_interface
*pim_ifp
;
1723 struct in_addr group_addr
;
1724 uint8_t resv_s_qrv
= 0;
1729 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1730 ifp
= igmp
->interface
;
1731 pim_ifp
= ifp
->info
;
1734 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1736 * Routers adopt the QRV value from the most recently received Query
1737 * as their own [Robustness Variable] value, unless that most
1738 * recently received QRV was zero, in which case the receivers use
1739 * the default [Robustness Variable] value specified in section 8.1
1740 * or a statically configured value.
1742 resv_s_qrv
= igmp_msg
[8];
1743 qrv
= 7 & resv_s_qrv
;
1744 igmp
->querier_robustness_variable
= qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
1747 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1749 * Multicast routers that are not the current querier adopt the QQI
1750 * value from the most recently received Query as their own [Query
1751 * Interval] value, unless that most recently received QQI was zero,
1752 * in which case the receiving routers use the default.
1754 if (igmp
->t_other_querier_timer
) {
1755 /* other querier present */
1759 qqi
= igmp_msg_decode8to16(qqic
);
1760 igmp
->querier_query_interval
= qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
1762 if (PIM_DEBUG_IGMP_TRACE
) {
1763 char ifaddr_str
[INET_ADDRSTRLEN
];
1764 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
1765 zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1767 qqi
? "recv-non-default" : "default",
1768 igmp
->querier_query_interval
,
1775 * RFC 3376: 6.6.1. Timer Updates
1777 * When a router sends or receives a query with a clear Suppress
1778 * Router-Side Processing flag, it must update its timers to reflect
1779 * the correct timeout values for the group or sources being queried.
1781 * General queries don't trigger timer update.
1783 s_flag
= (1 << 3) & resv_s_qrv
;
1786 /* s_flag is clear */
1788 if (PIM_INADDR_IS_ANY(group_addr
)) {
1789 /* this is a general query */
1790 /* log that general query should have the s_flag set */
1791 zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1792 from_str
, ifp
->name
);
1794 struct igmp_group
*group
;
1796 /* this is a non-general query: perform timer updates */
1798 group
= find_group_by_addr(igmp
, group_addr
);
1800 int recv_num_sources
= ntohs(*(uint16_t *)(igmp_msg
+ IGMP_V3_NUMSOURCES_OFFSET
));
1803 * RFC 3376: 6.6.1. Timer Updates
1804 * Query Q(G,A): Source Timer for sources in A are lowered to LMQT
1805 * Query Q(G): Group Timer is lowered to LMQT
1807 if (recv_num_sources
< 1) {
1808 /* Query Q(G): Group Timer is lowered to LMQT */
1810 igmp_group_timer_lower_to_lmqt(group
);
1812 /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
1814 /* Scan sources in query and lower their timers to LMQT */
1815 struct in_addr
*sources
= (struct in_addr
*)(igmp_msg
+ IGMP_V3_SOURCES_OFFSET
);
1816 for (i
= 0; i
< recv_num_sources
; ++i
) {
1817 struct in_addr src_addr
;
1818 struct igmp_source
*src
;
1819 memcpy(&src_addr
, sources
+ i
, sizeof(struct in_addr
));
1820 src
= igmp_find_source_by_addr(group
, src_addr
);
1822 igmp_source_timer_lower_to_lmqt(src
);
1827 char group_str
[INET_ADDRSTRLEN
];
1828 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1829 zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update",
1830 from_str
, ifp
->name
, group_str
);
1833 } /* s_flag is clear: timer updates */
1837 igmp_v3_recv_report (struct igmp_sock
*igmp
,
1838 struct in_addr from
, const char *from_str
,
1839 char *igmp_msg
, int igmp_msg_len
)
1841 uint16_t recv_checksum
;
1844 uint8_t *group_record
;
1845 uint8_t *report_pastend
= (uint8_t *) igmp_msg
+ igmp_msg_len
;
1846 struct interface
*ifp
= igmp
->interface
;
1850 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1851 zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1852 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V3_MSG_MIN_SIZE
);
1856 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1858 /* for computing checksum */
1859 *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1861 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1862 if (checksum
!= recv_checksum
) {
1863 zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1864 from_str
, ifp
->name
, recv_checksum
, checksum
);
1868 num_groups
= ntohs(*(uint16_t *) (igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1869 if (num_groups
< 1) {
1870 zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
1871 from_str
, ifp
->name
);
1875 if (PIM_DEBUG_IGMP_PACKETS
) {
1876 zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1877 from_str
, ifp
->name
, igmp_msg_len
, checksum
, num_groups
);
1880 group_record
= (uint8_t *) igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1883 for (i
= 0; i
< num_groups
; ++i
) {
1884 struct in_addr rec_group
;
1889 int rec_num_sources
;
1894 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
) > report_pastend
) {
1895 zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
1896 from_str
, ifp
->name
);
1900 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1901 rec_auxdatalen
= group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1902 rec_num_sources
= ntohs(* (uint16_t *) (group_record
+ IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1904 memcpy(&rec_group
, group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
, sizeof(struct in_addr
));
1906 if (PIM_DEBUG_IGMP_PACKETS
) {
1907 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1908 from_str
, ifp
->name
, i
, rec_type
, rec_auxdatalen
, rec_num_sources
, inet_ntoa(rec_group
));
1913 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1915 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1917 if ((src
+ 4) > report_pastend
) {
1918 zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
1919 from_str
, ifp
->name
);
1923 if (PIM_DEBUG_IGMP_PACKETS
) {
1926 if (!inet_ntop(AF_INET
, src
, src_str
, sizeof(src_str
)))
1927 sprintf(src_str
, "<source?>");
1929 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1930 from_str
, ifp
->name
, i
, inet_ntoa(rec_group
), src_str
);
1932 } /* for (sources) */
1935 lncb
.family
= AF_INET
;
1936 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1937 lncb
.prefixlen
= 24;
1940 g
.u
.prefix4
= rec_group
;
1943 * If we receive a igmp report with the group in 224.0.0.0/24
1944 * then we should ignore it
1946 if (prefix_match(&lncb
, &g
))
1951 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1952 igmpv3_report_isin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1954 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1955 igmpv3_report_isex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
, 0);
1957 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
1958 igmpv3_report_toin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1960 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
1961 igmpv3_report_toex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1963 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
1964 igmpv3_report_allow(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1966 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
1967 igmpv3_report_block(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1970 zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
1971 from_str
, ifp
->name
, rec_type
);
1974 group_record
+= 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
1977 } /* for (group records) */