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_TIMER_MSEC_ON(master
, source
->t_source_timer
,
220 source
, interval_msec
);
221 zassert(source
->t_source_timer
);
224 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
226 Source timer switched from (T == 0) to (T > 0): enable forwarding.
228 igmp_source_forward_start(source
);
231 void igmp_source_reset_gmi(struct igmp_sock
*igmp
,
232 struct igmp_group
*group
,
233 struct igmp_source
*source
)
235 long group_membership_interval_msec
;
236 struct pim_interface
*pim_ifp
;
237 struct interface
*ifp
;
239 ifp
= igmp
->interface
;
242 group_membership_interval_msec
=
243 PIM_IGMP_GMI_MSEC(igmp
->querier_robustness_variable
,
244 igmp
->querier_query_interval
,
245 pim_ifp
->igmp_query_max_response_time_dsec
);
247 if (PIM_DEBUG_IGMP_TRACE
) {
248 char group_str
[INET_ADDRSTRLEN
];
249 char source_str
[INET_ADDRSTRLEN
];
251 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
252 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
254 zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
256 group_membership_interval_msec
/ 1000,
257 group_membership_interval_msec
% 1000,
262 igmp_source_timer_on(group
, source
,
263 group_membership_interval_msec
);
266 static void source_mark_delete_flag(struct igmp_group
*group
)
268 struct listnode
*src_node
;
269 struct igmp_source
*src
;
271 for (ALL_LIST_ELEMENTS_RO (group
->group_source_list
, src_node
, src
)) {
272 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
276 static void source_mark_send_flag (struct igmp_group
*group
)
278 struct listnode
*src_node
;
279 struct igmp_source
*src
;
281 for (ALL_LIST_ELEMENTS_RO (group
->group_source_list
, src_node
, src
)) {
282 IGMP_SOURCE_DO_SEND(src
->source_flags
);
286 static int source_mark_send_flag_by_timer (struct igmp_group
*group
)
288 struct listnode
*src_node
;
289 struct igmp_source
*src
;
290 int num_marked_sources
= 0;
292 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
293 /* Is source timer running? */
294 if (src
->t_source_timer
) {
295 IGMP_SOURCE_DO_SEND(src
->source_flags
);
296 ++num_marked_sources
;
299 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
303 return num_marked_sources
;
306 static void source_clear_send_flag(struct list
*source_list
)
308 struct listnode
*src_node
;
309 struct igmp_source
*src
;
311 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
312 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
317 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
319 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group
*group
)
321 zassert(group
->group_filtermode_isexcl
);
323 if (listcount(group
->group_source_list
) < 1) {
324 igmp_anysource_forward_start(group
);
328 void igmp_source_free(struct igmp_source
*source
)
330 /* make sure there is no source timer running */
331 zassert(!source
->t_source_timer
);
333 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
336 static void source_channel_oil_detach(struct igmp_source
*source
)
338 if (source
->source_channel_oil
) {
339 pim_channel_oil_del(source
->source_channel_oil
);
340 source
->source_channel_oil
= NULL
;
345 igmp_source_delete: stop fowarding, and delete the source
346 igmp_source_forward_stop: stop fowarding, but keep the source
348 void igmp_source_delete(struct igmp_source
*source
)
350 struct igmp_group
*group
;
353 group
= source
->source_group
;
355 if (PIM_DEBUG_IGMP_TRACE
) {
356 char group_str
[INET_ADDRSTRLEN
];
357 char source_str
[INET_ADDRSTRLEN
];
358 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
359 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
360 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
361 source_str
, group_str
,
362 group
->group_igmp_sock
->fd
,
363 group
->group_igmp_sock
->interface
->name
);
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_TIMER_MSEC_ON(master
, group
->t_group_query_retransmit_timer
,
1331 igmp_group_retransmit
,
1335 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1337 return pim_time_timer_remain_msec(group
->t_group_timer
);
1340 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1342 return pim_time_timer_remain_msec(source
->t_source_timer
);
1346 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1348 static void group_query_send(struct igmp_group
*group
)
1350 long lmqc
; /* Last Member Query Count */
1352 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1354 /* lower group timer to lmqt */
1355 igmp_group_timer_lower_to_lmqt(group
);
1357 /* reset retransmission counter */
1358 group
->group_specific_query_retransmit_count
= lmqc
;
1360 /* immediately send group specific query (decrease retransmit counter by 1)*/
1361 group_retransmit_group(group
);
1363 /* make sure group retransmit timer is running */
1364 group_retransmit_timer_on(group
);
1368 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1370 static void source_query_send_by_flag(struct igmp_group
*group
,
1371 int num_sources_tosend
)
1373 struct igmp_sock
*igmp
;
1374 struct pim_interface
*pim_ifp
;
1375 struct listnode
*src_node
;
1376 struct igmp_source
*src
;
1377 long lmqc
; /* Last Member Query Count */
1378 long lmqi_msec
; /* Last Member Query Interval */
1379 long lmqt_msec
; /* Last Member Query Time */
1381 zassert(num_sources_tosend
> 0);
1383 igmp
= group
->group_igmp_sock
;
1384 pim_ifp
= igmp
->interface
->info
;
1386 lmqc
= igmp
->querier_robustness_variable
;
1387 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1388 lmqt_msec
= lmqc
* lmqi_msec
;
1391 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1393 (...) for each of the sources in X of group G, with source timer larger
1395 o Set number of retransmissions for each source to [Last Member
1397 o Lower source timer to LMQT.
1399 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1400 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1401 /* source "src" in X of group G */
1402 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1403 src
->source_query_retransmit_count
= lmqc
;
1404 igmp_source_timer_lower_to_lmqt(src
);
1409 /* send group-and-source specific queries */
1410 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1412 /* make sure group retransmit timer is running */
1413 group_retransmit_timer_on(group
);
1416 static void block_excl(struct igmp_group
*group
,
1417 int num_sources
, struct in_addr
*sources
)
1419 int num_sources_tosend
= 0;
1422 /* 1. clear off SEND flag from all known sources (X,Y) */
1423 source_clear_send_flag(group
->group_source_list
);
1425 /* 2. scan received sources (A) */
1426 for (i
= 0; i
< num_sources
; ++i
) {
1427 struct igmp_source
*source
;
1428 struct in_addr
*src_addr
;
1430 src_addr
= sources
+ i
;
1432 /* lookup reported source (A) in known sources (X,Y) */
1433 source
= igmp_find_source_by_addr(group
, *src_addr
);
1435 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1436 long group_timer_msec
;
1437 source
= source_new(group
, *src_addr
);
1439 /* ugh, internal malloc failure, skip source */
1443 zassert(!source
->t_source_timer
); /* timer == 0 */
1444 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1445 igmp_source_timer_on(group
, source
, group_timer_msec
);
1446 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1449 if (source
->t_source_timer
) {
1450 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1451 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1452 ++num_sources_tosend
;
1456 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1457 if (num_sources_tosend
> 0) {
1458 source_query_send_by_flag(group
, num_sources_tosend
);
1462 static void block_incl(struct igmp_group
*group
,
1463 int num_sources
, struct in_addr
*sources
)
1465 int num_sources_tosend
= 0;
1468 /* 1. clear off SEND flag from all known sources (B) */
1469 source_clear_send_flag(group
->group_source_list
);
1471 /* 2. scan received sources (A) */
1472 for (i
= 0; i
< num_sources
; ++i
) {
1473 struct igmp_source
*source
;
1474 struct in_addr
*src_addr
;
1476 src_addr
= sources
+ i
;
1478 /* lookup reported source (A) in known sources (B) */
1479 source
= igmp_find_source_by_addr(group
, *src_addr
);
1481 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1482 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1483 ++num_sources_tosend
;
1487 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1488 if (num_sources_tosend
> 0) {
1489 source_query_send_by_flag(group
, num_sources_tosend
);
1493 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1494 struct in_addr group_addr
,
1495 int num_sources
, struct in_addr
*sources
)
1497 struct interface
*ifp
= igmp
->interface
;
1498 struct igmp_group
*group
;
1500 on_trace(__PRETTY_FUNCTION__
,
1501 ifp
, from
, group_addr
, num_sources
, sources
);
1503 /* non-existant group is created as INCLUDE {empty} */
1504 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1509 if (group
->group_filtermode_isexcl
) {
1511 block_excl(group
, num_sources
, sources
);
1515 block_incl(group
, num_sources
, sources
);
1519 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1521 struct igmp_sock
*igmp
;
1522 struct interface
*ifp
;
1523 struct pim_interface
*pim_ifp
;
1525 int lmqi_dsec
; /* Last Member Query Interval */
1526 int lmqc
; /* Last Member Query Count */
1527 int lmqt_msec
; /* Last Member Query Time */
1530 RFC 3376: 6.2.2. Definition of Group Timers
1532 The group timer is only used when a group is in EXCLUDE mode and
1533 it represents the time for the *filter-mode* of the group to
1534 expire and switch to INCLUDE mode.
1536 if (!group
->group_filtermode_isexcl
) {
1540 igmp
= group
->group_igmp_sock
;
1541 ifp
= igmp
->interface
;
1542 pim_ifp
= ifp
->info
;
1545 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1546 lmqc
= igmp
->querier_robustness_variable
;
1547 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1549 if (PIM_DEBUG_IGMP_TRACE
) {
1550 char group_str
[INET_ADDRSTRLEN
];
1551 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1552 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1553 __PRETTY_FUNCTION__
,
1555 lmqc
, lmqi_dsec
, lmqt_msec
);
1558 zassert(group
->group_filtermode_isexcl
);
1560 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1563 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1565 struct igmp_group
*group
;
1566 struct igmp_sock
*igmp
;
1567 struct interface
*ifp
;
1568 struct pim_interface
*pim_ifp
;
1570 int lmqi_dsec
; /* Last Member Query Interval */
1571 int lmqc
; /* Last Member Query Count */
1572 int lmqt_msec
; /* Last Member Query Time */
1574 group
= source
->source_group
;
1575 igmp
= group
->group_igmp_sock
;
1576 ifp
= igmp
->interface
;
1577 pim_ifp
= ifp
->info
;
1580 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1581 lmqc
= igmp
->querier_robustness_variable
;
1582 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1584 if (PIM_DEBUG_IGMP_TRACE
) {
1585 char group_str
[INET_ADDRSTRLEN
];
1586 char source_str
[INET_ADDRSTRLEN
];
1587 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1588 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
1589 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1590 __PRETTY_FUNCTION__
,
1591 group_str
, source_str
, ifname
,
1592 lmqc
, lmqi_dsec
, lmqt_msec
);
1595 igmp_source_timer_on(group
, source
, lmqt_msec
);
1599 igmp_v3_send_query (struct igmp_group
*group
,
1605 struct in_addr dst_addr
,
1606 struct in_addr group_addr
,
1607 int query_max_response_time_dsec
,
1609 uint8_t querier_robustness_variable
,
1610 uint16_t querier_query_interval
)
1613 uint8_t max_resp_code
;
1616 struct sockaddr_in to
;
1620 zassert(num_sources
>= 0);
1622 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1623 if (msg_size
> query_buf_size
) {
1624 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1625 __FILE__
, __PRETTY_FUNCTION__
,
1626 msg_size
, query_buf_size
);
1630 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1631 zassert((s_flag
== 0) || (s_flag
== 1));
1633 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1634 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1637 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1639 If non-zero, the QRV field contains the [Robustness Variable]
1640 value used by the querier, i.e., the sender of the Query. If the
1641 querier's [Robustness Variable] exceeds 7, the maximum value of
1642 the QRV field, the QRV is set to zero.
1644 if (querier_robustness_variable
> 7) {
1645 querier_robustness_variable
= 0;
1648 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1649 query_buf
[1] = max_resp_code
;
1650 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = 0; /* for computing checksum */
1651 memcpy(query_buf
+4, &group_addr
, sizeof(struct in_addr
));
1653 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1654 query_buf
[9] = qqic
;
1655 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) = htons(num_sources
);
1657 checksum
= in_cksum(query_buf
, msg_size
);
1658 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1660 if (PIM_DEBUG_IGMP_PACKETS
) {
1661 char dst_str
[INET_ADDRSTRLEN
];
1662 char group_str
[INET_ADDRSTRLEN
];
1663 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1664 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1665 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",
1666 dst_str
, ifname
, group_str
,
1667 num_sources
, msg_size
, s_flag
, querier_robustness_variable
,
1668 querier_query_interval
, qqic
);
1671 memset(&to
, 0, sizeof(to
));
1672 to
.sin_family
= AF_INET
;
1673 to
.sin_addr
= dst_addr
;
1676 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1677 (struct sockaddr
*)&to
, tolen
);
1678 if (sent
!= (ssize_t
) msg_size
) {
1679 char dst_str
[INET_ADDRSTRLEN
];
1680 char group_str
[INET_ADDRSTRLEN
];
1681 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1682 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1684 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1685 dst_str
, ifname
, group_str
, msg_size
, errno
, safe_strerror(errno
));
1688 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1689 dst_str
, ifname
, group_str
, msg_size
, sent
);
1695 s_flag sanity test: s_flag must be set for general queries
1697 RFC 3376: 6.6.1. Timer Updates
1699 When a router sends or receives a query with a clear Suppress
1700 Router-Side Processing flag, it must update its timers to reflect
1701 the correct timeout values for the group or sources being queried.
1703 General queries don't trigger timer update.
1706 /* general query? */
1707 if (PIM_INADDR_IS_ANY(group_addr
)) {
1708 char dst_str
[INET_ADDRSTRLEN
];
1709 char group_str
[INET_ADDRSTRLEN
];
1710 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1711 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1712 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1713 __PRETTY_FUNCTION__
,
1714 dst_str
, ifname
, group_str
, num_sources
);
1720 igmp_v3_recv_query (struct igmp_sock
*igmp
, const char *from_str
, char *igmp_msg
)
1722 struct interface
*ifp
;
1723 struct pim_interface
*pim_ifp
;
1724 struct in_addr group_addr
;
1725 uint8_t resv_s_qrv
= 0;
1730 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1731 ifp
= igmp
->interface
;
1732 pim_ifp
= ifp
->info
;
1735 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1737 * Routers adopt the QRV value from the most recently received Query
1738 * as their own [Robustness Variable] value, unless that most
1739 * recently received QRV was zero, in which case the receivers use
1740 * the default [Robustness Variable] value specified in section 8.1
1741 * or a statically configured value.
1743 resv_s_qrv
= igmp_msg
[8];
1744 qrv
= 7 & resv_s_qrv
;
1745 igmp
->querier_robustness_variable
= qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
1748 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1750 * Multicast routers that are not the current querier adopt the QQI
1751 * value from the most recently received Query as their own [Query
1752 * Interval] value, unless that most recently received QQI was zero,
1753 * in which case the receiving routers use the default.
1755 if (igmp
->t_other_querier_timer
) {
1756 /* other querier present */
1760 qqi
= igmp_msg_decode8to16(qqic
);
1761 igmp
->querier_query_interval
= qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
1763 if (PIM_DEBUG_IGMP_TRACE
) {
1764 char ifaddr_str
[INET_ADDRSTRLEN
];
1765 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
1766 zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1768 qqi
? "recv-non-default" : "default",
1769 igmp
->querier_query_interval
,
1776 * RFC 3376: 6.6.1. Timer Updates
1778 * When a router sends or receives a query with a clear Suppress
1779 * Router-Side Processing flag, it must update its timers to reflect
1780 * the correct timeout values for the group or sources being queried.
1782 * General queries don't trigger timer update.
1784 s_flag
= (1 << 3) & resv_s_qrv
;
1787 /* s_flag is clear */
1789 if (PIM_INADDR_IS_ANY(group_addr
)) {
1790 /* this is a general query */
1791 /* log that general query should have the s_flag set */
1792 zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1793 from_str
, ifp
->name
);
1795 struct igmp_group
*group
;
1797 /* this is a non-general query: perform timer updates */
1799 group
= find_group_by_addr(igmp
, group_addr
);
1801 int recv_num_sources
= ntohs(*(uint16_t *)(igmp_msg
+ IGMP_V3_NUMSOURCES_OFFSET
));
1804 * RFC 3376: 6.6.1. Timer Updates
1805 * Query Q(G,A): Source Timer for sources in A are lowered to LMQT
1806 * Query Q(G): Group Timer is lowered to LMQT
1808 if (recv_num_sources
< 1) {
1809 /* Query Q(G): Group Timer is lowered to LMQT */
1811 igmp_group_timer_lower_to_lmqt(group
);
1813 /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
1815 /* Scan sources in query and lower their timers to LMQT */
1816 struct in_addr
*sources
= (struct in_addr
*)(igmp_msg
+ IGMP_V3_SOURCES_OFFSET
);
1817 for (i
= 0; i
< recv_num_sources
; ++i
) {
1818 struct in_addr src_addr
;
1819 struct igmp_source
*src
;
1820 memcpy(&src_addr
, sources
+ i
, sizeof(struct in_addr
));
1821 src
= igmp_find_source_by_addr(group
, src_addr
);
1823 igmp_source_timer_lower_to_lmqt(src
);
1828 char group_str
[INET_ADDRSTRLEN
];
1829 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1830 zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update",
1831 from_str
, ifp
->name
, group_str
);
1834 } /* s_flag is clear: timer updates */
1838 igmp_v3_recv_report (struct igmp_sock
*igmp
,
1839 struct in_addr from
, const char *from_str
,
1840 char *igmp_msg
, int igmp_msg_len
)
1842 uint16_t recv_checksum
;
1845 uint8_t *group_record
;
1846 uint8_t *report_pastend
= (uint8_t *) igmp_msg
+ igmp_msg_len
;
1847 struct interface
*ifp
= igmp
->interface
;
1851 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1852 zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1853 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V3_MSG_MIN_SIZE
);
1857 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1859 /* for computing checksum */
1860 *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1862 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1863 if (checksum
!= recv_checksum
) {
1864 zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1865 from_str
, ifp
->name
, recv_checksum
, checksum
);
1869 num_groups
= ntohs(*(uint16_t *) (igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1870 if (num_groups
< 1) {
1871 zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
1872 from_str
, ifp
->name
);
1876 if (PIM_DEBUG_IGMP_PACKETS
) {
1877 zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1878 from_str
, ifp
->name
, igmp_msg_len
, checksum
, num_groups
);
1881 group_record
= (uint8_t *) igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1884 for (i
= 0; i
< num_groups
; ++i
) {
1885 struct in_addr rec_group
;
1890 int rec_num_sources
;
1895 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
) > report_pastend
) {
1896 zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
1897 from_str
, ifp
->name
);
1901 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1902 rec_auxdatalen
= group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1903 rec_num_sources
= ntohs(* (uint16_t *) (group_record
+ IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1905 memcpy(&rec_group
, group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
, sizeof(struct in_addr
));
1907 if (PIM_DEBUG_IGMP_PACKETS
) {
1908 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1909 from_str
, ifp
->name
, i
, rec_type
, rec_auxdatalen
, rec_num_sources
, inet_ntoa(rec_group
));
1914 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1916 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1918 if ((src
+ 4) > report_pastend
) {
1919 zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
1920 from_str
, ifp
->name
);
1924 if (PIM_DEBUG_IGMP_PACKETS
) {
1927 if (!inet_ntop(AF_INET
, src
, src_str
, sizeof(src_str
)))
1928 sprintf(src_str
, "<source?>");
1930 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1931 from_str
, ifp
->name
, i
, inet_ntoa(rec_group
), src_str
);
1933 } /* for (sources) */
1936 lncb
.family
= AF_INET
;
1937 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1938 lncb
.prefixlen
= 24;
1941 g
.u
.prefix4
= rec_group
;
1944 * If we receive a igmp report with the group in 224.0.0.0/24
1945 * then we should ignore it
1947 if (prefix_match(&lncb
, &g
))
1952 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1953 igmpv3_report_isin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1955 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1956 igmpv3_report_isex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
, 0);
1958 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
1959 igmpv3_report_toin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1961 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
1962 igmpv3_report_toex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1964 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
1965 igmpv3_report_allow(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1967 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
1968 igmpv3_report_block(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1971 zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
1972 from_str
, ifp
->name
, rec_type
);
1975 group_record
+= 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
1978 } /* for (group records) */