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