blob: 3d3ee7ac84f2694f27c504e5c2fac22a10702780 [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
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"
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
313static void source_mark_delete_flag(struct list *source_list)
314{
315 struct listnode *src_node;
316 struct igmp_source *src;
317
318 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
319 IGMP_SOURCE_DO_DELETE(src->source_flags);
320 }
321}
322
323static void source_mark_send_flag(struct list *source_list)
324{
325 struct listnode *src_node;
326 struct igmp_source *src;
327
328 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
329 IGMP_SOURCE_DO_SEND(src->source_flags);
330 }
331}
332
333static int source_mark_send_flag_by_timer(struct list *source_list)
334{
335 struct listnode *src_node;
336 struct igmp_source *src;
337 int num_marked_sources = 0;
338
339 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
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
391void igmp_source_delete(struct igmp_source *source)
392{
393 struct igmp_group *group;
394
395 group = source->source_group;
396
397 if (PIM_DEBUG_IGMP_TRACE) {
398 char group_str[100];
399 char source_str[100];
400 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
401 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
402 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
403 source_str, group_str,
404 group->group_igmp_sock->fd,
405 group->group_igmp_sock->interface->name);
406 }
407
408 source_timer_off(group, source);
409 igmp_source_forward_stop(source);
410
411 /* make sure forwarding is disabled */
412 zassert(!IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
413
414 source_channel_oil_detach(source);
415
416 /*
417 notice that listnode_delete() can't be moved
418 into igmp_source_free() because the later is
419 called by list_delete_all_node()
420 */
421 listnode_delete(group->group_source_list, source);
422
423 igmp_source_free(source);
424
425 if (group->group_filtermode_isexcl) {
426 group_exclude_fwd_anysrc_ifempty(group);
427 }
428}
429
430static void source_delete_by_flag(struct list *source_list)
431{
432 struct listnode *src_node;
433 struct listnode *src_nextnode;
434 struct igmp_source *src;
435
436 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
437 if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
438 igmp_source_delete(src);
439}
440
441void igmp_source_delete_expired(struct list *source_list)
442{
443 struct listnode *src_node;
444 struct listnode *src_nextnode;
445 struct igmp_source *src;
446
447 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
448 if (!src->t_source_timer)
449 igmp_source_delete(src);
450}
451
452struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
453 struct in_addr src_addr)
454{
455 struct listnode *src_node;
456 struct igmp_source *src;
457
458 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
459 if (src_addr.s_addr == src->source_addr.s_addr)
460 return src;
461
462 return 0;
463}
464
465static struct igmp_source *source_new(struct igmp_group *group,
466 struct in_addr src_addr,
467 const char *ifname)
468{
469 struct igmp_source *src;
470
471 if (PIM_DEBUG_IGMP_TRACE) {
472 char group_str[100];
473 char source_str[100];
474 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
475 pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
476 zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
477 source_str, group_str,
478 group->group_igmp_sock->fd,
479 ifname);
480 }
481
482 src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
483 if (!src) {
484 zlog_warn("%s %s: XMALLOC() failure",
485 __FILE__, __PRETTY_FUNCTION__);
486 return 0; /* error, not found, could not create */
487 }
488
489 src->t_source_timer = 0;
490 src->source_group = group; /* back pointer */
491 src->source_addr = src_addr;
492 src->source_creation = pim_time_monotonic_sec();
493 src->source_flags = 0;
494 src->source_query_retransmit_count = 0;
495 src->source_channel_oil = 0;
496
497 listnode_add(group->group_source_list, src);
498
499 zassert(!src->t_source_timer); /* source timer == 0 */
500
501 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
502 igmp_anysource_forward_stop(group);
503
504 return src;
505}
506
507static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
508 struct igmp_group *group,
509 struct in_addr src_addr,
510 const char *ifname)
511{
512 struct igmp_source *src;
513
514 src = igmp_find_source_by_addr(group, src_addr);
515 if (src) {
516 return src;
517 }
518
519 src = source_new(group, src_addr, ifname);
520 if (!src) {
521 return 0;
522 }
523
524 return src;
525}
526
527static void allow(struct igmp_sock *igmp, struct in_addr from,
528 struct in_addr group_addr,
529 int num_sources, struct in_addr *sources)
530{
531 struct interface *ifp = igmp->interface;
532 struct pim_interface *pim_ifp;
533 struct igmp_group *group;
534
535 pim_ifp = ifp->info;
536
537 /* non-existant group is created as INCLUDE {empty} */
538 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
539 if (!group) {
540 return;
541 }
542
543 /* scan received sources */
544 for (int i = 0; i < num_sources; ++i) {
545 struct igmp_source *source;
546 struct in_addr *src_addr;
547
548 src_addr = sources + i;
549
550 source = add_source_by_addr(igmp, group, *src_addr, ifp->name);
551 if (!source) {
552 continue;
553 }
554
555 /*
556 RFC 3376: 6.4.1. Reception of Current-State Records
557
558 When receiving IS_IN reports for groups in EXCLUDE mode is
559 sources should be moved from set with (timers = 0) to set with
560 (timers > 0).
561
562 igmp_source_reset_gmi() below, resetting the source timers to
563 GMI, accomplishes this.
564 */
565 igmp_source_reset_gmi(igmp, group, source);
566
567 } /* scan received sources */
568}
569
570void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
571 struct in_addr group_addr,
572 int num_sources, struct in_addr *sources)
573{
574 on_trace(__PRETTY_FUNCTION__,
575 igmp->interface, from, group_addr, num_sources, sources);
576
577 allow(igmp, from, group_addr, num_sources, sources);
578}
579
580static void isex_excl(struct igmp_group *group,
581 int num_sources, struct in_addr *sources)
582{
583 /* EXCLUDE mode */
584 zassert(group->group_filtermode_isexcl);
585
586 /* E.1: set deletion flag for known sources (X,Y) */
587 source_mark_delete_flag(group->group_source_list);
588
589 /* scan received sources (A) */
590 for (int i = 0; i < num_sources; ++i) {
591 struct igmp_source *source;
592 struct in_addr *src_addr;
593
594 src_addr = sources + i;
595
596 /* E.2: lookup reported source from (A) in (X,Y) */
597 source = igmp_find_source_by_addr(group, *src_addr);
598 if (source) {
599 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
600 IGMP_SOURCE_DONT_DELETE(source->source_flags);
601 }
602 else {
603 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
604 source = source_new(group, *src_addr,
605 group->group_igmp_sock->interface->name);
606 if (!source) {
607 /* ugh, internal malloc failure, skip source */
608 continue;
609 }
610 zassert(!source->t_source_timer); /* timer == 0 */
611 igmp_source_reset_gmi(group->group_igmp_sock, group, source);
612 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
613 }
614
615 } /* scan received sources */
616
617 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
618 source_delete_by_flag(group->group_source_list);
619}
620
621static void isex_incl(struct igmp_group *group,
622 int num_sources, struct in_addr *sources)
623{
624 /* INCLUDE mode */
625 zassert(!group->group_filtermode_isexcl);
626
627 /* I.1: set deletion flag for known sources (A) */
628 source_mark_delete_flag(group->group_source_list);
629
630 /* scan received sources (B) */
631 for (int i = 0; i < num_sources; ++i) {
632 struct igmp_source *source;
633 struct in_addr *src_addr;
634
635 src_addr = sources + i;
636
637 /* I.2: lookup reported source (B) */
638 source = igmp_find_source_by_addr(group, *src_addr);
639 if (source) {
640 /* I.3: if found, clear deletion flag (A*B) */
641 IGMP_SOURCE_DONT_DELETE(source->source_flags);
642 }
643 else {
644 /* I.4: if not found, create source with timer=0 (B-A) */
645 source = source_new(group, *src_addr,
646 group->group_igmp_sock->interface->name);
647 if (!source) {
648 /* ugh, internal malloc failure, skip source */
649 continue;
650 }
651 zassert(!source->t_source_timer); /* (B-A) timer=0 */
652 }
653
654 } /* scan received sources */
655
656 /* I.5: delete all sources marked with deletion flag (A-B) */
657 source_delete_by_flag(group->group_source_list);
658
659 group->group_filtermode_isexcl = 1; /* boolean=true */
660
661 zassert(group->group_filtermode_isexcl);
662
663 group_exclude_fwd_anysrc_ifempty(group);
664}
665
666void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
667 struct in_addr group_addr,
668 int num_sources, struct in_addr *sources)
669{
670 struct interface *ifp = igmp->interface;
671 struct pim_interface *pim_ifp;
672 struct igmp_group *group;
673
674 on_trace(__PRETTY_FUNCTION__,
675 ifp, from, group_addr, num_sources, sources);
676
677 pim_ifp = ifp->info;
678
679 /* non-existant group is created as INCLUDE {empty} */
680 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
681 if (!group) {
682 return;
683 }
684
685 if (group->group_filtermode_isexcl) {
686 /* EXCLUDE mode */
687 isex_excl(group, num_sources, sources);
688 }
689 else {
690 /* INCLUDE mode */
691 isex_incl(group, num_sources, sources);
692 zassert(group->group_filtermode_isexcl);
693 }
694
695 zassert(group->group_filtermode_isexcl);
696
697 igmp_group_reset_gmi(group);
698}
699
700static void toin_incl(struct igmp_group *group,
701 int num_sources, struct in_addr *sources)
702{
703 struct igmp_sock *igmp = group->group_igmp_sock;
704 int num_sources_tosend = listcount(group->group_source_list);
705
706 /* Set SEND flag for all known sources (A) */
707 source_mark_send_flag(group->group_source_list);
708
709 /* Scan received sources (B) */
710 for (int i = 0; i < num_sources; ++i) {
711 struct igmp_source *source;
712 struct in_addr *src_addr;
713
714 src_addr = sources + i;
715
716 /* Lookup reported source (B) */
717 source = igmp_find_source_by_addr(group, *src_addr);
718 if (source) {
719 /* If found, clear SEND flag (A*B) */
720 IGMP_SOURCE_DONT_SEND(source->source_flags);
721 --num_sources_tosend;
722 }
723 else {
724 /* If not found, create new source */
725 source = source_new(group, *src_addr,
726 group->group_igmp_sock->interface->name);
727 if (!source) {
728 /* ugh, internal malloc failure, skip source */
729 continue;
730 }
731 }
732
733 /* (B)=GMI */
734 igmp_source_reset_gmi(igmp, group, source);
735 }
736
737 /* Send sources marked with SEND flag: Q(G,A-B) */
738 if (num_sources_tosend > 0) {
739 source_query_send_by_flag(group, num_sources_tosend);
740 }
741}
742
743static void toin_excl(struct igmp_group *group,
744 int num_sources, struct in_addr *sources)
745{
746 struct igmp_sock *igmp = group->group_igmp_sock;
747 int num_sources_tosend;
748
749 /* Set SEND flag for X (sources with timer > 0) */
750 num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
751
752 /* Scan received sources (A) */
753 for (int i = 0; i < num_sources; ++i) {
754 struct igmp_source *source;
755 struct in_addr *src_addr;
756
757 src_addr = sources + i;
758
759 /* Lookup reported source (A) */
760 source = igmp_find_source_by_addr(group, *src_addr);
761 if (source) {
762 if (source->t_source_timer) {
763 /* If found and timer running, clear SEND flag (X*A) */
764 IGMP_SOURCE_DONT_SEND(source->source_flags);
765 --num_sources_tosend;
766 }
767 }
768 else {
769 /* If not found, create new source */
770 source = source_new(group, *src_addr,
771 group->group_igmp_sock->interface->name);
772 if (!source) {
773 /* ugh, internal malloc failure, skip source */
774 continue;
775 }
776 }
777
778 /* (A)=GMI */
779 igmp_source_reset_gmi(igmp, group, source);
780 }
781
782 /* Send sources marked with SEND flag: Q(G,X-A) */
783 if (num_sources_tosend > 0) {
784 source_query_send_by_flag(group, num_sources_tosend);
785 }
786
787 /* Send Q(G) */
788 group_query_send(group);
789}
790
791void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
792 struct in_addr group_addr,
793 int num_sources, struct in_addr *sources)
794{
795 struct interface *ifp = igmp->interface;
796 struct pim_interface *pim_ifp;
797 struct igmp_group *group;
798
799 on_trace(__PRETTY_FUNCTION__,
800 ifp, from, group_addr, num_sources, sources);
801
802 pim_ifp = ifp->info;
803
804 /* non-existant group is created as INCLUDE {empty} */
805 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
806 if (!group) {
807 return;
808 }
809
810 if (group->group_filtermode_isexcl) {
811 /* EXCLUDE mode */
812 toin_excl(group, num_sources, sources);
813 }
814 else {
815 /* INCLUDE mode */
816 toin_incl(group, num_sources, sources);
817 }
818}
819
820static void toex_incl(struct igmp_group *group,
821 int num_sources, struct in_addr *sources)
822{
823 int num_sources_tosend = 0;
824
825 zassert(!group->group_filtermode_isexcl);
826
827 /* Set DELETE flag for all known sources (A) */
828 source_mark_delete_flag(group->group_source_list);
829
830 /* Clear off SEND flag from all known sources (A) */
831 source_clear_send_flag(group->group_source_list);
832
833 /* Scan received sources (B) */
834 for (int i = 0; i < num_sources; ++i) {
835 struct igmp_source *source;
836 struct in_addr *src_addr;
837
838 src_addr = sources + i;
839
840 /* Lookup reported source (B) */
841 source = igmp_find_source_by_addr(group, *src_addr);
842 if (source) {
843 /* If found, clear deletion flag: (A*B) */
844 IGMP_SOURCE_DONT_DELETE(source->source_flags);
845 /* and set SEND flag (A*B) */
846 IGMP_SOURCE_DO_SEND(source->source_flags);
847 ++num_sources_tosend;
848 }
849 else {
850 /* If source not found, create source with timer=0: (B-A)=0 */
851 source = source_new(group, *src_addr,
852 group->group_igmp_sock->interface->name);
853 if (!source) {
854 /* ugh, internal malloc failure, skip source */
855 continue;
856 }
857 zassert(!source->t_source_timer); /* (B-A) timer=0 */
858 }
859
860 } /* Scan received sources (B) */
861
862 group->group_filtermode_isexcl = 1; /* boolean=true */
863
864 /* Delete all sources marked with DELETE flag (A-B) */
865 source_delete_by_flag(group->group_source_list);
866
867 /* Send sources marked with SEND flag: Q(G,A*B) */
868 if (num_sources_tosend > 0) {
869 source_query_send_by_flag(group, num_sources_tosend);
870 }
871
872 zassert(group->group_filtermode_isexcl);
873
874 group_exclude_fwd_anysrc_ifempty(group);
875}
876
877static void toex_excl(struct igmp_group *group,
878 int num_sources, struct in_addr *sources)
879{
880 int num_sources_tosend = 0;
881
882 /* set DELETE flag for all known sources (X,Y) */
883 source_mark_delete_flag(group->group_source_list);
884
885 /* clear off SEND flag from all known sources (X,Y) */
886 source_clear_send_flag(group->group_source_list);
887
888 /* scan received sources (A) */
889 for (int i = 0; i < num_sources; ++i) {
890 struct igmp_source *source;
891 struct in_addr *src_addr;
892
893 src_addr = sources + i;
894
895 /* lookup reported source (A) in known sources (X,Y) */
896 source = igmp_find_source_by_addr(group, *src_addr);
897 if (source) {
898 /* if found, clear off DELETE flag from reported source (A) */
899 IGMP_SOURCE_DONT_DELETE(source->source_flags);
900 }
901 else {
902 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
903 long group_timer_msec;
904 source = source_new(group, *src_addr,
905 group->group_igmp_sock->interface->name);
906 if (!source) {
907 /* ugh, internal malloc failure, skip source */
908 continue;
909 }
910
911 zassert(!source->t_source_timer); /* timer == 0 */
912 group_timer_msec = igmp_group_timer_remain_msec(group);
913 igmp_source_timer_on(group, source, group_timer_msec);
914 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
915
916 /* make sure source is created with DELETE flag unset */
917 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
918 }
919
920 /* make sure reported source has DELETE flag unset */
921 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
922
923 if (source->t_source_timer) {
924 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
925 IGMP_SOURCE_DO_SEND(source->source_flags);
926 ++num_sources_tosend;
927 }
928
929 } /* scan received sources (A) */
930
931 /*
932 delete all sources marked with DELETE flag:
933 Delete (X-A)
934 Delete (Y-A)
935 */
936 source_delete_by_flag(group->group_source_list);
937
938 /* send sources marked with SEND flag: Q(G,A-Y) */
939 if (num_sources_tosend > 0) {
940 source_query_send_by_flag(group, num_sources_tosend);
941 }
942}
943
944void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
945 struct in_addr group_addr,
946 int num_sources, struct in_addr *sources)
947{
948 struct interface *ifp = igmp->interface;
949 struct pim_interface *pim_ifp;
950 struct igmp_group *group;
951
952 on_trace(__PRETTY_FUNCTION__,
953 ifp, from, group_addr, num_sources, sources);
954
955 pim_ifp = ifp->info;
956
957 /* non-existant group is created as INCLUDE {empty} */
958 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
959 if (!group) {
960 return;
961 }
962
963 if (group->group_filtermode_isexcl) {
964 /* EXCLUDE mode */
965 toex_excl(group, num_sources, sources);
966 }
967 else {
968 /* INCLUDE mode */
969 toex_incl(group, num_sources, sources);
970 zassert(group->group_filtermode_isexcl);
971 }
972 zassert(group->group_filtermode_isexcl);
973
974 /* Group Timer=GMI */
975 igmp_group_reset_gmi(group);
976}
977
978void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
979 struct in_addr group_addr,
980 int num_sources, struct in_addr *sources)
981{
982 on_trace(__PRETTY_FUNCTION__,
983 igmp->interface, from, group_addr, num_sources, sources);
984
985 allow(igmp, from, group_addr, num_sources, sources);
986}
987
988/*
989 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
990
991 When transmitting a group specific query, if the group timer is
992 larger than LMQT, the "Suppress Router-Side Processing" bit is set
993 in the query message.
994*/
995static void group_retransmit_group(struct igmp_group *group)
996{
997 char query_buf[PIM_IGMP_BUFSIZE_WRITE];
998 struct igmp_sock *igmp;
999 struct pim_interface *pim_ifp;
1000 long lmqc; /* Last Member Query Count */
1001 long lmqi_msec; /* Last Member Query Interval */
1002 long lmqt_msec; /* Last Member Query Time */
1003 int s_flag;
1004
1005 igmp = group->group_igmp_sock;
1006 pim_ifp = igmp->interface->info;
1007
1008 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001009 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001010 lmqt_msec = lmqc * lmqi_msec;
1011
1012 /*
1013 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1014
1015 When transmitting a group specific query, if the group timer is
1016 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1017 in the query message.
1018 */
1019 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1020
1021 if (PIM_DEBUG_IGMP_TRACE) {
1022 char group_str[100];
1023 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1024 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1025 group_str, igmp->interface->name, s_flag,
1026 group->group_specific_query_retransmit_count);
1027 }
1028
1029 /*
1030 RFC3376: 4.1.12. IP Destination Addresses for Queries
1031
1032 Group-Specific and Group-and-Source-Specific Queries are sent with
1033 an IP destination address equal to the multicast address of
1034 interest.
1035 */
1036
1037 pim_igmp_send_membership_query(group,
1038 igmp->fd,
1039 igmp->interface->name,
1040 query_buf,
1041 sizeof(query_buf),
1042 0 /* num_sources_tosend */,
1043 group->group_addr /* dst_addr */,
1044 group->group_addr /* group_addr */,
Leonard Herve236b0152009-08-11 15:51:52 -03001045 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001046 s_flag,
1047 igmp->querier_robustness_variable,
1048 igmp->querier_query_interval);
1049}
1050
1051/*
1052 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1053
1054 When building a group and source specific query for a group G, two
1055 separate query messages are sent for the group. The first one has
1056 the "Suppress Router-Side Processing" bit set and contains all the
1057 sources with retransmission state and timers greater than LMQT. The
1058 second has the "Suppress Router-Side Processing" bit clear and
1059 contains all the sources with retransmission state and timers lower
1060 or equal to LMQT. If either of the two calculated messages does not
1061 contain any sources, then its transmission is suppressed.
1062 */
1063static int group_retransmit_sources(struct igmp_group *group,
1064 int send_with_sflag_set)
1065{
1066 struct igmp_sock *igmp;
1067 struct pim_interface *pim_ifp;
1068 long lmqc; /* Last Member Query Count */
1069 long lmqi_msec; /* Last Member Query Interval */
1070 long lmqt_msec; /* Last Member Query Time */
1071 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1072 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1073 int query_buf1_max_sources;
1074 int query_buf2_max_sources;
1075 struct in_addr *source_addr1;
1076 struct in_addr *source_addr2;
1077 int num_sources_tosend1;
1078 int num_sources_tosend2;
1079 struct listnode *src_node;
1080 struct igmp_source *src;
1081 int num_retransmit_sources_left = 0;
1082
1083 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1084 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1085
1086 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1087 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1088
1089 igmp = group->group_igmp_sock;
1090 pim_ifp = igmp->interface->info;
1091
1092 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001093 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001094 lmqt_msec = lmqc * lmqi_msec;
1095
1096 /* Scan all group sources */
1097 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1098
1099 /* Source has retransmission state? */
1100 if (src->source_query_retransmit_count < 1)
1101 continue;
1102
1103 if (--src->source_query_retransmit_count > 0) {
1104 ++num_retransmit_sources_left;
1105 }
1106
1107 /* Copy source address into appropriate query buffer */
1108 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1109 *source_addr1 = src->source_addr;
1110 ++source_addr1;
1111 }
1112 else {
1113 *source_addr2 = src->source_addr;
1114 ++source_addr2;
1115 }
1116
1117 }
1118
1119 num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1120 num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1121
1122 if (PIM_DEBUG_IGMP_TRACE) {
1123 char group_str[100];
1124 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1125 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",
1126 group_str, igmp->interface->name,
1127 num_sources_tosend1,
1128 num_sources_tosend2,
1129 send_with_sflag_set,
1130 num_retransmit_sources_left);
1131 }
1132
1133 if (num_sources_tosend1 > 0) {
1134 /*
1135 Send group-and-source-specific query with s_flag set and all
1136 sources with timers greater than LMQT.
1137 */
1138
1139 if (send_with_sflag_set) {
1140
1141 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1142 if (num_sources_tosend1 > query_buf1_max_sources) {
1143 char group_str[100];
1144 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1145 zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%d (max_sources=%d)",
1146 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1147 num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
1148 }
1149 else {
1150 /*
1151 RFC3376: 4.1.12. IP Destination Addresses for Queries
1152
1153 Group-Specific and Group-and-Source-Specific Queries are sent with
1154 an IP destination address equal to the multicast address of
1155 interest.
1156 */
1157
1158 pim_igmp_send_membership_query(group,
1159 igmp->fd,
1160 igmp->interface->name,
1161 query_buf1,
1162 sizeof(query_buf1),
1163 num_sources_tosend1,
1164 group->group_addr,
1165 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001166 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001167 1 /* s_flag */,
1168 igmp->querier_robustness_variable,
1169 igmp->querier_query_interval);
1170
1171 }
1172
1173 } /* send_with_sflag_set */
1174
1175 }
1176
1177 if (num_sources_tosend2 > 0) {
1178 /*
1179 Send group-and-source-specific query with s_flag clear and all
1180 sources with timers lower or equal to LMQT.
1181 */
1182
1183 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1184 if (num_sources_tosend2 > query_buf2_max_sources) {
1185 char group_str[100];
1186 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1187 zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%d (max_sources=%d)",
1188 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1189 num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
1190 }
1191 else {
1192 /*
1193 RFC3376: 4.1.12. IP Destination Addresses for Queries
1194
1195 Group-Specific and Group-and-Source-Specific Queries are sent with
1196 an IP destination address equal to the multicast address of
1197 interest.
1198 */
1199
1200 pim_igmp_send_membership_query(group,
1201 igmp->fd,
1202 igmp->interface->name,
1203 query_buf2,
1204 sizeof(query_buf2),
1205 num_sources_tosend2,
1206 group->group_addr,
1207 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001208 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001209 0 /* s_flag */,
1210 igmp->querier_robustness_variable,
1211 igmp->querier_query_interval);
1212
1213 }
1214 }
1215
1216 return num_retransmit_sources_left;
1217}
1218
1219static int igmp_group_retransmit(struct thread *t)
1220{
1221 struct igmp_group *group;
1222 int num_retransmit_sources_left;
1223 int send_with_sflag_set; /* boolean */
1224
1225 zassert(t);
1226 group = THREAD_ARG(t);
1227 zassert(group);
1228
1229 if (PIM_DEBUG_IGMP_TRACE) {
1230 char group_str[100];
1231 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1232 zlog_debug("group_retransmit_timer: group %s on %s",
1233 group_str, group->group_igmp_sock->interface->name);
1234 }
1235
1236 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1237 if (group->group_specific_query_retransmit_count > 0) {
1238
1239 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1240 group_retransmit_group(group);
1241 --group->group_specific_query_retransmit_count;
1242
1243 /*
1244 RFC3376: 6.6.3.2
1245 If a group specific query is scheduled to be transmitted at the
1246 same time as a group and source specific query for the same group,
1247 then transmission of the group and source specific message with the
1248 "Suppress Router-Side Processing" bit set may be suppressed.
1249 */
1250 send_with_sflag_set = 0; /* boolean=false */
1251 }
1252 else {
1253 send_with_sflag_set = 1; /* boolean=true */
1254 }
1255
1256 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1257 num_retransmit_sources_left = group_retransmit_sources(group,
1258 send_with_sflag_set);
1259
1260 group->t_group_query_retransmit_timer = 0;
1261
1262 /*
1263 Keep group retransmit timer running if there is any retransmit
1264 counter pending
1265 */
1266 if ((num_retransmit_sources_left > 0) ||
1267 (group->group_specific_query_retransmit_count > 0)) {
1268 group_retransmit_timer_on(group);
1269 }
1270
1271 return 0;
1272}
1273
1274/*
1275 group_retransmit_timer_on:
1276 if group retransmit timer isn't running, starts it;
1277 otherwise, do nothing
1278*/
1279static void group_retransmit_timer_on(struct igmp_group *group)
1280{
1281 struct igmp_sock *igmp;
1282 struct pim_interface *pim_ifp;
1283 long lmqi_msec; /* Last Member Query Interval */
1284
1285 /* if group retransmit timer is running, do nothing */
1286 if (group->t_group_query_retransmit_timer) {
1287 return;
1288 }
1289
1290 igmp = group->group_igmp_sock;
1291 pim_ifp = igmp->interface->info;
1292
Leonard Herve236b0152009-08-11 15:51:52 -03001293 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001294
1295 if (PIM_DEBUG_IGMP_TRACE) {
1296 char group_str[100];
1297 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1298 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1299 lmqi_msec / 1000,
1300 lmqi_msec % 1000,
1301 group_str,
1302 igmp->interface->name);
1303 }
1304
1305 THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
1306 igmp_group_retransmit,
1307 group, lmqi_msec);
1308}
1309
1310static long igmp_group_timer_remain_msec(struct igmp_group *group)
1311{
1312 return pim_time_timer_remain_msec(group->t_group_timer);
1313}
1314
1315static long igmp_source_timer_remain_msec(struct igmp_source *source)
1316{
1317 return pim_time_timer_remain_msec(source->t_source_timer);
1318}
1319
1320/*
1321 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1322*/
1323static void group_query_send(struct igmp_group *group)
1324{
1325 long lmqc; /* Last Member Query Count */
1326
1327 lmqc = group->group_igmp_sock->querier_robustness_variable;
1328
1329 /* lower group timer to lmqt */
1330 igmp_group_timer_lower_to_lmqt(group);
1331
1332 /* reset retransmission counter */
1333 group->group_specific_query_retransmit_count = lmqc;
1334
1335 /* immediately send group specific query (decrease retransmit counter by 1)*/
1336 group_retransmit_group(group);
1337
1338 /* make sure group retransmit timer is running */
1339 group_retransmit_timer_on(group);
1340}
1341
1342/*
1343 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1344*/
1345static void source_query_send_by_flag(struct igmp_group *group,
1346 int num_sources_tosend)
1347{
1348 struct igmp_sock *igmp;
1349 struct pim_interface *pim_ifp;
1350 struct listnode *src_node;
1351 struct igmp_source *src;
1352 long lmqc; /* Last Member Query Count */
1353 long lmqi_msec; /* Last Member Query Interval */
1354 long lmqt_msec; /* Last Member Query Time */
1355
1356 zassert(num_sources_tosend > 0);
1357
1358 igmp = group->group_igmp_sock;
1359 pim_ifp = igmp->interface->info;
1360
1361 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001362 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001363 lmqt_msec = lmqc * lmqi_msec;
1364
1365 /*
1366 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1367
1368 (...) for each of the sources in X of group G, with source timer larger
1369 than LMQT:
1370 o Set number of retransmissions for each source to [Last Member
1371 Query Count].
1372 o Lower source timer to LMQT.
1373 */
1374 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1375 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1376 /* source "src" in X of group G */
1377 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1378 src->source_query_retransmit_count = lmqc;
1379 igmp_source_timer_lower_to_lmqt(src);
1380 }
1381 }
1382 }
1383
1384 /* send group-and-source specific queries */
1385 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1386
1387 /* make sure group retransmit timer is running */
1388 group_retransmit_timer_on(group);
1389}
1390
1391static void block_excl(struct igmp_group *group,
1392 int num_sources, struct in_addr *sources)
1393{
1394 int num_sources_tosend = 0;
1395
1396 /* 1. clear off SEND flag from all known sources (X,Y) */
1397 source_clear_send_flag(group->group_source_list);
1398
1399 /* 2. scan received sources (A) */
1400 for (int i = 0; i < num_sources; ++i) {
1401 struct igmp_source *source;
1402 struct in_addr *src_addr;
1403
1404 src_addr = sources + i;
1405
1406 /* lookup reported source (A) in known sources (X,Y) */
1407 source = igmp_find_source_by_addr(group, *src_addr);
1408 if (!source) {
1409 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1410 long group_timer_msec;
1411 source = source_new(group, *src_addr,
1412 group->group_igmp_sock->interface->name);
1413 if (!source) {
1414 /* ugh, internal malloc failure, skip source */
1415 continue;
1416 }
1417
1418 zassert(!source->t_source_timer); /* timer == 0 */
1419 group_timer_msec = igmp_group_timer_remain_msec(group);
1420 igmp_source_timer_on(group, source, group_timer_msec);
1421 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1422 }
1423
1424 if (source->t_source_timer) {
1425 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1426 IGMP_SOURCE_DO_SEND(source->source_flags);
1427 ++num_sources_tosend;
1428 }
1429 }
1430
1431 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1432 if (num_sources_tosend > 0) {
1433 source_query_send_by_flag(group, num_sources_tosend);
1434 }
1435}
1436
1437static void block_incl(struct igmp_group *group,
1438 int num_sources, struct in_addr *sources)
1439{
1440 int num_sources_tosend = 0;
1441
1442 /* 1. clear off SEND flag from all known sources (B) */
1443 source_clear_send_flag(group->group_source_list);
1444
1445 /* 2. scan received sources (A) */
1446 for (int i = 0; i < num_sources; ++i) {
1447 struct igmp_source *source;
1448 struct in_addr *src_addr;
1449
1450 src_addr = sources + i;
1451
1452 /* lookup reported source (A) in known sources (B) */
1453 source = igmp_find_source_by_addr(group, *src_addr);
1454 if (source) {
1455 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1456 IGMP_SOURCE_DO_SEND(source->source_flags);
1457 ++num_sources_tosend;
1458 }
1459 }
1460
1461 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1462 if (num_sources_tosend > 0) {
1463 source_query_send_by_flag(group, num_sources_tosend);
1464 }
1465}
1466
1467void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1468 struct in_addr group_addr,
1469 int num_sources, struct in_addr *sources)
1470{
1471 struct interface *ifp = igmp->interface;
1472 struct pim_interface *pim_ifp;
1473 struct igmp_group *group;
1474
1475 on_trace(__PRETTY_FUNCTION__,
1476 ifp, from, group_addr, num_sources, sources);
1477
1478 pim_ifp = ifp->info;
1479
1480 /* non-existant group is created as INCLUDE {empty} */
1481 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
1482 if (!group) {
1483 return;
1484 }
1485
1486 if (group->group_filtermode_isexcl) {
1487 /* EXCLUDE mode */
1488 block_excl(group, num_sources, sources);
1489 }
1490 else {
1491 /* INCLUDE mode */
1492 block_incl(group, num_sources, sources);
1493 }
1494}
1495
1496void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1497{
1498 struct igmp_sock *igmp;
1499 struct interface *ifp;
1500 struct pim_interface *pim_ifp;
1501 char *ifname;
1502 int lmqi_dsec; /* Last Member Query Interval */
1503 int lmqc; /* Last Member Query Count */
1504 int lmqt_msec; /* Last Member Query Time */
1505
1506 /*
1507 RFC 3376: 6.2.2. Definition of Group Timers
1508
1509 The group timer is only used when a group is in EXCLUDE mode and
1510 it represents the time for the *filter-mode* of the group to
1511 expire and switch to INCLUDE mode.
1512 */
1513 if (!group->group_filtermode_isexcl) {
1514 return;
1515 }
1516
1517 igmp = group->group_igmp_sock;
1518 ifp = igmp->interface;
1519 pim_ifp = ifp->info;
1520 ifname = ifp->name;
1521
Leonard Herve236b0152009-08-11 15:51:52 -03001522 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001523 lmqc = igmp->querier_robustness_variable;
1524 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1525
1526 if (PIM_DEBUG_IGMP_TRACE) {
1527 char group_str[100];
1528 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1529 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1530 __PRETTY_FUNCTION__,
1531 group_str, ifname,
1532 lmqc, lmqi_dsec, lmqt_msec);
1533 }
1534
1535 zassert(group->group_filtermode_isexcl);
1536
1537 igmp_group_timer_on(group, lmqt_msec, ifname);
1538}
1539
1540void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1541{
1542 struct igmp_group *group;
1543 struct igmp_sock *igmp;
1544 struct interface *ifp;
1545 struct pim_interface *pim_ifp;
1546 char *ifname;
1547 int lmqi_dsec; /* Last Member Query Interval */
1548 int lmqc; /* Last Member Query Count */
1549 int lmqt_msec; /* Last Member Query Time */
1550
1551 group = source->source_group;
1552 igmp = group->group_igmp_sock;
1553 ifp = igmp->interface;
1554 pim_ifp = ifp->info;
1555 ifname = ifp->name;
1556
Leonard Herve236b0152009-08-11 15:51:52 -03001557 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001558 lmqc = igmp->querier_robustness_variable;
1559 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1560
1561 if (PIM_DEBUG_IGMP_TRACE) {
1562 char group_str[100];
1563 char source_str[100];
1564 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1565 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1566 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1567 __PRETTY_FUNCTION__,
1568 group_str, source_str, ifname,
1569 lmqc, lmqi_dsec, lmqt_msec);
1570 }
1571
1572 igmp_source_timer_on(group, source, lmqt_msec);
1573}
1574
1575/*
1576 Copy sources to message:
1577
1578 struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
1579 if (num_sources > 0) {
1580 struct listnode *node;
1581 struct igmp_source *src;
1582 int i = 0;
1583
1584 for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
1585 sources[i++] = src->source_addr;
1586 }
1587 }
1588*/
1589void pim_igmp_send_membership_query(struct igmp_group *group,
1590 int fd,
1591 const char *ifname,
1592 char *query_buf,
1593 int query_buf_size,
1594 int num_sources,
1595 struct in_addr dst_addr,
1596 struct in_addr group_addr,
1597 int query_max_response_time_dsec,
1598 uint8_t s_flag,
1599 uint8_t querier_robustness_variable,
1600 uint16_t querier_query_interval)
1601{
1602 ssize_t msg_size;
1603 uint8_t max_resp_code;
1604 uint8_t qqic;
1605 ssize_t sent;
1606 struct sockaddr_in to;
1607 socklen_t tolen;
1608 uint16_t checksum;
1609
1610 zassert(num_sources >= 0);
1611
1612 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1613 if (msg_size > query_buf_size) {
1614 zlog_err("%s %s: unable to send: msg_size=%d larger than query_buf_size=%d",
1615 __FILE__, __PRETTY_FUNCTION__,
1616 msg_size, query_buf_size);
1617 return;
1618 }
1619
1620 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1621 zassert((s_flag == 0) || (s_flag == 1));
1622
1623 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1624 qqic = igmp_msg_encode16to8(querier_query_interval);
1625
1626 /*
1627 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1628
1629 If non-zero, the QRV field contains the [Robustness Variable]
1630 value used by the querier, i.e., the sender of the Query. If the
1631 querier's [Robustness Variable] exceeds 7, the maximum value of
1632 the QRV field, the QRV is set to zero.
1633 */
1634 if (querier_robustness_variable > 7) {
1635 querier_robustness_variable = 0;
1636 }
1637
1638 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1639 query_buf[1] = max_resp_code;
1640 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
1641 *(struct in_addr *)(query_buf + 4) = group_addr;
1642 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1643 query_buf[9] = qqic;
1644 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
1645
1646 checksum = pim_inet_checksum(query_buf, msg_size);
1647 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
1648
1649 if (PIM_DEBUG_IGMP_PACKETS) {
1650 char dst_str[100];
1651 char group_str[100];
1652 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1653 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1654 zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%d s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
1655 __PRETTY_FUNCTION__,
1656 dst_str, ifname, group_str, num_sources,
1657 msg_size, s_flag, querier_robustness_variable,
1658 querier_query_interval, qqic, checksum);
1659 }
1660
1661#if 0
1662 memset(&to, 0, sizeof(to));
1663#endif
1664 to.sin_family = AF_INET;
1665 to.sin_addr = dst_addr;
1666#if 0
1667 to.sin_port = htons(0);
1668#endif
1669 tolen = sizeof(to);
1670
1671 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, &to, tolen);
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=%d: errno=%d: %s",
1680 __PRETTY_FUNCTION__,
1681 dst_str, ifname, group_str, msg_size,
Everton Marquese96f0af2009-08-11 15:48:02 -03001682 e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -03001683 }
1684 else {
1685 zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%d: sent=%d",
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}