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