blob: f8926524eed238a86d7fd05b550e6ae4c9f9a0dd [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;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200534 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300535
536 pim_ifp = ifp->info;
537
538 /* non-existant group is created as INCLUDE {empty} */
539 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
540 if (!group) {
541 return;
542 }
543
544 /* scan received sources */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200545 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300546 struct igmp_source *source;
547 struct in_addr *src_addr;
548
549 src_addr = sources + i;
550
551 source = add_source_by_addr(igmp, group, *src_addr, ifp->name);
552 if (!source) {
553 continue;
554 }
555
556 /*
557 RFC 3376: 6.4.1. Reception of Current-State Records
558
559 When receiving IS_IN reports for groups in EXCLUDE mode is
560 sources should be moved from set with (timers = 0) to set with
561 (timers > 0).
562
563 igmp_source_reset_gmi() below, resetting the source timers to
564 GMI, accomplishes this.
565 */
566 igmp_source_reset_gmi(igmp, group, source);
567
568 } /* scan received sources */
569}
570
571void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
572 struct in_addr group_addr,
573 int num_sources, struct in_addr *sources)
574{
575 on_trace(__PRETTY_FUNCTION__,
576 igmp->interface, from, group_addr, num_sources, sources);
577
578 allow(igmp, from, group_addr, num_sources, sources);
579}
580
581static void isex_excl(struct igmp_group *group,
582 int num_sources, struct in_addr *sources)
583{
Leonard Herve942b0fd2009-08-14 15:49:06 +0200584 int i;
585
Everton Marques871dbcf2009-08-11 15:43:05 -0300586 /* EXCLUDE mode */
587 zassert(group->group_filtermode_isexcl);
588
589 /* E.1: set deletion flag for known sources (X,Y) */
590 source_mark_delete_flag(group->group_source_list);
591
592 /* scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200593 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300594 struct igmp_source *source;
595 struct in_addr *src_addr;
596
597 src_addr = sources + i;
598
599 /* E.2: lookup reported source from (A) in (X,Y) */
600 source = igmp_find_source_by_addr(group, *src_addr);
601 if (source) {
602 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
603 IGMP_SOURCE_DONT_DELETE(source->source_flags);
604 }
605 else {
606 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
607 source = source_new(group, *src_addr,
608 group->group_igmp_sock->interface->name);
609 if (!source) {
610 /* ugh, internal malloc failure, skip source */
611 continue;
612 }
613 zassert(!source->t_source_timer); /* timer == 0 */
614 igmp_source_reset_gmi(group->group_igmp_sock, group, source);
615 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
616 }
617
618 } /* scan received sources */
619
620 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
621 source_delete_by_flag(group->group_source_list);
622}
623
624static void isex_incl(struct igmp_group *group,
625 int num_sources, struct in_addr *sources)
626{
Leonard Herve942b0fd2009-08-14 15:49:06 +0200627 int i;
628
Everton Marques871dbcf2009-08-11 15:43:05 -0300629 /* INCLUDE mode */
630 zassert(!group->group_filtermode_isexcl);
631
632 /* I.1: set deletion flag for known sources (A) */
633 source_mark_delete_flag(group->group_source_list);
634
635 /* scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200636 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300637 struct igmp_source *source;
638 struct in_addr *src_addr;
639
640 src_addr = sources + i;
641
642 /* I.2: lookup reported source (B) */
643 source = igmp_find_source_by_addr(group, *src_addr);
644 if (source) {
645 /* I.3: if found, clear deletion flag (A*B) */
646 IGMP_SOURCE_DONT_DELETE(source->source_flags);
647 }
648 else {
649 /* I.4: if not found, create source with timer=0 (B-A) */
650 source = source_new(group, *src_addr,
651 group->group_igmp_sock->interface->name);
652 if (!source) {
653 /* ugh, internal malloc failure, skip source */
654 continue;
655 }
656 zassert(!source->t_source_timer); /* (B-A) timer=0 */
657 }
658
659 } /* scan received sources */
660
661 /* I.5: delete all sources marked with deletion flag (A-B) */
662 source_delete_by_flag(group->group_source_list);
663
664 group->group_filtermode_isexcl = 1; /* boolean=true */
665
666 zassert(group->group_filtermode_isexcl);
667
668 group_exclude_fwd_anysrc_ifempty(group);
669}
670
671void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
672 struct in_addr group_addr,
673 int num_sources, struct in_addr *sources)
674{
675 struct interface *ifp = igmp->interface;
676 struct pim_interface *pim_ifp;
677 struct igmp_group *group;
678
679 on_trace(__PRETTY_FUNCTION__,
680 ifp, from, group_addr, num_sources, sources);
681
682 pim_ifp = ifp->info;
683
684 /* non-existant group is created as INCLUDE {empty} */
685 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
686 if (!group) {
687 return;
688 }
689
690 if (group->group_filtermode_isexcl) {
691 /* EXCLUDE mode */
692 isex_excl(group, num_sources, sources);
693 }
694 else {
695 /* INCLUDE mode */
696 isex_incl(group, num_sources, sources);
697 zassert(group->group_filtermode_isexcl);
698 }
699
700 zassert(group->group_filtermode_isexcl);
701
702 igmp_group_reset_gmi(group);
703}
704
705static void toin_incl(struct igmp_group *group,
706 int num_sources, struct in_addr *sources)
707{
708 struct igmp_sock *igmp = group->group_igmp_sock;
709 int num_sources_tosend = listcount(group->group_source_list);
Leonard Herve942b0fd2009-08-14 15:49:06 +0200710 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300711
712 /* Set SEND flag for all known sources (A) */
713 source_mark_send_flag(group->group_source_list);
714
715 /* Scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200716 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300717 struct igmp_source *source;
718 struct in_addr *src_addr;
719
720 src_addr = sources + i;
721
722 /* Lookup reported source (B) */
723 source = igmp_find_source_by_addr(group, *src_addr);
724 if (source) {
725 /* If found, clear SEND flag (A*B) */
726 IGMP_SOURCE_DONT_SEND(source->source_flags);
727 --num_sources_tosend;
728 }
729 else {
730 /* If not found, create new source */
731 source = source_new(group, *src_addr,
732 group->group_igmp_sock->interface->name);
733 if (!source) {
734 /* ugh, internal malloc failure, skip source */
735 continue;
736 }
737 }
738
739 /* (B)=GMI */
740 igmp_source_reset_gmi(igmp, group, source);
741 }
742
743 /* Send sources marked with SEND flag: Q(G,A-B) */
744 if (num_sources_tosend > 0) {
745 source_query_send_by_flag(group, num_sources_tosend);
746 }
747}
748
749static void toin_excl(struct igmp_group *group,
750 int num_sources, struct in_addr *sources)
751{
752 struct igmp_sock *igmp = group->group_igmp_sock;
753 int num_sources_tosend;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200754 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300755
756 /* Set SEND flag for X (sources with timer > 0) */
757 num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
758
759 /* Scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200760 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300761 struct igmp_source *source;
762 struct in_addr *src_addr;
763
764 src_addr = sources + i;
765
766 /* Lookup reported source (A) */
767 source = igmp_find_source_by_addr(group, *src_addr);
768 if (source) {
769 if (source->t_source_timer) {
770 /* If found and timer running, clear SEND flag (X*A) */
771 IGMP_SOURCE_DONT_SEND(source->source_flags);
772 --num_sources_tosend;
773 }
774 }
775 else {
776 /* If not found, create new source */
777 source = source_new(group, *src_addr,
778 group->group_igmp_sock->interface->name);
779 if (!source) {
780 /* ugh, internal malloc failure, skip source */
781 continue;
782 }
783 }
784
785 /* (A)=GMI */
786 igmp_source_reset_gmi(igmp, group, source);
787 }
788
789 /* Send sources marked with SEND flag: Q(G,X-A) */
790 if (num_sources_tosend > 0) {
791 source_query_send_by_flag(group, num_sources_tosend);
792 }
793
794 /* Send Q(G) */
795 group_query_send(group);
796}
797
798void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
799 struct in_addr group_addr,
800 int num_sources, struct in_addr *sources)
801{
802 struct interface *ifp = igmp->interface;
803 struct pim_interface *pim_ifp;
804 struct igmp_group *group;
805
806 on_trace(__PRETTY_FUNCTION__,
807 ifp, from, group_addr, num_sources, sources);
808
809 pim_ifp = ifp->info;
810
811 /* non-existant group is created as INCLUDE {empty} */
812 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
813 if (!group) {
814 return;
815 }
816
817 if (group->group_filtermode_isexcl) {
818 /* EXCLUDE mode */
819 toin_excl(group, num_sources, sources);
820 }
821 else {
822 /* INCLUDE mode */
823 toin_incl(group, num_sources, sources);
824 }
825}
826
827static void toex_incl(struct igmp_group *group,
828 int num_sources, struct in_addr *sources)
829{
830 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200831 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300832
833 zassert(!group->group_filtermode_isexcl);
834
835 /* Set DELETE flag for all known sources (A) */
836 source_mark_delete_flag(group->group_source_list);
837
838 /* Clear off SEND flag from all known sources (A) */
839 source_clear_send_flag(group->group_source_list);
840
841 /* Scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200842 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300843 struct igmp_source *source;
844 struct in_addr *src_addr;
845
846 src_addr = sources + i;
847
848 /* Lookup reported source (B) */
849 source = igmp_find_source_by_addr(group, *src_addr);
850 if (source) {
851 /* If found, clear deletion flag: (A*B) */
852 IGMP_SOURCE_DONT_DELETE(source->source_flags);
853 /* and set SEND flag (A*B) */
854 IGMP_SOURCE_DO_SEND(source->source_flags);
855 ++num_sources_tosend;
856 }
857 else {
858 /* If source not found, create source with timer=0: (B-A)=0 */
859 source = source_new(group, *src_addr,
860 group->group_igmp_sock->interface->name);
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;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200889 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300890
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) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200898 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300899 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;
913 source = source_new(group, *src_addr,
914 group->group_igmp_sock->interface->name);
915 if (!source) {
916 /* ugh, internal malloc failure, skip source */
917 continue;
918 }
919
920 zassert(!source->t_source_timer); /* timer == 0 */
921 group_timer_msec = igmp_group_timer_remain_msec(group);
922 igmp_source_timer_on(group, source, group_timer_msec);
923 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
924
925 /* make sure source is created with DELETE flag unset */
926 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
927 }
928
929 /* make sure reported source has DELETE flag unset */
930 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
931
932 if (source->t_source_timer) {
933 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
934 IGMP_SOURCE_DO_SEND(source->source_flags);
935 ++num_sources_tosend;
936 }
937
938 } /* scan received sources (A) */
939
940 /*
941 delete all sources marked with DELETE flag:
942 Delete (X-A)
943 Delete (Y-A)
944 */
945 source_delete_by_flag(group->group_source_list);
946
947 /* send sources marked with SEND flag: Q(G,A-Y) */
948 if (num_sources_tosend > 0) {
949 source_query_send_by_flag(group, num_sources_tosend);
950 }
951}
952
953void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
954 struct in_addr group_addr,
955 int num_sources, struct in_addr *sources)
956{
957 struct interface *ifp = igmp->interface;
958 struct pim_interface *pim_ifp;
959 struct igmp_group *group;
960
961 on_trace(__PRETTY_FUNCTION__,
962 ifp, from, group_addr, num_sources, sources);
963
964 pim_ifp = ifp->info;
965
966 /* non-existant group is created as INCLUDE {empty} */
967 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
968 if (!group) {
969 return;
970 }
971
972 if (group->group_filtermode_isexcl) {
973 /* EXCLUDE mode */
974 toex_excl(group, num_sources, sources);
975 }
976 else {
977 /* INCLUDE mode */
978 toex_incl(group, num_sources, sources);
979 zassert(group->group_filtermode_isexcl);
980 }
981 zassert(group->group_filtermode_isexcl);
982
983 /* Group Timer=GMI */
984 igmp_group_reset_gmi(group);
985}
986
987void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
988 struct in_addr group_addr,
989 int num_sources, struct in_addr *sources)
990{
991 on_trace(__PRETTY_FUNCTION__,
992 igmp->interface, from, group_addr, num_sources, sources);
993
994 allow(igmp, from, group_addr, num_sources, sources);
995}
996
997/*
998 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
999
1000 When transmitting a group specific query, if the group timer is
1001 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1002 in the query message.
1003*/
1004static void group_retransmit_group(struct igmp_group *group)
1005{
1006 char query_buf[PIM_IGMP_BUFSIZE_WRITE];
1007 struct igmp_sock *igmp;
1008 struct pim_interface *pim_ifp;
1009 long lmqc; /* Last Member Query Count */
1010 long lmqi_msec; /* Last Member Query Interval */
1011 long lmqt_msec; /* Last Member Query Time */
1012 int s_flag;
1013
1014 igmp = group->group_igmp_sock;
1015 pim_ifp = igmp->interface->info;
1016
1017 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001018 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001019 lmqt_msec = lmqc * lmqi_msec;
1020
1021 /*
1022 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1023
1024 When transmitting a group specific query, if the group timer is
1025 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1026 in the query message.
1027 */
1028 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1029
1030 if (PIM_DEBUG_IGMP_TRACE) {
1031 char group_str[100];
1032 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1033 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1034 group_str, igmp->interface->name, s_flag,
1035 group->group_specific_query_retransmit_count);
1036 }
1037
1038 /*
1039 RFC3376: 4.1.12. IP Destination Addresses for Queries
1040
1041 Group-Specific and Group-and-Source-Specific Queries are sent with
1042 an IP destination address equal to the multicast address of
1043 interest.
1044 */
1045
1046 pim_igmp_send_membership_query(group,
1047 igmp->fd,
1048 igmp->interface->name,
1049 query_buf,
1050 sizeof(query_buf),
1051 0 /* num_sources_tosend */,
1052 group->group_addr /* dst_addr */,
1053 group->group_addr /* group_addr */,
Leonard Herve236b0152009-08-11 15:51:52 -03001054 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001055 s_flag,
1056 igmp->querier_robustness_variable,
1057 igmp->querier_query_interval);
1058}
1059
1060/*
1061 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1062
1063 When building a group and source specific query for a group G, two
1064 separate query messages are sent for the group. The first one has
1065 the "Suppress Router-Side Processing" bit set and contains all the
1066 sources with retransmission state and timers greater than LMQT. The
1067 second has the "Suppress Router-Side Processing" bit clear and
1068 contains all the sources with retransmission state and timers lower
1069 or equal to LMQT. If either of the two calculated messages does not
1070 contain any sources, then its transmission is suppressed.
1071 */
1072static int group_retransmit_sources(struct igmp_group *group,
1073 int send_with_sflag_set)
1074{
1075 struct igmp_sock *igmp;
1076 struct pim_interface *pim_ifp;
1077 long lmqc; /* Last Member Query Count */
1078 long lmqi_msec; /* Last Member Query Interval */
1079 long lmqt_msec; /* Last Member Query Time */
1080 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1081 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1082 int query_buf1_max_sources;
1083 int query_buf2_max_sources;
1084 struct in_addr *source_addr1;
1085 struct in_addr *source_addr2;
1086 int num_sources_tosend1;
1087 int num_sources_tosend2;
1088 struct listnode *src_node;
1089 struct igmp_source *src;
1090 int num_retransmit_sources_left = 0;
1091
1092 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1093 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1094
1095 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1096 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1097
1098 igmp = group->group_igmp_sock;
1099 pim_ifp = igmp->interface->info;
1100
1101 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001102 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001103 lmqt_msec = lmqc * lmqi_msec;
1104
1105 /* Scan all group sources */
1106 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1107
1108 /* Source has retransmission state? */
1109 if (src->source_query_retransmit_count < 1)
1110 continue;
1111
1112 if (--src->source_query_retransmit_count > 0) {
1113 ++num_retransmit_sources_left;
1114 }
1115
1116 /* Copy source address into appropriate query buffer */
1117 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1118 *source_addr1 = src->source_addr;
1119 ++source_addr1;
1120 }
1121 else {
1122 *source_addr2 = src->source_addr;
1123 ++source_addr2;
1124 }
1125
1126 }
1127
1128 num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1129 num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1130
1131 if (PIM_DEBUG_IGMP_TRACE) {
1132 char group_str[100];
1133 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1134 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",
1135 group_str, igmp->interface->name,
1136 num_sources_tosend1,
1137 num_sources_tosend2,
1138 send_with_sflag_set,
1139 num_retransmit_sources_left);
1140 }
1141
1142 if (num_sources_tosend1 > 0) {
1143 /*
1144 Send group-and-source-specific query with s_flag set and all
1145 sources with timers greater than LMQT.
1146 */
1147
1148 if (send_with_sflag_set) {
1149
1150 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1151 if (num_sources_tosend1 > query_buf1_max_sources) {
1152 char group_str[100];
1153 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001154 zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
Everton Marques871dbcf2009-08-11 15:43:05 -03001155 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1156 num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
1157 }
1158 else {
1159 /*
1160 RFC3376: 4.1.12. IP Destination Addresses for Queries
1161
1162 Group-Specific and Group-and-Source-Specific Queries are sent with
1163 an IP destination address equal to the multicast address of
1164 interest.
1165 */
1166
1167 pim_igmp_send_membership_query(group,
1168 igmp->fd,
1169 igmp->interface->name,
1170 query_buf1,
1171 sizeof(query_buf1),
1172 num_sources_tosend1,
1173 group->group_addr,
1174 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001175 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001176 1 /* s_flag */,
1177 igmp->querier_robustness_variable,
1178 igmp->querier_query_interval);
1179
1180 }
1181
1182 } /* send_with_sflag_set */
1183
1184 }
1185
1186 if (num_sources_tosend2 > 0) {
1187 /*
1188 Send group-and-source-specific query with s_flag clear and all
1189 sources with timers lower or equal to LMQT.
1190 */
1191
1192 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1193 if (num_sources_tosend2 > query_buf2_max_sources) {
1194 char group_str[100];
1195 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001196 zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
Everton Marques871dbcf2009-08-11 15:43:05 -03001197 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1198 num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
1199 }
1200 else {
1201 /*
1202 RFC3376: 4.1.12. IP Destination Addresses for Queries
1203
1204 Group-Specific and Group-and-Source-Specific Queries are sent with
1205 an IP destination address equal to the multicast address of
1206 interest.
1207 */
1208
1209 pim_igmp_send_membership_query(group,
1210 igmp->fd,
1211 igmp->interface->name,
1212 query_buf2,
1213 sizeof(query_buf2),
1214 num_sources_tosend2,
1215 group->group_addr,
1216 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001217 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001218 0 /* s_flag */,
1219 igmp->querier_robustness_variable,
1220 igmp->querier_query_interval);
1221
1222 }
1223 }
1224
1225 return num_retransmit_sources_left;
1226}
1227
1228static int igmp_group_retransmit(struct thread *t)
1229{
1230 struct igmp_group *group;
1231 int num_retransmit_sources_left;
1232 int send_with_sflag_set; /* boolean */
1233
1234 zassert(t);
1235 group = THREAD_ARG(t);
1236 zassert(group);
1237
1238 if (PIM_DEBUG_IGMP_TRACE) {
1239 char group_str[100];
1240 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1241 zlog_debug("group_retransmit_timer: group %s on %s",
1242 group_str, group->group_igmp_sock->interface->name);
1243 }
1244
1245 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1246 if (group->group_specific_query_retransmit_count > 0) {
1247
1248 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1249 group_retransmit_group(group);
1250 --group->group_specific_query_retransmit_count;
1251
1252 /*
1253 RFC3376: 6.6.3.2
1254 If a group specific query is scheduled to be transmitted at the
1255 same time as a group and source specific query for the same group,
1256 then transmission of the group and source specific message with the
1257 "Suppress Router-Side Processing" bit set may be suppressed.
1258 */
1259 send_with_sflag_set = 0; /* boolean=false */
1260 }
1261 else {
1262 send_with_sflag_set = 1; /* boolean=true */
1263 }
1264
1265 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1266 num_retransmit_sources_left = group_retransmit_sources(group,
1267 send_with_sflag_set);
1268
1269 group->t_group_query_retransmit_timer = 0;
1270
1271 /*
1272 Keep group retransmit timer running if there is any retransmit
1273 counter pending
1274 */
1275 if ((num_retransmit_sources_left > 0) ||
1276 (group->group_specific_query_retransmit_count > 0)) {
1277 group_retransmit_timer_on(group);
1278 }
1279
1280 return 0;
1281}
1282
1283/*
1284 group_retransmit_timer_on:
1285 if group retransmit timer isn't running, starts it;
1286 otherwise, do nothing
1287*/
1288static void group_retransmit_timer_on(struct igmp_group *group)
1289{
1290 struct igmp_sock *igmp;
1291 struct pim_interface *pim_ifp;
1292 long lmqi_msec; /* Last Member Query Interval */
1293
1294 /* if group retransmit timer is running, do nothing */
1295 if (group->t_group_query_retransmit_timer) {
1296 return;
1297 }
1298
1299 igmp = group->group_igmp_sock;
1300 pim_ifp = igmp->interface->info;
1301
Leonard Herve236b0152009-08-11 15:51:52 -03001302 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001303
1304 if (PIM_DEBUG_IGMP_TRACE) {
1305 char group_str[100];
1306 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1307 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1308 lmqi_msec / 1000,
1309 lmqi_msec % 1000,
1310 group_str,
1311 igmp->interface->name);
1312 }
1313
1314 THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
1315 igmp_group_retransmit,
1316 group, lmqi_msec);
1317}
1318
1319static long igmp_group_timer_remain_msec(struct igmp_group *group)
1320{
1321 return pim_time_timer_remain_msec(group->t_group_timer);
1322}
1323
1324static long igmp_source_timer_remain_msec(struct igmp_source *source)
1325{
1326 return pim_time_timer_remain_msec(source->t_source_timer);
1327}
1328
1329/*
1330 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1331*/
1332static void group_query_send(struct igmp_group *group)
1333{
1334 long lmqc; /* Last Member Query Count */
1335
1336 lmqc = group->group_igmp_sock->querier_robustness_variable;
1337
1338 /* lower group timer to lmqt */
1339 igmp_group_timer_lower_to_lmqt(group);
1340
1341 /* reset retransmission counter */
1342 group->group_specific_query_retransmit_count = lmqc;
1343
1344 /* immediately send group specific query (decrease retransmit counter by 1)*/
1345 group_retransmit_group(group);
1346
1347 /* make sure group retransmit timer is running */
1348 group_retransmit_timer_on(group);
1349}
1350
1351/*
1352 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1353*/
1354static void source_query_send_by_flag(struct igmp_group *group,
1355 int num_sources_tosend)
1356{
1357 struct igmp_sock *igmp;
1358 struct pim_interface *pim_ifp;
1359 struct listnode *src_node;
1360 struct igmp_source *src;
1361 long lmqc; /* Last Member Query Count */
1362 long lmqi_msec; /* Last Member Query Interval */
1363 long lmqt_msec; /* Last Member Query Time */
1364
1365 zassert(num_sources_tosend > 0);
1366
1367 igmp = group->group_igmp_sock;
1368 pim_ifp = igmp->interface->info;
1369
1370 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001371 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001372 lmqt_msec = lmqc * lmqi_msec;
1373
1374 /*
1375 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1376
1377 (...) for each of the sources in X of group G, with source timer larger
1378 than LMQT:
1379 o Set number of retransmissions for each source to [Last Member
1380 Query Count].
1381 o Lower source timer to LMQT.
1382 */
1383 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1384 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1385 /* source "src" in X of group G */
1386 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1387 src->source_query_retransmit_count = lmqc;
1388 igmp_source_timer_lower_to_lmqt(src);
1389 }
1390 }
1391 }
1392
1393 /* send group-and-source specific queries */
1394 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1395
1396 /* make sure group retransmit timer is running */
1397 group_retransmit_timer_on(group);
1398}
1399
1400static void block_excl(struct igmp_group *group,
1401 int num_sources, struct in_addr *sources)
1402{
1403 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +02001404 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -03001405
1406 /* 1. clear off SEND flag from all known sources (X,Y) */
1407 source_clear_send_flag(group->group_source_list);
1408
1409 /* 2. scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +02001410 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -03001411 struct igmp_source *source;
1412 struct in_addr *src_addr;
1413
1414 src_addr = sources + i;
1415
1416 /* lookup reported source (A) in known sources (X,Y) */
1417 source = igmp_find_source_by_addr(group, *src_addr);
1418 if (!source) {
1419 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1420 long group_timer_msec;
1421 source = source_new(group, *src_addr,
1422 group->group_igmp_sock->interface->name);
1423 if (!source) {
1424 /* ugh, internal malloc failure, skip source */
1425 continue;
1426 }
1427
1428 zassert(!source->t_source_timer); /* timer == 0 */
1429 group_timer_msec = igmp_group_timer_remain_msec(group);
1430 igmp_source_timer_on(group, source, group_timer_msec);
1431 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1432 }
1433
1434 if (source->t_source_timer) {
1435 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1436 IGMP_SOURCE_DO_SEND(source->source_flags);
1437 ++num_sources_tosend;
1438 }
1439 }
1440
1441 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1442 if (num_sources_tosend > 0) {
1443 source_query_send_by_flag(group, num_sources_tosend);
1444 }
1445}
1446
1447static void block_incl(struct igmp_group *group,
1448 int num_sources, struct in_addr *sources)
1449{
1450 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +02001451 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -03001452
1453 /* 1. clear off SEND flag from all known sources (B) */
1454 source_clear_send_flag(group->group_source_list);
1455
1456 /* 2. scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +02001457 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -03001458 struct igmp_source *source;
1459 struct in_addr *src_addr;
1460
1461 src_addr = sources + i;
1462
1463 /* lookup reported source (A) in known sources (B) */
1464 source = igmp_find_source_by_addr(group, *src_addr);
1465 if (source) {
1466 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1467 IGMP_SOURCE_DO_SEND(source->source_flags);
1468 ++num_sources_tosend;
1469 }
1470 }
1471
1472 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1473 if (num_sources_tosend > 0) {
1474 source_query_send_by_flag(group, num_sources_tosend);
1475 }
1476}
1477
1478void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1479 struct in_addr group_addr,
1480 int num_sources, struct in_addr *sources)
1481{
1482 struct interface *ifp = igmp->interface;
1483 struct pim_interface *pim_ifp;
1484 struct igmp_group *group;
1485
1486 on_trace(__PRETTY_FUNCTION__,
1487 ifp, from, group_addr, num_sources, sources);
1488
1489 pim_ifp = ifp->info;
1490
1491 /* non-existant group is created as INCLUDE {empty} */
1492 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
1493 if (!group) {
1494 return;
1495 }
1496
1497 if (group->group_filtermode_isexcl) {
1498 /* EXCLUDE mode */
1499 block_excl(group, num_sources, sources);
1500 }
1501 else {
1502 /* INCLUDE mode */
1503 block_incl(group, num_sources, sources);
1504 }
1505}
1506
1507void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1508{
1509 struct igmp_sock *igmp;
1510 struct interface *ifp;
1511 struct pim_interface *pim_ifp;
1512 char *ifname;
1513 int lmqi_dsec; /* Last Member Query Interval */
1514 int lmqc; /* Last Member Query Count */
1515 int lmqt_msec; /* Last Member Query Time */
1516
1517 /*
1518 RFC 3376: 6.2.2. Definition of Group Timers
1519
1520 The group timer is only used when a group is in EXCLUDE mode and
1521 it represents the time for the *filter-mode* of the group to
1522 expire and switch to INCLUDE mode.
1523 */
1524 if (!group->group_filtermode_isexcl) {
1525 return;
1526 }
1527
1528 igmp = group->group_igmp_sock;
1529 ifp = igmp->interface;
1530 pim_ifp = ifp->info;
1531 ifname = ifp->name;
1532
Leonard Herve236b0152009-08-11 15:51:52 -03001533 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001534 lmqc = igmp->querier_robustness_variable;
1535 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1536
1537 if (PIM_DEBUG_IGMP_TRACE) {
1538 char group_str[100];
1539 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1540 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1541 __PRETTY_FUNCTION__,
1542 group_str, ifname,
1543 lmqc, lmqi_dsec, lmqt_msec);
1544 }
1545
1546 zassert(group->group_filtermode_isexcl);
1547
1548 igmp_group_timer_on(group, lmqt_msec, ifname);
1549}
1550
1551void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1552{
1553 struct igmp_group *group;
1554 struct igmp_sock *igmp;
1555 struct interface *ifp;
1556 struct pim_interface *pim_ifp;
1557 char *ifname;
1558 int lmqi_dsec; /* Last Member Query Interval */
1559 int lmqc; /* Last Member Query Count */
1560 int lmqt_msec; /* Last Member Query Time */
1561
1562 group = source->source_group;
1563 igmp = group->group_igmp_sock;
1564 ifp = igmp->interface;
1565 pim_ifp = ifp->info;
1566 ifname = ifp->name;
1567
Leonard Herve236b0152009-08-11 15:51:52 -03001568 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001569 lmqc = igmp->querier_robustness_variable;
1570 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1571
1572 if (PIM_DEBUG_IGMP_TRACE) {
1573 char group_str[100];
1574 char source_str[100];
1575 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1576 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1577 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1578 __PRETTY_FUNCTION__,
1579 group_str, source_str, ifname,
1580 lmqc, lmqi_dsec, lmqt_msec);
1581 }
1582
1583 igmp_source_timer_on(group, source, lmqt_msec);
1584}
1585
1586/*
1587 Copy sources to message:
1588
1589 struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
1590 if (num_sources > 0) {
1591 struct listnode *node;
1592 struct igmp_source *src;
1593 int i = 0;
1594
1595 for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
1596 sources[i++] = src->source_addr;
1597 }
1598 }
1599*/
1600void pim_igmp_send_membership_query(struct igmp_group *group,
1601 int fd,
1602 const char *ifname,
1603 char *query_buf,
1604 int query_buf_size,
1605 int num_sources,
1606 struct in_addr dst_addr,
1607 struct in_addr group_addr,
1608 int query_max_response_time_dsec,
1609 uint8_t s_flag,
1610 uint8_t querier_robustness_variable,
1611 uint16_t querier_query_interval)
1612{
1613 ssize_t msg_size;
1614 uint8_t max_resp_code;
1615 uint8_t qqic;
1616 ssize_t sent;
1617 struct sockaddr_in to;
1618 socklen_t tolen;
1619 uint16_t checksum;
1620
1621 zassert(num_sources >= 0);
1622
1623 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1624 if (msg_size > query_buf_size) {
David Lamparter5c697982012-02-16 04:47:56 +01001625 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
Everton Marques871dbcf2009-08-11 15:43:05 -03001626 __FILE__, __PRETTY_FUNCTION__,
1627 msg_size, query_buf_size);
1628 return;
1629 }
1630
1631 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1632 zassert((s_flag == 0) || (s_flag == 1));
1633
1634 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1635 qqic = igmp_msg_encode16to8(querier_query_interval);
1636
1637 /*
1638 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1639
1640 If non-zero, the QRV field contains the [Robustness Variable]
1641 value used by the querier, i.e., the sender of the Query. If the
1642 querier's [Robustness Variable] exceeds 7, the maximum value of
1643 the QRV field, the QRV is set to zero.
1644 */
1645 if (querier_robustness_variable > 7) {
1646 querier_robustness_variable = 0;
1647 }
1648
1649 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1650 query_buf[1] = max_resp_code;
1651 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
1652 *(struct in_addr *)(query_buf + 4) = group_addr;
1653 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1654 query_buf[9] = qqic;
1655 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
1656
1657 checksum = pim_inet_checksum(query_buf, msg_size);
1658 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
1659
1660 if (PIM_DEBUG_IGMP_PACKETS) {
1661 char dst_str[100];
1662 char group_str[100];
1663 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1664 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001665 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",
Everton Marques871dbcf2009-08-11 15:43:05 -03001666 __PRETTY_FUNCTION__,
1667 dst_str, ifname, group_str, num_sources,
1668 msg_size, s_flag, querier_robustness_variable,
1669 querier_query_interval, qqic, checksum);
1670 }
1671
1672#if 0
1673 memset(&to, 0, sizeof(to));
1674#endif
1675 to.sin_family = AF_INET;
1676 to.sin_addr = dst_addr;
1677#if 0
1678 to.sin_port = htons(0);
1679#endif
1680 tolen = sizeof(to);
1681
1682 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, &to, tolen);
1683 if (sent != (ssize_t) msg_size) {
1684 int e = errno;
1685 char dst_str[100];
1686 char group_str[100];
1687 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1688 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1689 if (sent < 0) {
David Lamparter5c697982012-02-16 04:47:56 +01001690 zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
Everton Marques871dbcf2009-08-11 15:43:05 -03001691 __PRETTY_FUNCTION__,
1692 dst_str, ifname, group_str, msg_size,
Everton Marquese96f0af2009-08-11 15:48:02 -03001693 e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -03001694 }
1695 else {
David Lamparter5c697982012-02-16 04:47:56 +01001696 zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
Everton Marques871dbcf2009-08-11 15:43:05 -03001697 __PRETTY_FUNCTION__,
1698 dst_str, ifname, group_str,
1699 msg_size, sent);
1700 }
1701 return;
1702 }
1703
1704 /*
1705 s_flag sanity test: s_flag must be set for general queries
1706
1707 RFC 3376: 6.6.1. Timer Updates
1708
1709 When a router sends or receives a query with a clear Suppress
1710 Router-Side Processing flag, it must update its timers to reflect
1711 the correct timeout values for the group or sources being queried.
1712
1713 General queries don't trigger timer update.
1714 */
1715 if (!s_flag) {
1716 /* general query? */
1717 if (PIM_INADDR_IS_ANY(group_addr)) {
1718 char dst_str[100];
1719 char group_str[100];
1720 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1721 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1722 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1723 __PRETTY_FUNCTION__,
1724 dst_str, ifname, group_str, num_sources);
1725 }
1726 }
1727
1728}