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,
31 #include "pim_iface.h"
33 #include "pim_igmpv3.h"
37 #include "pim_zebra.h"
40 static void group_retransmit_timer_on(struct igmp_group
*group
);
41 static long igmp_group_timer_remain_msec(struct igmp_group
*group
);
42 static long igmp_source_timer_remain_msec(struct igmp_source
*source
);
43 static void group_query_send(struct igmp_group
*group
);
44 static void source_query_send_by_flag(struct igmp_group
*group
,
45 int num_sources_tosend
);
47 static void on_trace(const char *label
,
48 struct interface
*ifp
, struct in_addr from
,
49 struct in_addr group_addr
,
50 int num_sources
, struct in_addr
*sources
)
52 if (PIM_DEBUG_IGMP_TRACE
) {
53 char from_str
[INET_ADDRSTRLEN
];
54 char group_str
[INET_ADDRSTRLEN
];
56 pim_inet4_dump("<from?>", from
, from_str
, sizeof(from_str
));
57 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
59 zlog_debug("%s: from %s on %s: group=%s sources=%d",
60 label
, from_str
, ifp
->name
, group_str
, num_sources
);
64 void igmp_group_reset_gmi(struct igmp_group
*group
)
66 long group_membership_interval_msec
;
67 struct pim_interface
*pim_ifp
;
68 struct igmp_sock
*igmp
;
69 struct interface
*ifp
;
71 igmp
= group
->group_igmp_sock
;
72 ifp
= igmp
->interface
;
76 RFC 3376: 8.4. Group Membership Interval
78 The Group Membership Interval is the amount of time that must pass
79 before a multicast router decides there are no more members of a
80 group or a particular source on a network.
82 This value MUST be ((the Robustness Variable) times (the Query
83 Interval)) plus (one Query Response Interval).
85 group_membership_interval_msec = querier_robustness_variable *
86 (1000 * querier_query_interval) +
87 100 * query_response_interval_dsec;
89 group_membership_interval_msec
=
90 PIM_IGMP_GMI_MSEC(igmp
->querier_robustness_variable
,
91 igmp
->querier_query_interval
,
92 pim_ifp
->igmp_query_max_response_time_dsec
);
94 if (PIM_DEBUG_IGMP_TRACE
) {
95 char group_str
[INET_ADDRSTRLEN
];
96 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
97 zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
99 group_membership_interval_msec
/ 1000,
100 group_membership_interval_msec
% 1000,
105 RFC 3376: 6.2.2. Definition of Group Timers
107 The group timer is only used when a group is in EXCLUDE mode and
108 it represents the time for the *filter-mode* of the group to
109 expire and switch to INCLUDE mode.
111 zassert(group
->group_filtermode_isexcl
);
113 igmp_group_timer_on(group
, group_membership_interval_msec
, ifp
->name
);
116 static int igmp_source_timer(struct thread
*t
)
118 struct igmp_source
*source
;
119 struct igmp_group
*group
;
121 source
= THREAD_ARG(t
);
123 group
= source
->source_group
;
125 if (PIM_DEBUG_IGMP_TRACE
) {
126 char group_str
[INET_ADDRSTRLEN
];
127 char source_str
[INET_ADDRSTRLEN
];
128 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
129 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
130 zlog_debug("%s: Source timer expired for group %s source %s on %s",
132 group_str
, source_str
,
133 group
->group_igmp_sock
->interface
->name
);
136 zassert(source
->t_source_timer
);
137 source
->t_source_timer
= NULL
;
140 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
143 Filter-Mode Source Timer Value Action
144 ----------- ------------------ ------
145 INCLUDE TIMER == 0 Suggest to stop forwarding
146 traffic from source and
147 remove source record. If
148 there are no more source
149 records for the group, delete
152 EXCLUDE TIMER == 0 Suggest to not forward
154 (DO NOT remove record)
156 Source timer switched from (T > 0) to (T == 0): disable forwarding.
159 zassert(!source
->t_source_timer
);
161 if (group
->group_filtermode_isexcl
) {
164 igmp_source_forward_stop(source
);
169 /* igmp_source_delete() will stop forwarding source */
170 igmp_source_delete(source
);
173 If there are no more source records for the group, delete group
176 if (!listcount(group
->group_source_list
)) {
177 igmp_group_delete_empty_include(group
);
184 static void source_timer_off(struct igmp_group
*group
,
185 struct igmp_source
*source
)
187 if (!source
->t_source_timer
)
190 if (PIM_DEBUG_IGMP_TRACE
) {
191 char group_str
[INET_ADDRSTRLEN
];
192 char source_str
[INET_ADDRSTRLEN
];
193 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
194 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
195 zlog_debug("Cancelling TIMER event for group %s source %s on %s",
196 group_str
, source_str
,
197 group
->group_igmp_sock
->interface
->name
);
200 THREAD_OFF(source
->t_source_timer
);
201 zassert(!source
->t_source_timer
);
204 static void igmp_source_timer_on(struct igmp_group
*group
,
205 struct igmp_source
*source
,
208 source_timer_off(group
, source
);
210 if (PIM_DEBUG_IGMP_EVENTS
) {
211 char group_str
[INET_ADDRSTRLEN
];
212 char source_str
[INET_ADDRSTRLEN
];
213 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
214 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
215 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
216 interval_msec
/ 1000,
217 interval_msec
% 1000,
218 group_str
, source_str
,
219 group
->group_igmp_sock
->interface
->name
);
222 THREAD_TIMER_MSEC_ON(master
, source
->t_source_timer
,
224 source
, interval_msec
);
225 zassert(source
->t_source_timer
);
228 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
230 Source timer switched from (T == 0) to (T > 0): enable forwarding.
232 igmp_source_forward_start(source
);
235 void igmp_source_reset_gmi(struct igmp_sock
*igmp
,
236 struct igmp_group
*group
,
237 struct igmp_source
*source
)
239 long group_membership_interval_msec
;
240 struct pim_interface
*pim_ifp
;
241 struct interface
*ifp
;
243 ifp
= igmp
->interface
;
246 group_membership_interval_msec
=
247 PIM_IGMP_GMI_MSEC(igmp
->querier_robustness_variable
,
248 igmp
->querier_query_interval
,
249 pim_ifp
->igmp_query_max_response_time_dsec
);
251 if (PIM_DEBUG_IGMP_TRACE
) {
252 char group_str
[INET_ADDRSTRLEN
];
253 char source_str
[INET_ADDRSTRLEN
];
255 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
256 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
258 zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
260 group_membership_interval_msec
/ 1000,
261 group_membership_interval_msec
% 1000,
266 igmp_source_timer_on(group
, source
,
267 group_membership_interval_msec
);
270 static void source_mark_delete_flag(struct igmp_group
*group
)
272 struct listnode
*src_node
;
273 struct igmp_source
*src
;
275 for (ALL_LIST_ELEMENTS_RO (group
->group_source_list
, src_node
, src
)) {
276 IGMP_SOURCE_DO_DELETE(src
->source_flags
);
280 static void source_mark_send_flag (struct igmp_group
*group
)
282 struct listnode
*src_node
;
283 struct igmp_source
*src
;
285 for (ALL_LIST_ELEMENTS_RO (group
->group_source_list
, src_node
, src
)) {
286 IGMP_SOURCE_DO_SEND(src
->source_flags
);
290 static int source_mark_send_flag_by_timer (struct igmp_group
*group
)
292 struct listnode
*src_node
;
293 struct igmp_source
*src
;
294 int num_marked_sources
= 0;
296 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
297 /* Is source timer running? */
298 if (src
->t_source_timer
) {
299 IGMP_SOURCE_DO_SEND(src
->source_flags
);
300 ++num_marked_sources
;
303 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
307 return num_marked_sources
;
310 static void source_clear_send_flag(struct list
*source_list
)
312 struct listnode
*src_node
;
313 struct igmp_source
*src
;
315 for (ALL_LIST_ELEMENTS_RO(source_list
, src_node
, src
)) {
316 IGMP_SOURCE_DONT_SEND(src
->source_flags
);
321 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
323 static void group_exclude_fwd_anysrc_ifempty(struct igmp_group
*group
)
325 zassert(group
->group_filtermode_isexcl
);
327 if (listcount(group
->group_source_list
) < 1) {
328 igmp_anysource_forward_start(group
);
332 void igmp_source_free(struct igmp_source
*source
)
334 /* make sure there is no source timer running */
335 zassert(!source
->t_source_timer
);
337 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE
, source
);
340 static void source_channel_oil_detach(struct igmp_source
*source
)
342 if (source
->source_channel_oil
) {
343 pim_channel_oil_del(source
->source_channel_oil
);
344 source
->source_channel_oil
= NULL
;
349 igmp_source_delete: stop fowarding, and delete the source
350 igmp_source_forward_stop: stop fowarding, but keep the source
352 void igmp_source_delete(struct igmp_source
*source
)
354 struct igmp_group
*group
;
356 group
= source
->source_group
;
358 if (PIM_DEBUG_IGMP_TRACE
) {
359 char group_str
[INET_ADDRSTRLEN
];
360 char source_str
[INET_ADDRSTRLEN
];
361 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
362 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
363 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
364 source_str
, group_str
,
365 group
->group_igmp_sock
->fd
,
366 group
->group_igmp_sock
->interface
->name
);
369 source_timer_off(group
, source
);
370 igmp_source_forward_stop(source
);
372 /* sanity check that forwarding has been disabled */
373 if (IGMP_SOURCE_TEST_FORWARDING(source
->source_flags
)) {
374 char group_str
[INET_ADDRSTRLEN
];
375 char source_str
[INET_ADDRSTRLEN
];
376 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
377 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
378 zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
380 source_str
, group_str
,
381 group
->group_igmp_sock
->fd
,
382 group
->group_igmp_sock
->interface
->name
);
386 source_channel_oil_detach(source
);
389 notice that listnode_delete() can't be moved
390 into igmp_source_free() because the later is
391 called by list_delete_all_node()
393 listnode_delete(group
->group_source_list
, source
);
395 igmp_source_free(source
);
397 if (group
->group_filtermode_isexcl
) {
398 group_exclude_fwd_anysrc_ifempty(group
);
402 static void source_delete_by_flag(struct list
*source_list
)
404 struct listnode
*src_node
;
405 struct listnode
*src_nextnode
;
406 struct igmp_source
*src
;
408 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
409 if (IGMP_SOURCE_TEST_DELETE(src
->source_flags
))
410 igmp_source_delete(src
);
413 void igmp_source_delete_expired(struct list
*source_list
)
415 struct listnode
*src_node
;
416 struct listnode
*src_nextnode
;
417 struct igmp_source
*src
;
419 for (ALL_LIST_ELEMENTS(source_list
, src_node
, src_nextnode
, src
))
420 if (!src
->t_source_timer
)
421 igmp_source_delete(src
);
424 struct igmp_source
*igmp_find_source_by_addr(struct igmp_group
*group
,
425 struct in_addr src_addr
)
427 struct listnode
*src_node
;
428 struct igmp_source
*src
;
430 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
))
431 if (src_addr
.s_addr
== src
->source_addr
.s_addr
)
438 source_new (struct igmp_group
*group
,
439 struct in_addr src_addr
)
441 struct igmp_source
*src
;
443 if (PIM_DEBUG_IGMP_TRACE
) {
444 char group_str
[INET_ADDRSTRLEN
];
445 char source_str
[INET_ADDRSTRLEN
];
446 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
447 pim_inet4_dump("<source?>", src_addr
, source_str
, sizeof(source_str
));
448 zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
449 source_str
, group_str
,
450 group
->group_igmp_sock
->fd
,
451 group
->group_igmp_sock
->interface
->name
);
454 src
= XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE
, sizeof(*src
));
456 zlog_warn("%s %s: XCALLOC() failure",
457 __FILE__
, __PRETTY_FUNCTION__
);
458 return 0; /* error, not found, could not create */
461 src
->t_source_timer
= NULL
;
462 src
->source_group
= group
; /* back pointer */
463 src
->source_addr
= src_addr
;
464 src
->source_creation
= pim_time_monotonic_sec();
465 src
->source_flags
= 0;
466 src
->source_query_retransmit_count
= 0;
467 src
->source_channel_oil
= NULL
;
469 listnode_add(group
->group_source_list
, src
);
471 zassert(!src
->t_source_timer
); /* source timer == 0 */
473 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
474 igmp_anysource_forward_stop(group
);
479 static struct igmp_source
*add_source_by_addr(struct igmp_sock
*igmp
,
480 struct igmp_group
*group
,
481 struct in_addr src_addr
)
483 struct igmp_source
*src
;
485 src
= igmp_find_source_by_addr(group
, src_addr
);
490 src
= source_new(group
, src_addr
);
498 static void allow(struct igmp_sock
*igmp
, struct in_addr from
,
499 struct in_addr group_addr
,
500 int num_sources
, struct in_addr
*sources
)
502 struct igmp_source
*source
;
503 struct igmp_group
*group
;
506 /* non-existant group is created as INCLUDE {empty} */
507 group
= igmp_add_group_by_addr(igmp
, group_addr
);
512 /* scan received sources */
513 for (i
= 0; i
< num_sources
; ++i
) {
514 struct in_addr
*src_addr
;
516 src_addr
= sources
+ i
;
518 source
= add_source_by_addr(igmp
, group
, *src_addr
);
524 RFC 3376: 6.4.1. Reception of Current-State Records
526 When receiving IS_IN reports for groups in EXCLUDE mode is
527 sources should be moved from set with (timers = 0) to set with
530 igmp_source_reset_gmi() below, resetting the source timers to
531 GMI, accomplishes this.
533 igmp_source_reset_gmi(igmp
, group
, source
);
535 } /* scan received sources */
537 if ((num_sources
== 0) &&
538 (group
->group_filtermode_isexcl
) &&
539 (listcount (group
->group_source_list
) == 1))
541 struct in_addr star
= { .s_addr
= INADDR_ANY
};
543 source
= igmp_find_source_by_addr (group
, star
);
545 igmp_source_reset_gmi (igmp
, group
, source
);
549 void igmpv3_report_isin(struct igmp_sock
*igmp
, struct in_addr from
,
550 struct in_addr group_addr
,
551 int num_sources
, struct in_addr
*sources
)
553 on_trace(__PRETTY_FUNCTION__
,
554 igmp
->interface
, from
, group_addr
, num_sources
, sources
);
556 allow(igmp
, from
, group_addr
, num_sources
, sources
);
559 static void isex_excl(struct igmp_group
*group
,
560 int num_sources
, struct in_addr
*sources
)
562 struct igmp_source
*source
;
566 zassert(group
->group_filtermode_isexcl
);
568 /* E.1: set deletion flag for known sources (X,Y) */
569 source_mark_delete_flag (group
);
571 /* scan received sources (A) */
572 for (i
= 0; i
< num_sources
; ++i
) {
573 struct in_addr
*src_addr
;
575 src_addr
= sources
+ i
;
577 /* E.2: lookup reported source from (A) in (X,Y) */
578 source
= igmp_find_source_by_addr(group
, *src_addr
);
580 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
581 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
584 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
585 source
= source_new(group
, *src_addr
);
587 /* ugh, internal malloc failure, skip source */
590 zassert(!source
->t_source_timer
); /* timer == 0 */
591 igmp_source_reset_gmi(group
->group_igmp_sock
, group
, source
);
592 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
595 } /* scan received sources */
598 * If we are in isexcl mode and num_sources == 0
599 * than that means we have a *,g entry that
600 * needs to be handled
602 if (group
->group_filtermode_isexcl
&& num_sources
== 0)
604 struct in_addr star
= { .s_addr
= INADDR_ANY
};
605 source
= igmp_find_source_by_addr (group
, star
);
608 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
609 igmp_source_reset_gmi (group
->group_igmp_sock
, group
, source
);
613 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
614 source_delete_by_flag(group
->group_source_list
);
617 static void isex_incl(struct igmp_group
*group
,
618 int num_sources
, struct in_addr
*sources
)
623 zassert(!group
->group_filtermode_isexcl
);
625 /* I.1: set deletion flag for known sources (A) */
626 source_mark_delete_flag (group
);
628 /* scan received sources (B) */
629 for (i
= 0; i
< num_sources
; ++i
) {
630 struct igmp_source
*source
;
631 struct in_addr
*src_addr
;
633 src_addr
= sources
+ i
;
635 /* I.2: lookup reported source (B) */
636 source
= igmp_find_source_by_addr(group
, *src_addr
);
638 /* I.3: if found, clear deletion flag (A*B) */
639 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
642 /* I.4: if not found, create source with timer=0 (B-A) */
643 source
= source_new(group
, *src_addr
);
645 /* ugh, internal malloc failure, skip source */
648 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
651 } /* scan received sources */
653 /* I.5: delete all sources marked with deletion flag (A-B) */
654 source_delete_by_flag(group
->group_source_list
);
656 group
->group_filtermode_isexcl
= 1; /* boolean=true */
658 zassert(group
->group_filtermode_isexcl
);
660 group_exclude_fwd_anysrc_ifempty(group
);
663 void igmpv3_report_isex(struct igmp_sock
*igmp
, struct in_addr from
,
664 struct in_addr group_addr
,
665 int num_sources
, struct in_addr
*sources
,
666 int from_igmp_v2_report
)
668 struct interface
*ifp
= igmp
->interface
;
669 struct igmp_group
*group
;
671 on_trace(__PRETTY_FUNCTION__
,
672 ifp
, from
, group_addr
, num_sources
, sources
);
674 /* non-existant group is created as INCLUDE {empty} */
675 group
= igmp_add_group_by_addr(igmp
, group_addr
);
680 /* So we can display how we learned the group in our show command output */
681 if (from_igmp_v2_report
)
682 group
->igmp_version
= 2;
684 if (group
->group_filtermode_isexcl
) {
686 isex_excl(group
, num_sources
, sources
);
690 isex_incl(group
, num_sources
, sources
);
691 zassert(group
->group_filtermode_isexcl
);
694 zassert(group
->group_filtermode_isexcl
);
696 igmp_group_reset_gmi(group
);
699 static void toin_incl(struct igmp_group
*group
,
700 int num_sources
, struct in_addr
*sources
)
702 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
703 int num_sources_tosend
= listcount(group
->group_source_list
);
706 /* Set SEND flag for all known sources (A) */
707 source_mark_send_flag (group
);
709 /* Scan received sources (B) */
710 for (i
= 0; i
< num_sources
; ++i
) {
711 struct igmp_source
*source
;
712 struct in_addr
*src_addr
;
714 src_addr
= sources
+ i
;
716 /* Lookup reported source (B) */
717 source
= igmp_find_source_by_addr(group
, *src_addr
);
719 /* If found, clear SEND flag (A*B) */
720 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
721 --num_sources_tosend
;
724 /* If not found, create new source */
725 source
= source_new(group
, *src_addr
);
727 /* ugh, internal malloc failure, skip source */
733 igmp_source_reset_gmi(igmp
, group
, source
);
736 /* Send sources marked with SEND flag: Q(G,A-B) */
737 if (num_sources_tosend
> 0) {
738 source_query_send_by_flag(group
, num_sources_tosend
);
742 static void toin_excl(struct igmp_group
*group
,
743 int num_sources
, struct in_addr
*sources
)
745 struct igmp_sock
*igmp
= group
->group_igmp_sock
;
746 int num_sources_tosend
;
749 /* Set SEND flag for X (sources with timer > 0) */
750 num_sources_tosend
= source_mark_send_flag_by_timer (group
);
752 /* Scan received sources (A) */
753 for (i
= 0; i
< num_sources
; ++i
) {
754 struct igmp_source
*source
;
755 struct in_addr
*src_addr
;
757 src_addr
= sources
+ i
;
759 /* Lookup reported source (A) */
760 source
= igmp_find_source_by_addr(group
, *src_addr
);
762 if (source
->t_source_timer
) {
763 /* If found and timer running, clear SEND flag (X*A) */
764 IGMP_SOURCE_DONT_SEND(source
->source_flags
);
765 --num_sources_tosend
;
769 /* If not found, create new source */
770 source
= source_new(group
, *src_addr
);
772 /* ugh, internal malloc failure, skip source */
778 igmp_source_reset_gmi(igmp
, group
, source
);
781 /* Send sources marked with SEND flag: Q(G,X-A) */
782 if (num_sources_tosend
> 0) {
783 source_query_send_by_flag(group
, num_sources_tosend
);
787 group_query_send(group
);
790 void igmpv3_report_toin(struct igmp_sock
*igmp
, struct in_addr from
,
791 struct in_addr group_addr
,
792 int num_sources
, struct in_addr
*sources
)
794 struct interface
*ifp
= igmp
->interface
;
795 struct igmp_group
*group
;
797 on_trace(__PRETTY_FUNCTION__
,
798 ifp
, from
, group_addr
, num_sources
, sources
);
801 * If the requested filter mode is INCLUDE *and* the requested source
802 * list is empty, then the entry corresponding to the requested
803 * interface and multicast address is deleted if present. If no such
804 * entry is present, the request is ignored.
808 /* non-existant group is created as INCLUDE {empty} */
809 group
= igmp_add_group_by_addr(igmp
, group_addr
);
816 group
= find_group_by_addr (igmp
, group_addr
);
821 if (group
->group_filtermode_isexcl
) {
823 toin_excl(group
, num_sources
, sources
);
827 toin_incl(group
, num_sources
, sources
);
831 static void toex_incl(struct igmp_group
*group
,
832 int num_sources
, struct in_addr
*sources
)
834 int num_sources_tosend
= 0;
837 zassert(!group
->group_filtermode_isexcl
);
839 /* Set DELETE flag for all known sources (A) */
840 source_mark_delete_flag (group
);
842 /* Clear off SEND flag from all known sources (A) */
843 source_clear_send_flag(group
->group_source_list
);
845 /* Scan received sources (B) */
846 for (i
= 0; i
< num_sources
; ++i
) {
847 struct igmp_source
*source
;
848 struct in_addr
*src_addr
;
850 src_addr
= sources
+ i
;
852 /* Lookup reported source (B) */
853 source
= igmp_find_source_by_addr(group
, *src_addr
);
855 /* If found, clear deletion flag: (A*B) */
856 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
857 /* and set SEND flag (A*B) */
858 IGMP_SOURCE_DO_SEND(source
->source_flags
);
859 ++num_sources_tosend
;
862 /* If source not found, create source with timer=0: (B-A)=0 */
863 source
= source_new(group
, *src_addr
);
865 /* ugh, internal malloc failure, skip source */
868 zassert(!source
->t_source_timer
); /* (B-A) timer=0 */
871 } /* Scan received sources (B) */
873 group
->group_filtermode_isexcl
= 1; /* boolean=true */
875 /* Delete all sources marked with DELETE flag (A-B) */
876 source_delete_by_flag(group
->group_source_list
);
878 /* Send sources marked with SEND flag: Q(G,A*B) */
879 if (num_sources_tosend
> 0) {
880 source_query_send_by_flag(group
, num_sources_tosend
);
883 zassert(group
->group_filtermode_isexcl
);
885 group_exclude_fwd_anysrc_ifempty(group
);
888 static void toex_excl(struct igmp_group
*group
,
889 int num_sources
, struct in_addr
*sources
)
891 int num_sources_tosend
= 0;
894 /* set DELETE flag for all known sources (X,Y) */
895 source_mark_delete_flag (group
);
897 /* clear off SEND flag from all known sources (X,Y) */
898 source_clear_send_flag(group
->group_source_list
);
900 if (num_sources
== 0)
902 struct igmp_source
*source
;
903 struct in_addr any
= { .s_addr
= INADDR_ANY
};
905 source
= igmp_find_source_by_addr (group
, any
);
907 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
910 /* scan received sources (A) */
911 for (i
= 0; i
< num_sources
; ++i
) {
912 struct igmp_source
*source
;
913 struct in_addr
*src_addr
;
915 src_addr
= sources
+ i
;
917 /* lookup reported source (A) in known sources (X,Y) */
918 source
= igmp_find_source_by_addr(group
, *src_addr
);
920 /* if found, clear off DELETE flag from reported source (A) */
921 IGMP_SOURCE_DONT_DELETE(source
->source_flags
);
924 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
925 long group_timer_msec
;
926 source
= source_new(group
, *src_addr
);
928 /* ugh, internal malloc failure, skip source */
932 zassert(!source
->t_source_timer
); /* timer == 0 */
933 group_timer_msec
= igmp_group_timer_remain_msec(group
);
934 igmp_source_timer_on(group
, source
, group_timer_msec
);
935 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
937 /* make sure source is created with DELETE flag unset */
938 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
941 /* make sure reported source has DELETE flag unset */
942 zassert(!IGMP_SOURCE_TEST_DELETE(source
->source_flags
));
944 if (source
->t_source_timer
) {
945 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
946 IGMP_SOURCE_DO_SEND(source
->source_flags
);
947 ++num_sources_tosend
;
950 } /* scan received sources (A) */
953 delete all sources marked with DELETE flag:
957 source_delete_by_flag(group
->group_source_list
);
959 /* send sources marked with SEND flag: Q(G,A-Y) */
960 if (num_sources_tosend
> 0) {
961 source_query_send_by_flag(group
, num_sources_tosend
);
965 void igmpv3_report_toex(struct igmp_sock
*igmp
, struct in_addr from
,
966 struct in_addr group_addr
,
967 int num_sources
, struct in_addr
*sources
)
969 struct interface
*ifp
= igmp
->interface
;
970 struct igmp_group
*group
;
972 on_trace(__PRETTY_FUNCTION__
,
973 ifp
, from
, group_addr
, num_sources
, sources
);
975 /* non-existant group is created as INCLUDE {empty} */
976 group
= igmp_add_group_by_addr(igmp
, group_addr
);
981 if (group
->group_filtermode_isexcl
) {
983 toex_excl(group
, num_sources
, sources
);
987 toex_incl(group
, num_sources
, sources
);
988 zassert(group
->group_filtermode_isexcl
);
990 zassert(group
->group_filtermode_isexcl
);
992 /* Group Timer=GMI */
993 igmp_group_reset_gmi(group
);
996 void igmpv3_report_allow(struct igmp_sock
*igmp
, struct in_addr from
,
997 struct in_addr group_addr
,
998 int num_sources
, struct in_addr
*sources
)
1000 on_trace(__PRETTY_FUNCTION__
,
1001 igmp
->interface
, from
, group_addr
, num_sources
, sources
);
1003 allow(igmp
, from
, group_addr
, num_sources
, sources
);
1007 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1009 When transmitting a group specific query, if the group timer is
1010 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1011 in the query message.
1013 static void group_retransmit_group(struct igmp_group
*group
)
1015 struct igmp_sock
*igmp
;
1016 struct pim_interface
*pim_ifp
;
1017 long lmqc
; /* Last Member Query Count */
1018 long lmqi_msec
; /* Last Member Query Interval */
1019 long lmqt_msec
; /* Last Member Query Time */
1023 igmp
= group
->group_igmp_sock
;
1024 pim_ifp
= igmp
->interface
->info
;
1026 if (pim_ifp
->igmp_version
== 3) {
1027 query_buf_size
= PIM_IGMP_BUFSIZE_WRITE
;
1029 query_buf_size
= IGMP_V12_MSG_SIZE
;
1032 char query_buf
[query_buf_size
];
1034 lmqc
= igmp
->querier_robustness_variable
;
1035 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1036 lmqt_msec
= lmqc
* lmqi_msec
;
1039 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1041 When transmitting a group specific query, if the group timer is
1042 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1043 in the query message.
1045 s_flag
= igmp_group_timer_remain_msec(group
) > lmqt_msec
;
1047 if (PIM_DEBUG_IGMP_TRACE
) {
1048 char group_str
[INET_ADDRSTRLEN
];
1049 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1050 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1051 group_str
, igmp
->interface
->name
, s_flag
,
1052 group
->group_specific_query_retransmit_count
);
1056 RFC3376: 4.1.12. IP Destination Addresses for Queries
1058 Group-Specific and Group-and-Source-Specific Queries are sent with
1059 an IP destination address equal to the multicast address of
1063 igmp_send_query(pim_ifp
->igmp_version
,
1066 igmp
->interface
->name
,
1069 0 /* num_sources_tosend */,
1070 group
->group_addr
/* dst_addr */,
1071 group
->group_addr
/* group_addr */,
1072 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1074 igmp
->querier_robustness_variable
,
1075 igmp
->querier_query_interval
);
1079 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1081 When building a group and source specific query for a group G, two
1082 separate query messages are sent for the group. The first one has
1083 the "Suppress Router-Side Processing" bit set and contains all the
1084 sources with retransmission state and timers greater than LMQT. The
1085 second has the "Suppress Router-Side Processing" bit clear and
1086 contains all the sources with retransmission state and timers lower
1087 or equal to LMQT. If either of the two calculated messages does not
1088 contain any sources, then its transmission is suppressed.
1090 static int group_retransmit_sources(struct igmp_group
*group
,
1091 int send_with_sflag_set
)
1093 struct igmp_sock
*igmp
;
1094 struct pim_interface
*pim_ifp
;
1095 long lmqc
; /* Last Member Query Count */
1096 long lmqi_msec
; /* Last Member Query Interval */
1097 long lmqt_msec
; /* Last Member Query Time */
1098 char query_buf1
[PIM_IGMP_BUFSIZE_WRITE
]; /* 1 = with s_flag set */
1099 char query_buf2
[PIM_IGMP_BUFSIZE_WRITE
]; /* 2 = with s_flag clear */
1100 int query_buf1_max_sources
;
1101 int query_buf2_max_sources
;
1102 struct in_addr
*source_addr1
;
1103 struct in_addr
*source_addr2
;
1104 int num_sources_tosend1
;
1105 int num_sources_tosend2
;
1106 struct listnode
*src_node
;
1107 struct igmp_source
*src
;
1108 int num_retransmit_sources_left
= 0;
1110 source_addr1
= (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1111 source_addr2
= (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1113 igmp
= group
->group_igmp_sock
;
1114 pim_ifp
= igmp
->interface
->info
;
1116 lmqc
= igmp
->querier_robustness_variable
;
1117 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1118 lmqt_msec
= lmqc
* lmqi_msec
;
1120 /* Scan all group sources */
1121 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1123 /* Source has retransmission state? */
1124 if (src
->source_query_retransmit_count
< 1)
1127 if (--src
->source_query_retransmit_count
> 0) {
1128 ++num_retransmit_sources_left
;
1131 /* Copy source address into appropriate query buffer */
1132 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1133 *source_addr1
= src
->source_addr
;
1137 *source_addr2
= src
->source_addr
;
1143 num_sources_tosend1
= source_addr1
- (struct in_addr
*)(query_buf1
+ IGMP_V3_SOURCES_OFFSET
);
1144 num_sources_tosend2
= source_addr2
- (struct in_addr
*)(query_buf2
+ IGMP_V3_SOURCES_OFFSET
);
1146 if (PIM_DEBUG_IGMP_TRACE
) {
1147 char group_str
[INET_ADDRSTRLEN
];
1148 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1149 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",
1150 group_str
, igmp
->interface
->name
,
1151 num_sources_tosend1
,
1152 num_sources_tosend2
,
1153 send_with_sflag_set
,
1154 num_retransmit_sources_left
);
1157 if (num_sources_tosend1
> 0) {
1159 Send group-and-source-specific query with s_flag set and all
1160 sources with timers greater than LMQT.
1163 if (send_with_sflag_set
) {
1165 query_buf1_max_sources
= (sizeof(query_buf1
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1166 if (num_sources_tosend1
> query_buf1_max_sources
) {
1167 char group_str
[INET_ADDRSTRLEN
];
1168 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1169 zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1170 __PRETTY_FUNCTION__
, group_str
, igmp
->interface
->name
,
1171 num_sources_tosend1
, sizeof(query_buf1
), query_buf1_max_sources
);
1175 RFC3376: 4.1.12. IP Destination Addresses for Queries
1177 Group-Specific and Group-and-Source-Specific Queries are sent with
1178 an IP destination address equal to the multicast address of
1182 igmp_send_query(pim_ifp
->igmp_version
,
1185 igmp
->interface
->name
,
1188 num_sources_tosend1
,
1191 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1193 igmp
->querier_robustness_variable
,
1194 igmp
->querier_query_interval
);
1197 } /* send_with_sflag_set */
1201 if (num_sources_tosend2
> 0) {
1203 Send group-and-source-specific query with s_flag clear and all
1204 sources with timers lower or equal to LMQT.
1207 query_buf2_max_sources
= (sizeof(query_buf2
) - IGMP_V3_SOURCES_OFFSET
) >> 2;
1208 if (num_sources_tosend2
> query_buf2_max_sources
) {
1209 char group_str
[INET_ADDRSTRLEN
];
1210 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1211 zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1212 __PRETTY_FUNCTION__
, group_str
, igmp
->interface
->name
,
1213 num_sources_tosend2
, sizeof(query_buf2
), query_buf2_max_sources
);
1217 RFC3376: 4.1.12. IP Destination Addresses for Queries
1219 Group-Specific and Group-and-Source-Specific Queries are sent with
1220 an IP destination address equal to the multicast address of
1224 igmp_send_query(pim_ifp
->igmp_version
,
1227 igmp
->interface
->name
,
1230 num_sources_tosend2
,
1233 pim_ifp
->igmp_specific_query_max_response_time_dsec
,
1235 igmp
->querier_robustness_variable
,
1236 igmp
->querier_query_interval
);
1240 return num_retransmit_sources_left
;
1243 static int igmp_group_retransmit(struct thread
*t
)
1245 struct igmp_group
*group
;
1246 int num_retransmit_sources_left
;
1247 int send_with_sflag_set
; /* boolean */
1249 group
= THREAD_ARG(t
);
1251 if (PIM_DEBUG_IGMP_TRACE
) {
1252 char group_str
[INET_ADDRSTRLEN
];
1253 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1254 zlog_debug("group_retransmit_timer: group %s on %s",
1255 group_str
, group
->group_igmp_sock
->interface
->name
);
1258 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1259 if (group
->group_specific_query_retransmit_count
> 0) {
1261 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1262 group_retransmit_group(group
);
1263 --group
->group_specific_query_retransmit_count
;
1267 If a group specific query is scheduled to be transmitted at the
1268 same time as a group and source specific query for the same group,
1269 then transmission of the group and source specific message with the
1270 "Suppress Router-Side Processing" bit set may be suppressed.
1272 send_with_sflag_set
= 0; /* boolean=false */
1275 send_with_sflag_set
= 1; /* boolean=true */
1278 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1279 num_retransmit_sources_left
= group_retransmit_sources(group
,
1280 send_with_sflag_set
);
1282 group
->t_group_query_retransmit_timer
= NULL
;
1285 Keep group retransmit timer running if there is any retransmit
1288 if ((num_retransmit_sources_left
> 0) ||
1289 (group
->group_specific_query_retransmit_count
> 0)) {
1290 group_retransmit_timer_on(group
);
1297 group_retransmit_timer_on:
1298 if group retransmit timer isn't running, starts it;
1299 otherwise, do nothing
1301 static void group_retransmit_timer_on(struct igmp_group
*group
)
1303 struct igmp_sock
*igmp
;
1304 struct pim_interface
*pim_ifp
;
1305 long lmqi_msec
; /* Last Member Query Interval */
1307 /* if group retransmit timer is running, do nothing */
1308 if (group
->t_group_query_retransmit_timer
) {
1312 igmp
= group
->group_igmp_sock
;
1313 pim_ifp
= igmp
->interface
->info
;
1315 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1317 if (PIM_DEBUG_IGMP_TRACE
) {
1318 char group_str
[INET_ADDRSTRLEN
];
1319 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1320 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1324 igmp
->interface
->name
);
1327 THREAD_TIMER_MSEC_ON(master
, group
->t_group_query_retransmit_timer
,
1328 igmp_group_retransmit
,
1332 static long igmp_group_timer_remain_msec(struct igmp_group
*group
)
1334 return pim_time_timer_remain_msec(group
->t_group_timer
);
1337 static long igmp_source_timer_remain_msec(struct igmp_source
*source
)
1339 return pim_time_timer_remain_msec(source
->t_source_timer
);
1343 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1345 static void group_query_send(struct igmp_group
*group
)
1347 long lmqc
; /* Last Member Query Count */
1349 lmqc
= group
->group_igmp_sock
->querier_robustness_variable
;
1351 /* lower group timer to lmqt */
1352 igmp_group_timer_lower_to_lmqt(group
);
1354 /* reset retransmission counter */
1355 group
->group_specific_query_retransmit_count
= lmqc
;
1357 /* immediately send group specific query (decrease retransmit counter by 1)*/
1358 group_retransmit_group(group
);
1360 /* make sure group retransmit timer is running */
1361 group_retransmit_timer_on(group
);
1365 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1367 static void source_query_send_by_flag(struct igmp_group
*group
,
1368 int num_sources_tosend
)
1370 struct igmp_sock
*igmp
;
1371 struct pim_interface
*pim_ifp
;
1372 struct listnode
*src_node
;
1373 struct igmp_source
*src
;
1374 long lmqc
; /* Last Member Query Count */
1375 long lmqi_msec
; /* Last Member Query Interval */
1376 long lmqt_msec
; /* Last Member Query Time */
1378 zassert(num_sources_tosend
> 0);
1380 igmp
= group
->group_igmp_sock
;
1381 pim_ifp
= igmp
->interface
->info
;
1383 lmqc
= igmp
->querier_robustness_variable
;
1384 lmqi_msec
= 100 * pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1385 lmqt_msec
= lmqc
* lmqi_msec
;
1388 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1390 (...) for each of the sources in X of group G, with source timer larger
1392 o Set number of retransmissions for each source to [Last Member
1394 o Lower source timer to LMQT.
1396 for (ALL_LIST_ELEMENTS_RO(group
->group_source_list
, src_node
, src
)) {
1397 if (IGMP_SOURCE_TEST_SEND(src
->source_flags
)) {
1398 /* source "src" in X of group G */
1399 if (igmp_source_timer_remain_msec(src
) > lmqt_msec
) {
1400 src
->source_query_retransmit_count
= lmqc
;
1401 igmp_source_timer_lower_to_lmqt(src
);
1406 /* send group-and-source specific queries */
1407 group_retransmit_sources(group
, 1 /* send_with_sflag_set=true */);
1409 /* make sure group retransmit timer is running */
1410 group_retransmit_timer_on(group
);
1413 static void block_excl(struct igmp_group
*group
,
1414 int num_sources
, struct in_addr
*sources
)
1416 int num_sources_tosend
= 0;
1419 /* 1. clear off SEND flag from all known sources (X,Y) */
1420 source_clear_send_flag(group
->group_source_list
);
1422 /* 2. scan received sources (A) */
1423 for (i
= 0; i
< num_sources
; ++i
) {
1424 struct igmp_source
*source
;
1425 struct in_addr
*src_addr
;
1427 src_addr
= sources
+ i
;
1429 /* lookup reported source (A) in known sources (X,Y) */
1430 source
= igmp_find_source_by_addr(group
, *src_addr
);
1432 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1433 long group_timer_msec
;
1434 source
= source_new(group
, *src_addr
);
1436 /* ugh, internal malloc failure, skip source */
1440 zassert(!source
->t_source_timer
); /* timer == 0 */
1441 group_timer_msec
= igmp_group_timer_remain_msec(group
);
1442 igmp_source_timer_on(group
, source
, group_timer_msec
);
1443 zassert(source
->t_source_timer
); /* (A-X-Y) timer > 0 */
1446 if (source
->t_source_timer
) {
1447 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1448 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1449 ++num_sources_tosend
;
1453 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1454 if (num_sources_tosend
> 0) {
1455 source_query_send_by_flag(group
, num_sources_tosend
);
1459 static void block_incl(struct igmp_group
*group
,
1460 int num_sources
, struct in_addr
*sources
)
1462 int num_sources_tosend
= 0;
1465 /* 1. clear off SEND flag from all known sources (B) */
1466 source_clear_send_flag(group
->group_source_list
);
1468 /* 2. scan received sources (A) */
1469 for (i
= 0; i
< num_sources
; ++i
) {
1470 struct igmp_source
*source
;
1471 struct in_addr
*src_addr
;
1473 src_addr
= sources
+ i
;
1475 /* lookup reported source (A) in known sources (B) */
1476 source
= igmp_find_source_by_addr(group
, *src_addr
);
1478 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1479 IGMP_SOURCE_DO_SEND(source
->source_flags
);
1480 ++num_sources_tosend
;
1484 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1485 if (num_sources_tosend
> 0) {
1486 source_query_send_by_flag(group
, num_sources_tosend
);
1490 void igmpv3_report_block(struct igmp_sock
*igmp
, struct in_addr from
,
1491 struct in_addr group_addr
,
1492 int num_sources
, struct in_addr
*sources
)
1494 struct interface
*ifp
= igmp
->interface
;
1495 struct igmp_group
*group
;
1497 on_trace(__PRETTY_FUNCTION__
,
1498 ifp
, from
, group_addr
, num_sources
, sources
);
1500 /* non-existant group is created as INCLUDE {empty} */
1501 group
= igmp_add_group_by_addr(igmp
, group_addr
);
1506 if (group
->group_filtermode_isexcl
) {
1508 block_excl(group
, num_sources
, sources
);
1512 block_incl(group
, num_sources
, sources
);
1516 void igmp_group_timer_lower_to_lmqt(struct igmp_group
*group
)
1518 struct igmp_sock
*igmp
;
1519 struct interface
*ifp
;
1520 struct pim_interface
*pim_ifp
;
1522 int lmqi_dsec
; /* Last Member Query Interval */
1523 int lmqc
; /* Last Member Query Count */
1524 int lmqt_msec
; /* Last Member Query Time */
1527 RFC 3376: 6.2.2. Definition of Group Timers
1529 The group timer is only used when a group is in EXCLUDE mode and
1530 it represents the time for the *filter-mode* of the group to
1531 expire and switch to INCLUDE mode.
1533 if (!group
->group_filtermode_isexcl
) {
1537 igmp
= group
->group_igmp_sock
;
1538 ifp
= igmp
->interface
;
1539 pim_ifp
= ifp
->info
;
1542 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1543 lmqc
= igmp
->querier_robustness_variable
;
1544 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1546 if (PIM_DEBUG_IGMP_TRACE
) {
1547 char group_str
[INET_ADDRSTRLEN
];
1548 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1549 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1550 __PRETTY_FUNCTION__
,
1552 lmqc
, lmqi_dsec
, lmqt_msec
);
1555 zassert(group
->group_filtermode_isexcl
);
1557 igmp_group_timer_on(group
, lmqt_msec
, ifname
);
1560 void igmp_source_timer_lower_to_lmqt(struct igmp_source
*source
)
1562 struct igmp_group
*group
;
1563 struct igmp_sock
*igmp
;
1564 struct interface
*ifp
;
1565 struct pim_interface
*pim_ifp
;
1567 int lmqi_dsec
; /* Last Member Query Interval */
1568 int lmqc
; /* Last Member Query Count */
1569 int lmqt_msec
; /* Last Member Query Time */
1571 group
= source
->source_group
;
1572 igmp
= group
->group_igmp_sock
;
1573 ifp
= igmp
->interface
;
1574 pim_ifp
= ifp
->info
;
1577 lmqi_dsec
= pim_ifp
->igmp_specific_query_max_response_time_dsec
;
1578 lmqc
= igmp
->querier_robustness_variable
;
1579 lmqt_msec
= PIM_IGMP_LMQT_MSEC(lmqi_dsec
, lmqc
); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1581 if (PIM_DEBUG_IGMP_TRACE
) {
1582 char group_str
[INET_ADDRSTRLEN
];
1583 char source_str
[INET_ADDRSTRLEN
];
1584 pim_inet4_dump("<group?>", group
->group_addr
, group_str
, sizeof(group_str
));
1585 pim_inet4_dump("<source?>", source
->source_addr
, source_str
, sizeof(source_str
));
1586 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1587 __PRETTY_FUNCTION__
,
1588 group_str
, source_str
, ifname
,
1589 lmqc
, lmqi_dsec
, lmqt_msec
);
1592 igmp_source_timer_on(group
, source
, lmqt_msec
);
1596 igmp_v3_send_query (struct igmp_group
*group
,
1602 struct in_addr dst_addr
,
1603 struct in_addr group_addr
,
1604 int query_max_response_time_dsec
,
1606 uint8_t querier_robustness_variable
,
1607 uint16_t querier_query_interval
)
1610 uint8_t max_resp_code
;
1613 struct sockaddr_in to
;
1617 zassert(num_sources
>= 0);
1619 msg_size
= IGMP_V3_SOURCES_OFFSET
+ (num_sources
<< 2);
1620 if (msg_size
> query_buf_size
) {
1621 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1622 __FILE__
, __PRETTY_FUNCTION__
,
1623 msg_size
, query_buf_size
);
1627 s_flag
= PIM_FORCE_BOOLEAN(s_flag
);
1628 zassert((s_flag
== 0) || (s_flag
== 1));
1630 max_resp_code
= igmp_msg_encode16to8(query_max_response_time_dsec
);
1631 qqic
= igmp_msg_encode16to8(querier_query_interval
);
1634 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1636 If non-zero, the QRV field contains the [Robustness Variable]
1637 value used by the querier, i.e., the sender of the Query. If the
1638 querier's [Robustness Variable] exceeds 7, the maximum value of
1639 the QRV field, the QRV is set to zero.
1641 if (querier_robustness_variable
> 7) {
1642 querier_robustness_variable
= 0;
1645 query_buf
[0] = PIM_IGMP_MEMBERSHIP_QUERY
;
1646 query_buf
[1] = max_resp_code
;
1647 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = 0; /* for computing checksum */
1648 memcpy(query_buf
+4, &group_addr
, sizeof(struct in_addr
));
1650 query_buf
[8] = (s_flag
<< 3) | querier_robustness_variable
;
1651 query_buf
[9] = qqic
;
1652 *(uint16_t *)(query_buf
+ IGMP_V3_NUMSOURCES_OFFSET
) = htons(num_sources
);
1654 checksum
= in_cksum(query_buf
, msg_size
);
1655 *(uint16_t *)(query_buf
+ IGMP_CHECKSUM_OFFSET
) = checksum
;
1657 if (PIM_DEBUG_IGMP_PACKETS
) {
1658 char dst_str
[INET_ADDRSTRLEN
];
1659 char group_str
[INET_ADDRSTRLEN
];
1660 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1661 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1662 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",
1663 dst_str
, ifname
, group_str
,
1664 num_sources
, msg_size
, s_flag
, querier_robustness_variable
,
1665 querier_query_interval
, qqic
);
1668 memset(&to
, 0, sizeof(to
));
1669 to
.sin_family
= AF_INET
;
1670 to
.sin_addr
= dst_addr
;
1673 sent
= sendto(fd
, query_buf
, msg_size
, MSG_DONTWAIT
,
1674 (struct sockaddr
*)&to
, tolen
);
1675 if (sent
!= (ssize_t
) msg_size
) {
1676 char dst_str
[INET_ADDRSTRLEN
];
1677 char group_str
[INET_ADDRSTRLEN
];
1678 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1679 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1681 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1682 dst_str
, ifname
, group_str
, msg_size
, errno
, safe_strerror(errno
));
1685 zlog_warn("Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1686 dst_str
, ifname
, group_str
, msg_size
, sent
);
1692 s_flag sanity test: s_flag must be set for general queries
1694 RFC 3376: 6.6.1. Timer Updates
1696 When a router sends or receives a query with a clear Suppress
1697 Router-Side Processing flag, it must update its timers to reflect
1698 the correct timeout values for the group or sources being queried.
1700 General queries don't trigger timer update.
1703 /* general query? */
1704 if (PIM_INADDR_IS_ANY(group_addr
)) {
1705 char dst_str
[INET_ADDRSTRLEN
];
1706 char group_str
[INET_ADDRSTRLEN
];
1707 pim_inet4_dump("<dst?>", dst_addr
, dst_str
, sizeof(dst_str
));
1708 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1709 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1710 __PRETTY_FUNCTION__
,
1711 dst_str
, ifname
, group_str
, num_sources
);
1717 igmp_v3_recv_query (struct igmp_sock
*igmp
, const char *from_str
, char *igmp_msg
)
1719 struct interface
*ifp
;
1720 struct pim_interface
*pim_ifp
;
1721 struct in_addr group_addr
;
1722 uint8_t resv_s_qrv
= 0;
1727 memcpy(&group_addr
, igmp_msg
+ 4, sizeof(struct in_addr
));
1728 ifp
= igmp
->interface
;
1729 pim_ifp
= ifp
->info
;
1732 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1734 * Routers adopt the QRV value from the most recently received Query
1735 * as their own [Robustness Variable] value, unless that most
1736 * recently received QRV was zero, in which case the receivers use
1737 * the default [Robustness Variable] value specified in section 8.1
1738 * or a statically configured value.
1740 resv_s_qrv
= igmp_msg
[8];
1741 qrv
= 7 & resv_s_qrv
;
1742 igmp
->querier_robustness_variable
= qrv
? qrv
: pim_ifp
->igmp_default_robustness_variable
;
1745 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1747 * Multicast routers that are not the current querier adopt the QQI
1748 * value from the most recently received Query as their own [Query
1749 * Interval] value, unless that most recently received QQI was zero,
1750 * in which case the receiving routers use the default.
1752 if (igmp
->t_other_querier_timer
) {
1753 /* other querier present */
1757 qqi
= igmp_msg_decode8to16(qqic
);
1758 igmp
->querier_query_interval
= qqi
? qqi
: pim_ifp
->igmp_default_query_interval
;
1760 if (PIM_DEBUG_IGMP_TRACE
) {
1761 char ifaddr_str
[INET_ADDRSTRLEN
];
1762 pim_inet4_dump("<ifaddr?>", igmp
->ifaddr
, ifaddr_str
, sizeof(ifaddr_str
));
1763 zlog_debug("Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1765 qqi
? "recv-non-default" : "default",
1766 igmp
->querier_query_interval
,
1773 * RFC 3376: 6.6.1. Timer Updates
1775 * When a router sends or receives a query with a clear Suppress
1776 * Router-Side Processing flag, it must update its timers to reflect
1777 * the correct timeout values for the group or sources being queried.
1779 * General queries don't trigger timer update.
1781 s_flag
= (1 << 3) & resv_s_qrv
;
1784 /* s_flag is clear */
1786 if (PIM_INADDR_IS_ANY(group_addr
)) {
1787 /* this is a general query */
1788 /* log that general query should have the s_flag set */
1789 zlog_warn("General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1790 from_str
, ifp
->name
);
1792 struct igmp_group
*group
;
1794 /* this is a non-general query: perform timer updates */
1796 group
= find_group_by_addr(igmp
, group_addr
);
1798 int recv_num_sources
= ntohs(*(uint16_t *)(igmp_msg
+ IGMP_V3_NUMSOURCES_OFFSET
));
1801 * RFC 3376: 6.6.1. Timer Updates
1802 * Query Q(G,A): Source Timer for sources in A are lowered to LMQT
1803 * Query Q(G): Group Timer is lowered to LMQT
1805 if (recv_num_sources
< 1) {
1806 /* Query Q(G): Group Timer is lowered to LMQT */
1808 igmp_group_timer_lower_to_lmqt(group
);
1810 /* Query Q(G,A): Source Timer for sources in A are lowered to LMQT */
1812 /* Scan sources in query and lower their timers to LMQT */
1813 struct in_addr
*sources
= (struct in_addr
*)(igmp_msg
+ IGMP_V3_SOURCES_OFFSET
);
1814 for (i
= 0; i
< recv_num_sources
; ++i
) {
1815 struct in_addr src_addr
;
1816 struct igmp_source
*src
;
1817 memcpy(&src_addr
, sources
+ i
, sizeof(struct in_addr
));
1818 src
= igmp_find_source_by_addr(group
, src_addr
);
1820 igmp_source_timer_lower_to_lmqt(src
);
1825 char group_str
[INET_ADDRSTRLEN
];
1826 pim_inet4_dump("<group?>", group_addr
, group_str
, sizeof(group_str
));
1827 zlog_warn("IGMP query v3 from %s on %s: could not find group %s for timer update",
1828 from_str
, ifp
->name
, group_str
);
1831 } /* s_flag is clear: timer updates */
1835 igmp_v3_recv_report (struct igmp_sock
*igmp
,
1836 struct in_addr from
, const char *from_str
,
1837 char *igmp_msg
, int igmp_msg_len
)
1839 uint16_t recv_checksum
;
1842 uint8_t *group_record
;
1843 uint8_t *report_pastend
= (uint8_t *) igmp_msg
+ igmp_msg_len
;
1844 struct interface
*ifp
= igmp
->interface
;
1848 if (igmp_msg_len
< IGMP_V3_MSG_MIN_SIZE
) {
1849 zlog_warn("Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1850 from_str
, ifp
->name
, igmp_msg_len
, IGMP_V3_MSG_MIN_SIZE
);
1854 recv_checksum
= *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
);
1856 /* for computing checksum */
1857 *(uint16_t *) (igmp_msg
+ IGMP_CHECKSUM_OFFSET
) = 0;
1859 checksum
= in_cksum(igmp_msg
, igmp_msg_len
);
1860 if (checksum
!= recv_checksum
) {
1861 zlog_warn("Recv IGMP report v3 from %s on %s: checksum mismatch: received=%x computed=%x",
1862 from_str
, ifp
->name
, recv_checksum
, checksum
);
1866 num_groups
= ntohs(*(uint16_t *) (igmp_msg
+ IGMP_V3_REPORT_NUMGROUPS_OFFSET
));
1867 if (num_groups
< 1) {
1868 zlog_warn("Recv IGMP report v3 from %s on %s: missing group records",
1869 from_str
, ifp
->name
);
1873 if (PIM_DEBUG_IGMP_PACKETS
) {
1874 zlog_debug("Recv IGMP report v3 from %s on %s: size=%d checksum=%x groups=%d",
1875 from_str
, ifp
->name
, igmp_msg_len
, checksum
, num_groups
);
1878 group_record
= (uint8_t *) igmp_msg
+ IGMP_V3_REPORT_GROUPPRECORD_OFFSET
;
1881 for (i
= 0; i
< num_groups
; ++i
) {
1882 struct in_addr rec_group
;
1887 int rec_num_sources
;
1892 if ((group_record
+ IGMP_V3_GROUP_RECORD_MIN_SIZE
) > report_pastend
) {
1893 zlog_warn("Recv IGMP report v3 from %s on %s: group record beyond report end",
1894 from_str
, ifp
->name
);
1898 rec_type
= group_record
[IGMP_V3_GROUP_RECORD_TYPE_OFFSET
];
1899 rec_auxdatalen
= group_record
[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET
];
1900 rec_num_sources
= ntohs(* (uint16_t *) (group_record
+ IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET
));
1902 memcpy(&rec_group
, group_record
+ IGMP_V3_GROUP_RECORD_GROUP_OFFSET
, sizeof(struct in_addr
));
1904 if (PIM_DEBUG_IGMP_PACKETS
) {
1905 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%s",
1906 from_str
, ifp
->name
, i
, rec_type
, rec_auxdatalen
, rec_num_sources
, inet_ntoa(rec_group
));
1911 sources
= group_record
+ IGMP_V3_GROUP_RECORD_SOURCE_OFFSET
;
1913 for (j
= 0, src
= sources
; j
< rec_num_sources
; ++j
, src
+= 4) {
1915 if ((src
+ 4) > report_pastend
) {
1916 zlog_warn("Recv IGMP report v3 from %s on %s: group source beyond report end",
1917 from_str
, ifp
->name
);
1921 if (PIM_DEBUG_IGMP_PACKETS
) {
1924 if (!inet_ntop(AF_INET
, src
, src_str
, sizeof(src_str
)))
1925 sprintf(src_str
, "<source?>");
1927 zlog_debug("Recv IGMP report v3 from %s on %s: record=%d group=%s source=%s",
1928 from_str
, ifp
->name
, i
, inet_ntoa(rec_group
), src_str
);
1930 } /* for (sources) */
1933 lncb
.family
= AF_INET
;
1934 lncb
.u
.prefix4
.s_addr
= 0x000000E0;
1935 lncb
.prefixlen
= 24;
1938 g
.u
.prefix4
= rec_group
;
1941 * If we receive a igmp report with the group in 224.0.0.0/24
1942 * then we should ignore it
1944 if (prefix_match(&lncb
, &g
))
1949 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE
:
1950 igmpv3_report_isin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1952 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE
:
1953 igmpv3_report_isex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
, 0);
1955 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE
:
1956 igmpv3_report_toin(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1958 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE
:
1959 igmpv3_report_toex(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1961 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES
:
1962 igmpv3_report_allow(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1964 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES
:
1965 igmpv3_report_block(igmp
, from
, rec_group
, rec_num_sources
, (struct in_addr
*) sources
);
1968 zlog_warn("Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
1969 from_str
, ifp
->name
, rec_type
);
1972 group_record
+= 8 + (rec_num_sources
<< 2) + (rec_auxdatalen
<< 2);
1975 } /* for (group records) */