]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmpv3.c
pimd: Add debug messages as to why a register packet is rejected.
[mirror_frr.git] / pimd / pim_igmpv3.c
CommitLineData
12e41d03
DL
1/*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4
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.
9
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.
14
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,
18 MA 02110-1301 USA
19
12e41d03
DL
20*/
21
22#include <zebra.h>
23#include "log.h"
24#include "memory.h"
744d91b3 25#include "if.h"
12e41d03
DL
26
27#include "pimd.h"
28#include "pim_iface.h"
29#include "pim_igmp.h"
30#include "pim_igmpv3.h"
31#include "pim_str.h"
32#include "pim_util.h"
33#include "pim_time.h"
34#include "pim_zebra.h"
35#include "pim_oil.h"
36
37static void group_retransmit_timer_on(struct igmp_group *group);
38static long igmp_group_timer_remain_msec(struct igmp_group *group);
39static long igmp_source_timer_remain_msec(struct igmp_source *source);
40static void group_query_send(struct igmp_group *group);
41static void source_query_send_by_flag(struct igmp_group *group,
42 int num_sources_tosend);
43
44static void on_trace(const char *label,
45 struct interface *ifp, struct in_addr from,
46 struct in_addr group_addr,
47 int num_sources, struct in_addr *sources)
48{
49 if (PIM_DEBUG_IGMP_TRACE) {
50 char from_str[100];
51 char group_str[100];
52
53 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
54 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
55
56 zlog_debug("%s: from %s on %s: group=%s sources=%d",
57 label, from_str, ifp->name, group_str, num_sources);
58 }
59}
60
61int igmp_group_compat_mode(const struct igmp_sock *igmp,
62 const struct igmp_group *group)
63{
64 struct pim_interface *pim_ifp;
65 int64_t now_dsec;
66 long older_host_present_interval_dsec;
67
68 zassert(igmp);
69 zassert(igmp->interface);
70 zassert(igmp->interface->info);
71
72 pim_ifp = igmp->interface->info;
73
74 /*
75 RFC 3376: 8.13. Older Host Present Interval
76
77 This value MUST be ((the Robustness Variable) times (the Query
78 Interval)) plus (one Query Response Interval).
79
80 older_host_present_interval_dsec = \
81 igmp->querier_robustness_variable * \
82 10 * igmp->querier_query_interval + \
83 pim_ifp->query_max_response_time_dsec;
84 */
85 older_host_present_interval_dsec =
86 PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
87 igmp->querier_query_interval,
88 pim_ifp->igmp_query_max_response_time_dsec);
89
90 now_dsec = pim_time_monotonic_dsec();
91 if (now_dsec < 1) {
92 /* broken timer logged by pim_time_monotonic_dsec() */
93 return 3;
94 }
95
96 if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
97 return 1; /* IGMPv1 */
98
99 if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
100 return 2; /* IGMPv2 */
101
102 return 3; /* IGMPv3 */
103}
104
105void igmp_group_reset_gmi(struct igmp_group *group)
106{
107 long group_membership_interval_msec;
108 struct pim_interface *pim_ifp;
109 struct igmp_sock *igmp;
110 struct interface *ifp;
111
112 igmp = group->group_igmp_sock;
113 ifp = igmp->interface;
114 pim_ifp = ifp->info;
115
116 /*
117 RFC 3376: 8.4. Group Membership Interval
118
119 The Group Membership Interval is the amount of time that must pass
120 before a multicast router decides there are no more members of a
121 group or a particular source on a network.
122
123 This value MUST be ((the Robustness Variable) times (the Query
124 Interval)) plus (one Query Response Interval).
125
126 group_membership_interval_msec = querier_robustness_variable *
127 (1000 * querier_query_interval) +
128 100 * query_response_interval_dsec;
129 */
130 group_membership_interval_msec =
131 PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
132 igmp->querier_query_interval,
133 pim_ifp->igmp_query_max_response_time_dsec);
134
135 if (PIM_DEBUG_IGMP_TRACE) {
136 char group_str[100];
137 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
138 zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
139 group_str,
140 group_membership_interval_msec / 1000,
141 group_membership_interval_msec % 1000,
142 ifp->name);
143 }
144
145 /*
146 RFC 3376: 6.2.2. Definition of Group Timers
147
148 The group timer is only used when a group is in EXCLUDE mode and
149 it represents the time for the *filter-mode* of the group to
150 expire and switch to INCLUDE mode.
151 */
152 zassert(group->group_filtermode_isexcl);
153
154 igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
155}
156
157static int igmp_source_timer(struct thread *t)
158{
159 struct igmp_source *source;
160 struct igmp_group *group;
161
162 zassert(t);
163 source = THREAD_ARG(t);
164 zassert(source);
165
166 group = source->source_group;
167
168 if (PIM_DEBUG_IGMP_TRACE) {
169 char group_str[100];
170 char source_str[100];
171 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
172 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
173 zlog_debug("%s: Source timer expired for group %s source %s on %s",
174 __PRETTY_FUNCTION__,
175 group_str, source_str,
176 group->group_igmp_sock->interface->name);
177 }
178
179 zassert(source->t_source_timer);
180 source->t_source_timer = 0;
181
182 /*
183 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
184
185 Group
186 Filter-Mode Source Timer Value Action
187 ----------- ------------------ ------
188 INCLUDE TIMER == 0 Suggest to stop forwarding
189 traffic from source and
190 remove source record. If
191 there are no more source
192 records for the group, delete
193 group record.
194
195 EXCLUDE TIMER == 0 Suggest to not forward
196 traffic from source
197 (DO NOT remove record)
198
199 Source timer switched from (T > 0) to (T == 0): disable forwarding.
200 */
201
202 zassert(!source->t_source_timer);
203
204 if (group->group_filtermode_isexcl) {
205 /* EXCLUDE mode */
206
207 igmp_source_forward_stop(source);
208 }
209 else {
210 /* INCLUDE mode */
211
212 /* igmp_source_delete() will stop forwarding source */
213 igmp_source_delete(source);
214
215 /*
216 If there are no more source records for the group, delete group
217 record.
218 */
219 if (!listcount(group->group_source_list)) {
220 igmp_group_delete_empty_include(group);
221 }
222 }
223
224 return 0;
225}
226
227static void source_timer_off(struct igmp_group *group,
228 struct igmp_source *source)
229{
230 if (!source->t_source_timer)
231 return;
232
233 if (PIM_DEBUG_IGMP_TRACE) {
234 char group_str[100];
235 char source_str[100];
236 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
237 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
238 zlog_debug("Cancelling TIMER event for group %s source %s on %s",
239 group_str, source_str,
240 group->group_igmp_sock->interface->name);
241 }
242
243 THREAD_OFF(source->t_source_timer);
244 zassert(!source->t_source_timer);
245}
246
247static void igmp_source_timer_on(struct igmp_group *group,
248 struct igmp_source *source,
249 long interval_msec)
250{
251 source_timer_off(group, source);
252
253 if (PIM_DEBUG_IGMP_EVENTS) {
254 char group_str[100];
255 char source_str[100];
256 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
257 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
258 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
259 interval_msec / 1000,
260 interval_msec % 1000,
261 group_str, source_str,
262 group->group_igmp_sock->interface->name);
263 }
264
265 THREAD_TIMER_MSEC_ON(master, source->t_source_timer,
266 igmp_source_timer,
267 source, interval_msec);
268 zassert(source->t_source_timer);
269
270 /*
271 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
272
273 Source timer switched from (T == 0) to (T > 0): enable forwarding.
274 */
275 igmp_source_forward_start(source);
276}
277
278void igmp_source_reset_gmi(struct igmp_sock *igmp,
279 struct igmp_group *group,
280 struct igmp_source *source)
281{
282 long group_membership_interval_msec;
283 struct pim_interface *pim_ifp;
284 struct interface *ifp;
285
286 ifp = igmp->interface;
287 pim_ifp = ifp->info;
288
289 group_membership_interval_msec =
290 PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
291 igmp->querier_query_interval,
292 pim_ifp->igmp_query_max_response_time_dsec);
293
294 if (PIM_DEBUG_IGMP_TRACE) {
295 char group_str[100];
296 char source_str[100];
297
298 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
299 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
300
301 zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
302 source_str,
303 group_membership_interval_msec / 1000,
304 group_membership_interval_msec % 1000,
305 group_str,
306 ifp->name);
307 }
308
309 igmp_source_timer_on(group, source,
310 group_membership_interval_msec);
311}
312
d2ccd60b 313static void source_mark_delete_flag(struct igmp_group *group)
12e41d03
DL
314{
315 struct listnode *src_node;
316 struct igmp_source *src;
317
d2ccd60b 318 for (ALL_LIST_ELEMENTS_RO (group->group_source_list, src_node, src)) {
12e41d03
DL
319 IGMP_SOURCE_DO_DELETE(src->source_flags);
320 }
321}
322
d2ccd60b 323static void source_mark_send_flag (struct igmp_group *group)
12e41d03
DL
324{
325 struct listnode *src_node;
326 struct igmp_source *src;
327
d2ccd60b 328 for (ALL_LIST_ELEMENTS_RO (group->group_source_list, src_node, src)) {
12e41d03
DL
329 IGMP_SOURCE_DO_SEND(src->source_flags);
330 }
331}
332
d2ccd60b 333static int source_mark_send_flag_by_timer (struct igmp_group *group)
12e41d03
DL
334{
335 struct listnode *src_node;
336 struct igmp_source *src;
337 int num_marked_sources = 0;
338
d2ccd60b 339 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
12e41d03
DL
340 /* Is source timer running? */
341 if (src->t_source_timer) {
342 IGMP_SOURCE_DO_SEND(src->source_flags);
343 ++num_marked_sources;
344 }
345 else {
346 IGMP_SOURCE_DONT_SEND(src->source_flags);
347 }
348 }
349
350 return num_marked_sources;
351}
352
353static void source_clear_send_flag(struct list *source_list)
354{
355 struct listnode *src_node;
356 struct igmp_source *src;
357
358 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
359 IGMP_SOURCE_DONT_SEND(src->source_flags);
360 }
361}
362
363/*
364 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
365*/
366static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
367{
368 zassert(group->group_filtermode_isexcl);
369
370 if (listcount(group->group_source_list) < 1) {
371 igmp_anysource_forward_start(group);
372 }
373}
374
375void igmp_source_free(struct igmp_source *source)
376{
377 /* make sure there is no source timer running */
378 zassert(!source->t_source_timer);
379
380 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
381}
382
383static void source_channel_oil_detach(struct igmp_source *source)
384{
385 if (source->source_channel_oil) {
386 pim_channel_oil_del(source->source_channel_oil);
387 source->source_channel_oil = 0;
388 }
389}
390
391/*
392 igmp_source_delete: stop fowarding, and delete the source
393 igmp_source_forward_stop: stop fowarding, but keep the source
394*/
395void igmp_source_delete(struct igmp_source *source)
396{
397 struct igmp_group *group;
398
399 group = source->source_group;
400
401 if (PIM_DEBUG_IGMP_TRACE) {
402 char group_str[100];
403 char source_str[100];
404 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
405 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
406 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
407 source_str, group_str,
408 group->group_igmp_sock->fd,
409 group->group_igmp_sock->interface->name);
410 }
411
412 source_timer_off(group, source);
413 igmp_source_forward_stop(source);
414
415 /* sanity check that forwarding has been disabled */
416 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
417 char group_str[100];
418 char source_str[100];
419 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
420 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
421 zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
422 __PRETTY_FUNCTION__,
423 source_str, group_str,
424 group->group_igmp_sock->fd,
425 group->group_igmp_sock->interface->name);
426 /* warning only */
427 }
428
429 source_channel_oil_detach(source);
430
431 /*
432 notice that listnode_delete() can't be moved
433 into igmp_source_free() because the later is
434 called by list_delete_all_node()
435 */
436 listnode_delete(group->group_source_list, source);
437
438 igmp_source_free(source);
439
440 if (group->group_filtermode_isexcl) {
441 group_exclude_fwd_anysrc_ifempty(group);
442 }
443}
444
445static void source_delete_by_flag(struct list *source_list)
446{
447 struct listnode *src_node;
448 struct listnode *src_nextnode;
449 struct igmp_source *src;
450
451 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
452 if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
453 igmp_source_delete(src);
454}
455
456void igmp_source_delete_expired(struct list *source_list)
457{
458 struct listnode *src_node;
459 struct listnode *src_nextnode;
460 struct igmp_source *src;
461
462 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
463 if (!src->t_source_timer)
464 igmp_source_delete(src);
465}
466
467struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
468 struct in_addr src_addr)
469{
470 struct listnode *src_node;
471 struct igmp_source *src;
472
473 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
474 if (src_addr.s_addr == src->source_addr.s_addr)
475 return src;
476
477 return 0;
478}
479
9bd7302f
DS
480struct igmp_source *
481source_new (struct igmp_group *group,
482 struct in_addr src_addr)
12e41d03
DL
483{
484 struct igmp_source *src;
485
486 if (PIM_DEBUG_IGMP_TRACE) {
487 char group_str[100];
488 char source_str[100];
489 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
490 pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
491 zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
492 source_str, group_str,
493 group->group_igmp_sock->fd,
9bd7302f 494 group->group_igmp_sock->interface->name);
12e41d03
DL
495 }
496
36d9e7dc 497 src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
12e41d03 498 if (!src) {
36d9e7dc 499 zlog_warn("%s %s: XCALLOC() failure",
12e41d03
DL
500 __FILE__, __PRETTY_FUNCTION__);
501 return 0; /* error, not found, could not create */
502 }
503
9bd7302f 504 src->t_source_timer = NULL;
12e41d03
DL
505 src->source_group = group; /* back pointer */
506 src->source_addr = src_addr;
507 src->source_creation = pim_time_monotonic_sec();
508 src->source_flags = 0;
509 src->source_query_retransmit_count = 0;
9bd7302f 510 src->source_channel_oil = NULL;
12e41d03
DL
511
512 listnode_add(group->group_source_list, src);
513
514 zassert(!src->t_source_timer); /* source timer == 0 */
515
516 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
517 igmp_anysource_forward_stop(group);
518
519 return src;
520}
521
522static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
523 struct igmp_group *group,
9bd7302f 524 struct in_addr src_addr)
12e41d03
DL
525{
526 struct igmp_source *src;
527
528 src = igmp_find_source_by_addr(group, src_addr);
529 if (src) {
530 return src;
531 }
532
9bd7302f 533 src = source_new(group, src_addr);
12e41d03
DL
534 if (!src) {
535 return 0;
536 }
537
538 return src;
539}
540
541static void allow(struct igmp_sock *igmp, struct in_addr from,
542 struct in_addr group_addr,
543 int num_sources, struct in_addr *sources)
544{
60b1960f 545 struct igmp_source *source;
12e41d03
DL
546 struct igmp_group *group;
547 int i;
548
549 /* non-existant group is created as INCLUDE {empty} */
915c13b3 550 group = igmp_add_group_by_addr(igmp, group_addr);
12e41d03
DL
551 if (!group) {
552 return;
553 }
554
555 /* scan received sources */
556 for (i = 0; i < num_sources; ++i) {
12e41d03
DL
557 struct in_addr *src_addr;
558
559 src_addr = sources + i;
560
9bd7302f 561 source = add_source_by_addr(igmp, group, *src_addr);
12e41d03
DL
562 if (!source) {
563 continue;
564 }
565
566 /*
567 RFC 3376: 6.4.1. Reception of Current-State Records
568
569 When receiving IS_IN reports for groups in EXCLUDE mode is
570 sources should be moved from set with (timers = 0) to set with
571 (timers > 0).
572
573 igmp_source_reset_gmi() below, resetting the source timers to
574 GMI, accomplishes this.
575 */
576 igmp_source_reset_gmi(igmp, group, source);
577
578 } /* scan received sources */
60b1960f
DS
579
580 if ((num_sources == 0) &&
581 (group->group_filtermode_isexcl) &&
582 (listcount (group->group_source_list) == 1))
583 {
584 struct in_addr star = { .s_addr = INADDR_ANY };
585
586 source = igmp_find_source_by_addr (group, star);
587 if (source)
588 igmp_source_reset_gmi (igmp, group, source);
589 }
12e41d03
DL
590}
591
592void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
593 struct in_addr group_addr,
594 int num_sources, struct in_addr *sources)
595{
596 on_trace(__PRETTY_FUNCTION__,
597 igmp->interface, from, group_addr, num_sources, sources);
598
599 allow(igmp, from, group_addr, num_sources, sources);
600}
601
602static void isex_excl(struct igmp_group *group,
603 int num_sources, struct in_addr *sources)
604{
539fba1a 605 struct igmp_source *source;
12e41d03
DL
606 int i;
607
608 /* EXCLUDE mode */
609 zassert(group->group_filtermode_isexcl);
610
611 /* E.1: set deletion flag for known sources (X,Y) */
d2ccd60b 612 source_mark_delete_flag (group);
12e41d03
DL
613
614 /* scan received sources (A) */
615 for (i = 0; i < num_sources; ++i) {
12e41d03
DL
616 struct in_addr *src_addr;
617
618 src_addr = sources + i;
619
620 /* E.2: lookup reported source from (A) in (X,Y) */
621 source = igmp_find_source_by_addr(group, *src_addr);
622 if (source) {
623 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
624 IGMP_SOURCE_DONT_DELETE(source->source_flags);
625 }
626 else {
627 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
9bd7302f 628 source = source_new(group, *src_addr);
12e41d03
DL
629 if (!source) {
630 /* ugh, internal malloc failure, skip source */
631 continue;
632 }
633 zassert(!source->t_source_timer); /* timer == 0 */
634 igmp_source_reset_gmi(group->group_igmp_sock, group, source);
635 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
636 }
637
638 } /* scan received sources */
639
539fba1a
DS
640 /*
641 * If we are in isexcl mode and num_sources == 0
642 * than that means we have a *,g entry that
643 * needs to be handled
644 */
645 if (group->group_filtermode_isexcl && num_sources == 0)
646 {
647 struct in_addr star = { .s_addr = INADDR_ANY };
648 source = igmp_find_source_by_addr (group, star);
649 if (source)
0bc327f7
DS
650 {
651 IGMP_SOURCE_DONT_DELETE(source->source_flags);
652 igmp_source_reset_gmi (group->group_igmp_sock, group, source);
653 }
539fba1a
DS
654 }
655
12e41d03
DL
656 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
657 source_delete_by_flag(group->group_source_list);
658}
659
660static void isex_incl(struct igmp_group *group,
661 int num_sources, struct in_addr *sources)
662{
663 int i;
664
665 /* INCLUDE mode */
666 zassert(!group->group_filtermode_isexcl);
667
668 /* I.1: set deletion flag for known sources (A) */
d2ccd60b 669 source_mark_delete_flag (group);
12e41d03
DL
670
671 /* scan received sources (B) */
672 for (i = 0; i < num_sources; ++i) {
673 struct igmp_source *source;
674 struct in_addr *src_addr;
675
676 src_addr = sources + i;
677
678 /* I.2: lookup reported source (B) */
679 source = igmp_find_source_by_addr(group, *src_addr);
680 if (source) {
681 /* I.3: if found, clear deletion flag (A*B) */
682 IGMP_SOURCE_DONT_DELETE(source->source_flags);
683 }
684 else {
685 /* I.4: if not found, create source with timer=0 (B-A) */
9bd7302f 686 source = source_new(group, *src_addr);
12e41d03
DL
687 if (!source) {
688 /* ugh, internal malloc failure, skip source */
689 continue;
690 }
691 zassert(!source->t_source_timer); /* (B-A) timer=0 */
692 }
693
694 } /* scan received sources */
695
696 /* I.5: delete all sources marked with deletion flag (A-B) */
697 source_delete_by_flag(group->group_source_list);
698
699 group->group_filtermode_isexcl = 1; /* boolean=true */
700
701 zassert(group->group_filtermode_isexcl);
702
703 group_exclude_fwd_anysrc_ifempty(group);
704}
705
706void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
707 struct in_addr group_addr,
708 int num_sources, struct in_addr *sources)
709{
710 struct interface *ifp = igmp->interface;
711 struct igmp_group *group;
712
713 on_trace(__PRETTY_FUNCTION__,
714 ifp, from, group_addr, num_sources, sources);
715
716 /* non-existant group is created as INCLUDE {empty} */
915c13b3 717 group = igmp_add_group_by_addr(igmp, group_addr);
12e41d03
DL
718 if (!group) {
719 return;
720 }
721
722 if (group->group_filtermode_isexcl) {
723 /* EXCLUDE mode */
724 isex_excl(group, num_sources, sources);
725 }
726 else {
727 /* INCLUDE mode */
728 isex_incl(group, num_sources, sources);
729 zassert(group->group_filtermode_isexcl);
730 }
731
732 zassert(group->group_filtermode_isexcl);
733
734 igmp_group_reset_gmi(group);
735}
736
737static void toin_incl(struct igmp_group *group,
738 int num_sources, struct in_addr *sources)
739{
740 struct igmp_sock *igmp = group->group_igmp_sock;
741 int num_sources_tosend = listcount(group->group_source_list);
742 int i;
743
744 /* Set SEND flag for all known sources (A) */
d2ccd60b 745 source_mark_send_flag (group);
12e41d03
DL
746
747 /* Scan received sources (B) */
748 for (i = 0; i < num_sources; ++i) {
749 struct igmp_source *source;
750 struct in_addr *src_addr;
751
752 src_addr = sources + i;
753
754 /* Lookup reported source (B) */
755 source = igmp_find_source_by_addr(group, *src_addr);
756 if (source) {
757 /* If found, clear SEND flag (A*B) */
758 IGMP_SOURCE_DONT_SEND(source->source_flags);
759 --num_sources_tosend;
760 }
761 else {
762 /* If not found, create new source */
9bd7302f 763 source = source_new(group, *src_addr);
12e41d03
DL
764 if (!source) {
765 /* ugh, internal malloc failure, skip source */
766 continue;
767 }
768 }
769
770 /* (B)=GMI */
771 igmp_source_reset_gmi(igmp, group, source);
772 }
773
774 /* Send sources marked with SEND flag: Q(G,A-B) */
775 if (num_sources_tosend > 0) {
776 source_query_send_by_flag(group, num_sources_tosend);
777 }
778}
779
780static void toin_excl(struct igmp_group *group,
781 int num_sources, struct in_addr *sources)
782{
783 struct igmp_sock *igmp = group->group_igmp_sock;
784 int num_sources_tosend;
785 int i;
786
787 /* Set SEND flag for X (sources with timer > 0) */
d2ccd60b 788 num_sources_tosend = source_mark_send_flag_by_timer (group);
12e41d03
DL
789
790 /* Scan received sources (A) */
791 for (i = 0; i < num_sources; ++i) {
792 struct igmp_source *source;
793 struct in_addr *src_addr;
794
795 src_addr = sources + i;
796
797 /* Lookup reported source (A) */
798 source = igmp_find_source_by_addr(group, *src_addr);
799 if (source) {
800 if (source->t_source_timer) {
801 /* If found and timer running, clear SEND flag (X*A) */
802 IGMP_SOURCE_DONT_SEND(source->source_flags);
803 --num_sources_tosend;
804 }
805 }
806 else {
807 /* If not found, create new source */
9bd7302f 808 source = source_new(group, *src_addr);
12e41d03
DL
809 if (!source) {
810 /* ugh, internal malloc failure, skip source */
811 continue;
812 }
813 }
814
815 /* (A)=GMI */
816 igmp_source_reset_gmi(igmp, group, source);
817 }
818
819 /* Send sources marked with SEND flag: Q(G,X-A) */
820 if (num_sources_tosend > 0) {
821 source_query_send_by_flag(group, num_sources_tosend);
822 }
823
824 /* Send Q(G) */
825 group_query_send(group);
826}
827
828void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
829 struct in_addr group_addr,
830 int num_sources, struct in_addr *sources)
831{
832 struct interface *ifp = igmp->interface;
833 struct igmp_group *group;
834
835 on_trace(__PRETTY_FUNCTION__,
836 ifp, from, group_addr, num_sources, sources);
837
5b1207f7
DS
838 /*
839 * If the requested filter mode is INCLUDE *and* the requested source
840 * list is empty, then the entry corresponding to the requested
841 * interface and multicast address is deleted if present. If no such
842 * entry is present, the request is ignored.
843 */
844 if (num_sources)
845 {
846 /* non-existant group is created as INCLUDE {empty} */
847 group = igmp_add_group_by_addr(igmp, group_addr);
848 if (!group) {
849 return;
850 }
851 }
852 else
853 {
854 group = find_group_by_addr (igmp, group_addr);
855 if (!group)
856 return;
857 }
12e41d03
DL
858
859 if (group->group_filtermode_isexcl) {
860 /* EXCLUDE mode */
861 toin_excl(group, num_sources, sources);
862 }
863 else {
864 /* INCLUDE mode */
865 toin_incl(group, num_sources, sources);
866 }
867}
868
869static void toex_incl(struct igmp_group *group,
870 int num_sources, struct in_addr *sources)
871{
872 int num_sources_tosend = 0;
873 int i;
874
875 zassert(!group->group_filtermode_isexcl);
876
877 /* Set DELETE flag for all known sources (A) */
d2ccd60b 878 source_mark_delete_flag (group);
12e41d03
DL
879
880 /* Clear off SEND flag from all known sources (A) */
881 source_clear_send_flag(group->group_source_list);
882
883 /* Scan received sources (B) */
884 for (i = 0; i < num_sources; ++i) {
885 struct igmp_source *source;
886 struct in_addr *src_addr;
887
888 src_addr = sources + i;
889
890 /* Lookup reported source (B) */
891 source = igmp_find_source_by_addr(group, *src_addr);
892 if (source) {
893 /* If found, clear deletion flag: (A*B) */
894 IGMP_SOURCE_DONT_DELETE(source->source_flags);
895 /* and set SEND flag (A*B) */
896 IGMP_SOURCE_DO_SEND(source->source_flags);
897 ++num_sources_tosend;
898 }
899 else {
900 /* If source not found, create source with timer=0: (B-A)=0 */
9bd7302f 901 source = source_new(group, *src_addr);
12e41d03
DL
902 if (!source) {
903 /* ugh, internal malloc failure, skip source */
904 continue;
905 }
906 zassert(!source->t_source_timer); /* (B-A) timer=0 */
907 }
908
909 } /* Scan received sources (B) */
910
911 group->group_filtermode_isexcl = 1; /* boolean=true */
912
913 /* Delete all sources marked with DELETE flag (A-B) */
914 source_delete_by_flag(group->group_source_list);
915
916 /* Send sources marked with SEND flag: Q(G,A*B) */
917 if (num_sources_tosend > 0) {
918 source_query_send_by_flag(group, num_sources_tosend);
919 }
920
921 zassert(group->group_filtermode_isexcl);
922
923 group_exclude_fwd_anysrc_ifempty(group);
924}
925
926static void toex_excl(struct igmp_group *group,
927 int num_sources, struct in_addr *sources)
928{
929 int num_sources_tosend = 0;
930 int i;
931
932 /* set DELETE flag for all known sources (X,Y) */
d2ccd60b 933 source_mark_delete_flag (group);
12e41d03
DL
934
935 /* clear off SEND flag from all known sources (X,Y) */
936 source_clear_send_flag(group->group_source_list);
937
a459fe85
DS
938 if (num_sources == 0)
939 {
940 struct igmp_source *source;
941 struct in_addr any = { .s_addr = INADDR_ANY };
942
943 source = igmp_find_source_by_addr (group, any);
944 if (source)
945 IGMP_SOURCE_DONT_DELETE(source->source_flags);
946 }
947
12e41d03
DL
948 /* scan received sources (A) */
949 for (i = 0; i < num_sources; ++i) {
950 struct igmp_source *source;
951 struct in_addr *src_addr;
952
953 src_addr = sources + i;
954
955 /* lookup reported source (A) in known sources (X,Y) */
956 source = igmp_find_source_by_addr(group, *src_addr);
957 if (source) {
958 /* if found, clear off DELETE flag from reported source (A) */
959 IGMP_SOURCE_DONT_DELETE(source->source_flags);
960 }
961 else {
962 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
963 long group_timer_msec;
9bd7302f 964 source = source_new(group, *src_addr);
12e41d03
DL
965 if (!source) {
966 /* ugh, internal malloc failure, skip source */
967 continue;
968 }
969
970 zassert(!source->t_source_timer); /* timer == 0 */
971 group_timer_msec = igmp_group_timer_remain_msec(group);
972 igmp_source_timer_on(group, source, group_timer_msec);
973 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
974
975 /* make sure source is created with DELETE flag unset */
976 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
977 }
978
979 /* make sure reported source has DELETE flag unset */
980 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
981
982 if (source->t_source_timer) {
983 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
984 IGMP_SOURCE_DO_SEND(source->source_flags);
985 ++num_sources_tosend;
986 }
987
988 } /* scan received sources (A) */
989
990 /*
991 delete all sources marked with DELETE flag:
992 Delete (X-A)
993 Delete (Y-A)
994 */
995 source_delete_by_flag(group->group_source_list);
996
997 /* send sources marked with SEND flag: Q(G,A-Y) */
998 if (num_sources_tosend > 0) {
999 source_query_send_by_flag(group, num_sources_tosend);
1000 }
1001}
1002
1003void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
1004 struct in_addr group_addr,
1005 int num_sources, struct in_addr *sources)
1006{
1007 struct interface *ifp = igmp->interface;
1008 struct igmp_group *group;
1009
1010 on_trace(__PRETTY_FUNCTION__,
1011 ifp, from, group_addr, num_sources, sources);
1012
1013 /* non-existant group is created as INCLUDE {empty} */
915c13b3 1014 group = igmp_add_group_by_addr(igmp, group_addr);
12e41d03
DL
1015 if (!group) {
1016 return;
1017 }
1018
1019 if (group->group_filtermode_isexcl) {
1020 /* EXCLUDE mode */
1021 toex_excl(group, num_sources, sources);
1022 }
1023 else {
1024 /* INCLUDE mode */
1025 toex_incl(group, num_sources, sources);
1026 zassert(group->group_filtermode_isexcl);
1027 }
1028 zassert(group->group_filtermode_isexcl);
1029
1030 /* Group Timer=GMI */
1031 igmp_group_reset_gmi(group);
1032}
1033
1034void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
1035 struct in_addr group_addr,
1036 int num_sources, struct in_addr *sources)
1037{
1038 on_trace(__PRETTY_FUNCTION__,
1039 igmp->interface, from, group_addr, num_sources, sources);
1040
1041 allow(igmp, from, group_addr, num_sources, sources);
1042}
1043
1044/*
1045 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1046
1047 When transmitting a group specific query, if the group timer is
1048 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1049 in the query message.
1050*/
1051static void group_retransmit_group(struct igmp_group *group)
1052{
1053 char query_buf[PIM_IGMP_BUFSIZE_WRITE];
1054 struct igmp_sock *igmp;
1055 struct pim_interface *pim_ifp;
1056 long lmqc; /* Last Member Query Count */
1057 long lmqi_msec; /* Last Member Query Interval */
1058 long lmqt_msec; /* Last Member Query Time */
1059 int s_flag;
1060
1061 igmp = group->group_igmp_sock;
1062 pim_ifp = igmp->interface->info;
1063
1064 lmqc = igmp->querier_robustness_variable;
1065 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1066 lmqt_msec = lmqc * lmqi_msec;
1067
1068 /*
1069 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1070
1071 When transmitting a group specific query, if the group timer is
1072 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1073 in the query message.
1074 */
1075 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1076
1077 if (PIM_DEBUG_IGMP_TRACE) {
1078 char group_str[100];
1079 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1080 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1081 group_str, igmp->interface->name, s_flag,
1082 group->group_specific_query_retransmit_count);
1083 }
1084
1085 /*
1086 RFC3376: 4.1.12. IP Destination Addresses for Queries
1087
1088 Group-Specific and Group-and-Source-Specific Queries are sent with
1089 an IP destination address equal to the multicast address of
1090 interest.
1091 */
1092
1093 pim_igmp_send_membership_query(group,
1094 igmp->fd,
1095 igmp->interface->name,
1096 query_buf,
1097 sizeof(query_buf),
1098 0 /* num_sources_tosend */,
1099 group->group_addr /* dst_addr */,
1100 group->group_addr /* group_addr */,
1101 pim_ifp->igmp_specific_query_max_response_time_dsec,
1102 s_flag,
1103 igmp->querier_robustness_variable,
1104 igmp->querier_query_interval);
1105}
1106
1107/*
1108 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1109
1110 When building a group and source specific query for a group G, two
1111 separate query messages are sent for the group. The first one has
1112 the "Suppress Router-Side Processing" bit set and contains all the
1113 sources with retransmission state and timers greater than LMQT. The
1114 second has the "Suppress Router-Side Processing" bit clear and
1115 contains all the sources with retransmission state and timers lower
1116 or equal to LMQT. If either of the two calculated messages does not
1117 contain any sources, then its transmission is suppressed.
1118 */
1119static int group_retransmit_sources(struct igmp_group *group,
1120 int send_with_sflag_set)
1121{
1122 struct igmp_sock *igmp;
1123 struct pim_interface *pim_ifp;
1124 long lmqc; /* Last Member Query Count */
1125 long lmqi_msec; /* Last Member Query Interval */
1126 long lmqt_msec; /* Last Member Query Time */
1127 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1128 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1129 int query_buf1_max_sources;
1130 int query_buf2_max_sources;
1131 struct in_addr *source_addr1;
1132 struct in_addr *source_addr2;
1133 int num_sources_tosend1;
1134 int num_sources_tosend2;
1135 struct listnode *src_node;
1136 struct igmp_source *src;
1137 int num_retransmit_sources_left = 0;
1138
12e41d03
DL
1139 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1140 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1141
1142 igmp = group->group_igmp_sock;
1143 pim_ifp = igmp->interface->info;
1144
1145 lmqc = igmp->querier_robustness_variable;
1146 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1147 lmqt_msec = lmqc * lmqi_msec;
1148
1149 /* Scan all group sources */
1150 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1151
1152 /* Source has retransmission state? */
1153 if (src->source_query_retransmit_count < 1)
1154 continue;
1155
1156 if (--src->source_query_retransmit_count > 0) {
1157 ++num_retransmit_sources_left;
1158 }
1159
1160 /* Copy source address into appropriate query buffer */
1161 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1162 *source_addr1 = src->source_addr;
1163 ++source_addr1;
1164 }
1165 else {
1166 *source_addr2 = src->source_addr;
1167 ++source_addr2;
1168 }
1169
1170 }
1171
1172 num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1173 num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1174
1175 if (PIM_DEBUG_IGMP_TRACE) {
1176 char group_str[100];
1177 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1178 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",
1179 group_str, igmp->interface->name,
1180 num_sources_tosend1,
1181 num_sources_tosend2,
1182 send_with_sflag_set,
1183 num_retransmit_sources_left);
1184 }
1185
1186 if (num_sources_tosend1 > 0) {
1187 /*
1188 Send group-and-source-specific query with s_flag set and all
1189 sources with timers greater than LMQT.
1190 */
1191
1192 if (send_with_sflag_set) {
1193
1194 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1195 if (num_sources_tosend1 > query_buf1_max_sources) {
1196 char group_str[100];
1197 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1198 zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1199 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1200 num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
1201 }
1202 else {
1203 /*
1204 RFC3376: 4.1.12. IP Destination Addresses for Queries
1205
1206 Group-Specific and Group-and-Source-Specific Queries are sent with
1207 an IP destination address equal to the multicast address of
1208 interest.
1209 */
1210
1211 pim_igmp_send_membership_query(group,
1212 igmp->fd,
1213 igmp->interface->name,
1214 query_buf1,
1215 sizeof(query_buf1),
1216 num_sources_tosend1,
1217 group->group_addr,
1218 group->group_addr,
1219 pim_ifp->igmp_specific_query_max_response_time_dsec,
1220 1 /* s_flag */,
1221 igmp->querier_robustness_variable,
1222 igmp->querier_query_interval);
1223
1224 }
1225
1226 } /* send_with_sflag_set */
1227
1228 }
1229
1230 if (num_sources_tosend2 > 0) {
1231 /*
1232 Send group-and-source-specific query with s_flag clear and all
1233 sources with timers lower or equal to LMQT.
1234 */
1235
1236 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1237 if (num_sources_tosend2 > query_buf2_max_sources) {
1238 char group_str[100];
1239 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1240 zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1241 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1242 num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
1243 }
1244 else {
1245 /*
1246 RFC3376: 4.1.12. IP Destination Addresses for Queries
1247
1248 Group-Specific and Group-and-Source-Specific Queries are sent with
1249 an IP destination address equal to the multicast address of
1250 interest.
1251 */
1252
1253 pim_igmp_send_membership_query(group,
1254 igmp->fd,
1255 igmp->interface->name,
1256 query_buf2,
1257 sizeof(query_buf2),
1258 num_sources_tosend2,
1259 group->group_addr,
1260 group->group_addr,
1261 pim_ifp->igmp_specific_query_max_response_time_dsec,
1262 0 /* s_flag */,
1263 igmp->querier_robustness_variable,
1264 igmp->querier_query_interval);
1265
1266 }
1267 }
1268
1269 return num_retransmit_sources_left;
1270}
1271
1272static int igmp_group_retransmit(struct thread *t)
1273{
1274 struct igmp_group *group;
1275 int num_retransmit_sources_left;
1276 int send_with_sflag_set; /* boolean */
1277
1278 zassert(t);
1279 group = THREAD_ARG(t);
1280 zassert(group);
1281
1282 if (PIM_DEBUG_IGMP_TRACE) {
1283 char group_str[100];
1284 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1285 zlog_debug("group_retransmit_timer: group %s on %s",
1286 group_str, group->group_igmp_sock->interface->name);
1287 }
1288
1289 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1290 if (group->group_specific_query_retransmit_count > 0) {
1291
1292 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1293 group_retransmit_group(group);
1294 --group->group_specific_query_retransmit_count;
1295
1296 /*
1297 RFC3376: 6.6.3.2
1298 If a group specific query is scheduled to be transmitted at the
1299 same time as a group and source specific query for the same group,
1300 then transmission of the group and source specific message with the
1301 "Suppress Router-Side Processing" bit set may be suppressed.
1302 */
1303 send_with_sflag_set = 0; /* boolean=false */
1304 }
1305 else {
1306 send_with_sflag_set = 1; /* boolean=true */
1307 }
1308
1309 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1310 num_retransmit_sources_left = group_retransmit_sources(group,
1311 send_with_sflag_set);
1312
1313 group->t_group_query_retransmit_timer = 0;
1314
1315 /*
1316 Keep group retransmit timer running if there is any retransmit
1317 counter pending
1318 */
1319 if ((num_retransmit_sources_left > 0) ||
1320 (group->group_specific_query_retransmit_count > 0)) {
1321 group_retransmit_timer_on(group);
1322 }
1323
1324 return 0;
1325}
1326
1327/*
1328 group_retransmit_timer_on:
1329 if group retransmit timer isn't running, starts it;
1330 otherwise, do nothing
1331*/
1332static void group_retransmit_timer_on(struct igmp_group *group)
1333{
1334 struct igmp_sock *igmp;
1335 struct pim_interface *pim_ifp;
1336 long lmqi_msec; /* Last Member Query Interval */
1337
1338 /* if group retransmit timer is running, do nothing */
1339 if (group->t_group_query_retransmit_timer) {
1340 return;
1341 }
1342
1343 igmp = group->group_igmp_sock;
1344 pim_ifp = igmp->interface->info;
1345
1346 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1347
1348 if (PIM_DEBUG_IGMP_TRACE) {
1349 char group_str[100];
1350 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1351 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1352 lmqi_msec / 1000,
1353 lmqi_msec % 1000,
1354 group_str,
1355 igmp->interface->name);
1356 }
1357
1358 THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
1359 igmp_group_retransmit,
1360 group, lmqi_msec);
1361}
1362
1363static long igmp_group_timer_remain_msec(struct igmp_group *group)
1364{
1365 return pim_time_timer_remain_msec(group->t_group_timer);
1366}
1367
1368static long igmp_source_timer_remain_msec(struct igmp_source *source)
1369{
1370 return pim_time_timer_remain_msec(source->t_source_timer);
1371}
1372
1373/*
1374 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1375*/
1376static void group_query_send(struct igmp_group *group)
1377{
1378 long lmqc; /* Last Member Query Count */
1379
1380 lmqc = group->group_igmp_sock->querier_robustness_variable;
1381
1382 /* lower group timer to lmqt */
1383 igmp_group_timer_lower_to_lmqt(group);
1384
1385 /* reset retransmission counter */
1386 group->group_specific_query_retransmit_count = lmqc;
1387
1388 /* immediately send group specific query (decrease retransmit counter by 1)*/
1389 group_retransmit_group(group);
1390
1391 /* make sure group retransmit timer is running */
1392 group_retransmit_timer_on(group);
1393}
1394
1395/*
1396 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1397*/
1398static void source_query_send_by_flag(struct igmp_group *group,
1399 int num_sources_tosend)
1400{
1401 struct igmp_sock *igmp;
1402 struct pim_interface *pim_ifp;
1403 struct listnode *src_node;
1404 struct igmp_source *src;
1405 long lmqc; /* Last Member Query Count */
1406 long lmqi_msec; /* Last Member Query Interval */
1407 long lmqt_msec; /* Last Member Query Time */
1408
1409 zassert(num_sources_tosend > 0);
1410
1411 igmp = group->group_igmp_sock;
1412 pim_ifp = igmp->interface->info;
1413
1414 lmqc = igmp->querier_robustness_variable;
1415 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
1416 lmqt_msec = lmqc * lmqi_msec;
1417
1418 /*
1419 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1420
1421 (...) for each of the sources in X of group G, with source timer larger
1422 than LMQT:
1423 o Set number of retransmissions for each source to [Last Member
1424 Query Count].
1425 o Lower source timer to LMQT.
1426 */
1427 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1428 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1429 /* source "src" in X of group G */
1430 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1431 src->source_query_retransmit_count = lmqc;
1432 igmp_source_timer_lower_to_lmqt(src);
1433 }
1434 }
1435 }
1436
1437 /* send group-and-source specific queries */
1438 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1439
1440 /* make sure group retransmit timer is running */
1441 group_retransmit_timer_on(group);
1442}
1443
1444static void block_excl(struct igmp_group *group,
1445 int num_sources, struct in_addr *sources)
1446{
1447 int num_sources_tosend = 0;
1448 int i;
1449
1450 /* 1. clear off SEND flag from all known sources (X,Y) */
1451 source_clear_send_flag(group->group_source_list);
1452
1453 /* 2. scan received sources (A) */
1454 for (i = 0; i < num_sources; ++i) {
1455 struct igmp_source *source;
1456 struct in_addr *src_addr;
1457
1458 src_addr = sources + i;
1459
1460 /* lookup reported source (A) in known sources (X,Y) */
1461 source = igmp_find_source_by_addr(group, *src_addr);
1462 if (!source) {
1463 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1464 long group_timer_msec;
9bd7302f 1465 source = source_new(group, *src_addr);
12e41d03
DL
1466 if (!source) {
1467 /* ugh, internal malloc failure, skip source */
1468 continue;
1469 }
1470
1471 zassert(!source->t_source_timer); /* timer == 0 */
1472 group_timer_msec = igmp_group_timer_remain_msec(group);
1473 igmp_source_timer_on(group, source, group_timer_msec);
1474 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1475 }
1476
1477 if (source->t_source_timer) {
1478 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1479 IGMP_SOURCE_DO_SEND(source->source_flags);
1480 ++num_sources_tosend;
1481 }
1482 }
1483
1484 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1485 if (num_sources_tosend > 0) {
1486 source_query_send_by_flag(group, num_sources_tosend);
1487 }
1488}
1489
1490static void block_incl(struct igmp_group *group,
1491 int num_sources, struct in_addr *sources)
1492{
1493 int num_sources_tosend = 0;
1494 int i;
1495
1496 /* 1. clear off SEND flag from all known sources (B) */
1497 source_clear_send_flag(group->group_source_list);
1498
1499 /* 2. scan received sources (A) */
1500 for (i = 0; i < num_sources; ++i) {
1501 struct igmp_source *source;
1502 struct in_addr *src_addr;
1503
1504 src_addr = sources + i;
1505
1506 /* lookup reported source (A) in known sources (B) */
1507 source = igmp_find_source_by_addr(group, *src_addr);
1508 if (source) {
1509 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1510 IGMP_SOURCE_DO_SEND(source->source_flags);
1511 ++num_sources_tosend;
1512 }
1513 }
1514
1515 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1516 if (num_sources_tosend > 0) {
1517 source_query_send_by_flag(group, num_sources_tosend);
1518 }
1519}
1520
1521void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1522 struct in_addr group_addr,
1523 int num_sources, struct in_addr *sources)
1524{
1525 struct interface *ifp = igmp->interface;
1526 struct igmp_group *group;
1527
1528 on_trace(__PRETTY_FUNCTION__,
1529 ifp, from, group_addr, num_sources, sources);
1530
1531 /* non-existant group is created as INCLUDE {empty} */
915c13b3 1532 group = igmp_add_group_by_addr(igmp, group_addr);
12e41d03
DL
1533 if (!group) {
1534 return;
1535 }
1536
1537 if (group->group_filtermode_isexcl) {
1538 /* EXCLUDE mode */
1539 block_excl(group, num_sources, sources);
1540 }
1541 else {
1542 /* INCLUDE mode */
1543 block_incl(group, num_sources, sources);
1544 }
1545}
1546
1547void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1548{
1549 struct igmp_sock *igmp;
1550 struct interface *ifp;
1551 struct pim_interface *pim_ifp;
1552 char *ifname;
1553 int lmqi_dsec; /* Last Member Query Interval */
1554 int lmqc; /* Last Member Query Count */
1555 int lmqt_msec; /* Last Member Query Time */
1556
1557 /*
1558 RFC 3376: 6.2.2. Definition of Group Timers
1559
1560 The group timer is only used when a group is in EXCLUDE mode and
1561 it represents the time for the *filter-mode* of the group to
1562 expire and switch to INCLUDE mode.
1563 */
1564 if (!group->group_filtermode_isexcl) {
1565 return;
1566 }
1567
1568 igmp = group->group_igmp_sock;
1569 ifp = igmp->interface;
1570 pim_ifp = ifp->info;
1571 ifname = ifp->name;
1572
1573 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1574 lmqc = igmp->querier_robustness_variable;
1575 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1576
1577 if (PIM_DEBUG_IGMP_TRACE) {
1578 char group_str[100];
1579 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1580 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1581 __PRETTY_FUNCTION__,
1582 group_str, ifname,
1583 lmqc, lmqi_dsec, lmqt_msec);
1584 }
1585
1586 zassert(group->group_filtermode_isexcl);
1587
1588 igmp_group_timer_on(group, lmqt_msec, ifname);
1589}
1590
1591void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1592{
1593 struct igmp_group *group;
1594 struct igmp_sock *igmp;
1595 struct interface *ifp;
1596 struct pim_interface *pim_ifp;
1597 char *ifname;
1598 int lmqi_dsec; /* Last Member Query Interval */
1599 int lmqc; /* Last Member Query Count */
1600 int lmqt_msec; /* Last Member Query Time */
1601
1602 group = source->source_group;
1603 igmp = group->group_igmp_sock;
1604 ifp = igmp->interface;
1605 pim_ifp = ifp->info;
1606 ifname = ifp->name;
1607
1608 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
1609 lmqc = igmp->querier_robustness_variable;
1610 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1611
1612 if (PIM_DEBUG_IGMP_TRACE) {
1613 char group_str[100];
1614 char source_str[100];
1615 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1616 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1617 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1618 __PRETTY_FUNCTION__,
1619 group_str, source_str, ifname,
1620 lmqc, lmqi_dsec, lmqt_msec);
1621 }
1622
1623 igmp_source_timer_on(group, source, lmqt_msec);
1624}
1625
1626/*
1627 Copy sources to message:
1628
1629 struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
1630 if (num_sources > 0) {
1631 struct listnode *node;
1632 struct igmp_source *src;
1633 int i = 0;
1634
1635 for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
1636 sources[i++] = src->source_addr;
1637 }
1638 }
1639*/
1640void pim_igmp_send_membership_query(struct igmp_group *group,
1641 int fd,
1642 const char *ifname,
1643 char *query_buf,
1644 int query_buf_size,
1645 int num_sources,
1646 struct in_addr dst_addr,
1647 struct in_addr group_addr,
1648 int query_max_response_time_dsec,
1649 uint8_t s_flag,
1650 uint8_t querier_robustness_variable,
1651 uint16_t querier_query_interval)
1652{
1653 ssize_t msg_size;
1654 uint8_t max_resp_code;
1655 uint8_t qqic;
1656 ssize_t sent;
1657 struct sockaddr_in to;
1658 socklen_t tolen;
1659 uint16_t checksum;
1660
1661 zassert(num_sources >= 0);
1662
1663 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1664 if (msg_size > query_buf_size) {
1665 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1666 __FILE__, __PRETTY_FUNCTION__,
1667 msg_size, query_buf_size);
1668 return;
1669 }
1670
1671 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1672 zassert((s_flag == 0) || (s_flag == 1));
1673
1674 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1675 qqic = igmp_msg_encode16to8(querier_query_interval);
1676
1677 /*
1678 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1679
1680 If non-zero, the QRV field contains the [Robustness Variable]
1681 value used by the querier, i.e., the sender of the Query. If the
1682 querier's [Robustness Variable] exceeds 7, the maximum value of
1683 the QRV field, the QRV is set to zero.
1684 */
1685 if (querier_robustness_variable > 7) {
1686 querier_robustness_variable = 0;
1687 }
1688
1689 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1690 query_buf[1] = max_resp_code;
1691 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
1692 memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
1693
1694 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1695 query_buf[9] = qqic;
1696 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
1697
1698 checksum = in_cksum(query_buf, msg_size);
1699 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
1700
1701 if (PIM_DEBUG_IGMP_PACKETS) {
1702 char dst_str[100];
1703 char group_str[100];
1704 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1705 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1706 zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
1707 __PRETTY_FUNCTION__,
1708 dst_str, ifname, group_str, num_sources,
1709 msg_size, s_flag, querier_robustness_variable,
1710 querier_query_interval, qqic, checksum);
1711 }
1712
12e41d03 1713 memset(&to, 0, sizeof(to));
12e41d03
DL
1714 to.sin_family = AF_INET;
1715 to.sin_addr = dst_addr;
12e41d03
DL
1716 tolen = sizeof(to);
1717
59e96cda
DL
1718 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1719 (struct sockaddr *)&to, tolen);
12e41d03 1720 if (sent != (ssize_t) msg_size) {
12e41d03
DL
1721 char dst_str[100];
1722 char group_str[100];
1723 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1724 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1725 if (sent < 0) {
1726 zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1727 __PRETTY_FUNCTION__,
1728 dst_str, ifname, group_str, msg_size,
3d7765d7 1729 errno, safe_strerror(errno));
12e41d03
DL
1730 }
1731 else {
1732 zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
1733 __PRETTY_FUNCTION__,
1734 dst_str, ifname, group_str,
1735 msg_size, sent);
1736 }
1737 return;
1738 }
1739
1740 /*
1741 s_flag sanity test: s_flag must be set for general queries
1742
1743 RFC 3376: 6.6.1. Timer Updates
1744
1745 When a router sends or receives a query with a clear Suppress
1746 Router-Side Processing flag, it must update its timers to reflect
1747 the correct timeout values for the group or sources being queried.
1748
1749 General queries don't trigger timer update.
1750 */
1751 if (!s_flag) {
1752 /* general query? */
1753 if (PIM_INADDR_IS_ANY(group_addr)) {
1754 char dst_str[100];
1755 char group_str[100];
1756 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1757 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1758 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1759 __PRETTY_FUNCTION__,
1760 dst_str, ifname, group_str, num_sources);
1761 }
1762 }
1763
1764}