blob: 3657f2f946ce6aef0b49a706416b114d58ee622b [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
Everton Marques24e3a9b2014-09-30 19:14:19 -0300391/*
392 igmp_source_delete: stop fowarding, and delete the source
393 igmp_source_forward_stop: stop fowarding, but keep the source
394*/
Everton Marques871dbcf2009-08-11 15:43:05 -0300395void igmp_source_delete(struct igmp_source *source)
396{
397 struct igmp_group *group;
398
399 group = source->source_group;
400
401 if (PIM_DEBUG_IGMP_TRACE) {
402 char group_str[100];
403 char source_str[100];
404 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
405 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
406 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
407 source_str, group_str,
408 group->group_igmp_sock->fd,
409 group->group_igmp_sock->interface->name);
410 }
411
412 source_timer_off(group, source);
413 igmp_source_forward_stop(source);
414
Everton Marquesbb61be22014-08-22 15:40:02 -0300415 /* sanity check that forwarding has been disabled */
416 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
417 char group_str[100];
418 char source_str[100];
419 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
420 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
421 zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
422 __PRETTY_FUNCTION__,
423 source_str, group_str,
424 group->group_igmp_sock->fd,
425 group->group_igmp_sock->interface->name);
426 /* warning only */
427 }
Everton Marques871dbcf2009-08-11 15:43:05 -0300428
429 source_channel_oil_detach(source);
430
431 /*
432 notice that listnode_delete() can't be moved
433 into igmp_source_free() because the later is
434 called by list_delete_all_node()
435 */
436 listnode_delete(group->group_source_list, source);
437
438 igmp_source_free(source);
439
440 if (group->group_filtermode_isexcl) {
441 group_exclude_fwd_anysrc_ifempty(group);
442 }
443}
444
445static void source_delete_by_flag(struct list *source_list)
446{
447 struct listnode *src_node;
448 struct listnode *src_nextnode;
449 struct igmp_source *src;
450
451 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
452 if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
453 igmp_source_delete(src);
454}
455
456void igmp_source_delete_expired(struct list *source_list)
457{
458 struct listnode *src_node;
459 struct listnode *src_nextnode;
460 struct igmp_source *src;
461
462 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
463 if (!src->t_source_timer)
464 igmp_source_delete(src);
465}
466
467struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
468 struct in_addr src_addr)
469{
470 struct listnode *src_node;
471 struct igmp_source *src;
472
473 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
474 if (src_addr.s_addr == src->source_addr.s_addr)
475 return src;
476
477 return 0;
478}
479
480static struct igmp_source *source_new(struct igmp_group *group,
481 struct in_addr src_addr,
482 const char *ifname)
483{
484 struct igmp_source *src;
485
486 if (PIM_DEBUG_IGMP_TRACE) {
487 char group_str[100];
488 char source_str[100];
489 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
490 pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
491 zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
492 source_str, group_str,
493 group->group_igmp_sock->fd,
494 ifname);
495 }
496
497 src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
498 if (!src) {
499 zlog_warn("%s %s: XMALLOC() failure",
500 __FILE__, __PRETTY_FUNCTION__);
501 return 0; /* error, not found, could not create */
502 }
503
504 src->t_source_timer = 0;
505 src->source_group = group; /* back pointer */
506 src->source_addr = src_addr;
507 src->source_creation = pim_time_monotonic_sec();
508 src->source_flags = 0;
509 src->source_query_retransmit_count = 0;
510 src->source_channel_oil = 0;
511
512 listnode_add(group->group_source_list, src);
513
514 zassert(!src->t_source_timer); /* source timer == 0 */
515
516 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
517 igmp_anysource_forward_stop(group);
518
519 return src;
520}
521
522static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
523 struct igmp_group *group,
524 struct in_addr src_addr,
525 const char *ifname)
526{
527 struct igmp_source *src;
528
529 src = igmp_find_source_by_addr(group, src_addr);
530 if (src) {
531 return src;
532 }
533
534 src = source_new(group, src_addr, ifname);
535 if (!src) {
536 return 0;
537 }
538
539 return src;
540}
541
542static void allow(struct igmp_sock *igmp, struct in_addr from,
543 struct in_addr group_addr,
544 int num_sources, struct in_addr *sources)
545{
546 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300547 struct igmp_group *group;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200548 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300549
Everton Marques871dbcf2009-08-11 15:43:05 -0300550 /* non-existant group is created as INCLUDE {empty} */
551 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
552 if (!group) {
553 return;
554 }
555
556 /* scan received sources */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200557 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300558 struct igmp_source *source;
559 struct in_addr *src_addr;
560
561 src_addr = sources + i;
562
563 source = add_source_by_addr(igmp, group, *src_addr, ifp->name);
564 if (!source) {
565 continue;
566 }
567
568 /*
569 RFC 3376: 6.4.1. Reception of Current-State Records
570
571 When receiving IS_IN reports for groups in EXCLUDE mode is
572 sources should be moved from set with (timers = 0) to set with
573 (timers > 0).
574
575 igmp_source_reset_gmi() below, resetting the source timers to
576 GMI, accomplishes this.
577 */
578 igmp_source_reset_gmi(igmp, group, source);
579
580 } /* scan received sources */
581}
582
583void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
584 struct in_addr group_addr,
585 int num_sources, struct in_addr *sources)
586{
587 on_trace(__PRETTY_FUNCTION__,
588 igmp->interface, from, group_addr, num_sources, sources);
589
590 allow(igmp, from, group_addr, num_sources, sources);
591}
592
593static void isex_excl(struct igmp_group *group,
594 int num_sources, struct in_addr *sources)
595{
Leonard Herve942b0fd2009-08-14 15:49:06 +0200596 int i;
597
Everton Marques871dbcf2009-08-11 15:43:05 -0300598 /* EXCLUDE mode */
599 zassert(group->group_filtermode_isexcl);
600
601 /* E.1: set deletion flag for known sources (X,Y) */
602 source_mark_delete_flag(group->group_source_list);
603
604 /* scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200605 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300606 struct igmp_source *source;
607 struct in_addr *src_addr;
608
609 src_addr = sources + i;
610
611 /* E.2: lookup reported source from (A) in (X,Y) */
612 source = igmp_find_source_by_addr(group, *src_addr);
613 if (source) {
614 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
615 IGMP_SOURCE_DONT_DELETE(source->source_flags);
616 }
617 else {
618 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
619 source = source_new(group, *src_addr,
620 group->group_igmp_sock->interface->name);
621 if (!source) {
622 /* ugh, internal malloc failure, skip source */
623 continue;
624 }
625 zassert(!source->t_source_timer); /* timer == 0 */
626 igmp_source_reset_gmi(group->group_igmp_sock, group, source);
627 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
628 }
629
630 } /* scan received sources */
631
632 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
633 source_delete_by_flag(group->group_source_list);
634}
635
636static void isex_incl(struct igmp_group *group,
637 int num_sources, struct in_addr *sources)
638{
Leonard Herve942b0fd2009-08-14 15:49:06 +0200639 int i;
640
Everton Marques871dbcf2009-08-11 15:43:05 -0300641 /* INCLUDE mode */
642 zassert(!group->group_filtermode_isexcl);
643
644 /* I.1: set deletion flag for known sources (A) */
645 source_mark_delete_flag(group->group_source_list);
646
647 /* scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200648 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300649 struct igmp_source *source;
650 struct in_addr *src_addr;
651
652 src_addr = sources + i;
653
654 /* I.2: lookup reported source (B) */
655 source = igmp_find_source_by_addr(group, *src_addr);
656 if (source) {
657 /* I.3: if found, clear deletion flag (A*B) */
658 IGMP_SOURCE_DONT_DELETE(source->source_flags);
659 }
660 else {
661 /* I.4: if not found, create source with timer=0 (B-A) */
662 source = source_new(group, *src_addr,
663 group->group_igmp_sock->interface->name);
664 if (!source) {
665 /* ugh, internal malloc failure, skip source */
666 continue;
667 }
668 zassert(!source->t_source_timer); /* (B-A) timer=0 */
669 }
670
671 } /* scan received sources */
672
673 /* I.5: delete all sources marked with deletion flag (A-B) */
674 source_delete_by_flag(group->group_source_list);
675
676 group->group_filtermode_isexcl = 1; /* boolean=true */
677
678 zassert(group->group_filtermode_isexcl);
679
680 group_exclude_fwd_anysrc_ifempty(group);
681}
682
683void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
684 struct in_addr group_addr,
685 int num_sources, struct in_addr *sources)
686{
687 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300688 struct igmp_group *group;
689
690 on_trace(__PRETTY_FUNCTION__,
691 ifp, from, group_addr, num_sources, sources);
692
Everton Marques871dbcf2009-08-11 15:43:05 -0300693 /* non-existant group is created as INCLUDE {empty} */
694 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
695 if (!group) {
696 return;
697 }
698
699 if (group->group_filtermode_isexcl) {
700 /* EXCLUDE mode */
701 isex_excl(group, num_sources, sources);
702 }
703 else {
704 /* INCLUDE mode */
705 isex_incl(group, num_sources, sources);
706 zassert(group->group_filtermode_isexcl);
707 }
708
709 zassert(group->group_filtermode_isexcl);
710
711 igmp_group_reset_gmi(group);
712}
713
714static void toin_incl(struct igmp_group *group,
715 int num_sources, struct in_addr *sources)
716{
717 struct igmp_sock *igmp = group->group_igmp_sock;
718 int num_sources_tosend = listcount(group->group_source_list);
Leonard Herve942b0fd2009-08-14 15:49:06 +0200719 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300720
721 /* Set SEND flag for all known sources (A) */
722 source_mark_send_flag(group->group_source_list);
723
724 /* Scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200725 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300726 struct igmp_source *source;
727 struct in_addr *src_addr;
728
729 src_addr = sources + i;
730
731 /* Lookup reported source (B) */
732 source = igmp_find_source_by_addr(group, *src_addr);
733 if (source) {
734 /* If found, clear SEND flag (A*B) */
735 IGMP_SOURCE_DONT_SEND(source->source_flags);
736 --num_sources_tosend;
737 }
738 else {
739 /* If not found, create new source */
740 source = source_new(group, *src_addr,
741 group->group_igmp_sock->interface->name);
742 if (!source) {
743 /* ugh, internal malloc failure, skip source */
744 continue;
745 }
746 }
747
748 /* (B)=GMI */
749 igmp_source_reset_gmi(igmp, group, source);
750 }
751
752 /* Send sources marked with SEND flag: Q(G,A-B) */
753 if (num_sources_tosend > 0) {
754 source_query_send_by_flag(group, num_sources_tosend);
755 }
756}
757
758static void toin_excl(struct igmp_group *group,
759 int num_sources, struct in_addr *sources)
760{
761 struct igmp_sock *igmp = group->group_igmp_sock;
762 int num_sources_tosend;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200763 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300764
765 /* Set SEND flag for X (sources with timer > 0) */
766 num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
767
768 /* Scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200769 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300770 struct igmp_source *source;
771 struct in_addr *src_addr;
772
773 src_addr = sources + i;
774
775 /* Lookup reported source (A) */
776 source = igmp_find_source_by_addr(group, *src_addr);
777 if (source) {
778 if (source->t_source_timer) {
779 /* If found and timer running, clear SEND flag (X*A) */
780 IGMP_SOURCE_DONT_SEND(source->source_flags);
781 --num_sources_tosend;
782 }
783 }
784 else {
785 /* If not found, create new source */
786 source = source_new(group, *src_addr,
787 group->group_igmp_sock->interface->name);
788 if (!source) {
789 /* ugh, internal malloc failure, skip source */
790 continue;
791 }
792 }
793
794 /* (A)=GMI */
795 igmp_source_reset_gmi(igmp, group, source);
796 }
797
798 /* Send sources marked with SEND flag: Q(G,X-A) */
799 if (num_sources_tosend > 0) {
800 source_query_send_by_flag(group, num_sources_tosend);
801 }
802
803 /* Send Q(G) */
804 group_query_send(group);
805}
806
807void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
808 struct in_addr group_addr,
809 int num_sources, struct in_addr *sources)
810{
811 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300812 struct igmp_group *group;
813
814 on_trace(__PRETTY_FUNCTION__,
815 ifp, from, group_addr, num_sources, sources);
816
Everton Marques871dbcf2009-08-11 15:43:05 -0300817 /* non-existant group is created as INCLUDE {empty} */
818 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
819 if (!group) {
820 return;
821 }
822
823 if (group->group_filtermode_isexcl) {
824 /* EXCLUDE mode */
825 toin_excl(group, num_sources, sources);
826 }
827 else {
828 /* INCLUDE mode */
829 toin_incl(group, num_sources, sources);
830 }
831}
832
833static void toex_incl(struct igmp_group *group,
834 int num_sources, struct in_addr *sources)
835{
836 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200837 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300838
839 zassert(!group->group_filtermode_isexcl);
840
841 /* Set DELETE flag for all known sources (A) */
842 source_mark_delete_flag(group->group_source_list);
843
844 /* Clear off SEND flag from all known sources (A) */
845 source_clear_send_flag(group->group_source_list);
846
847 /* Scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200848 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300849 struct igmp_source *source;
850 struct in_addr *src_addr;
851
852 src_addr = sources + i;
853
854 /* Lookup reported source (B) */
855 source = igmp_find_source_by_addr(group, *src_addr);
856 if (source) {
857 /* If found, clear deletion flag: (A*B) */
858 IGMP_SOURCE_DONT_DELETE(source->source_flags);
859 /* and set SEND flag (A*B) */
860 IGMP_SOURCE_DO_SEND(source->source_flags);
861 ++num_sources_tosend;
862 }
863 else {
864 /* If source not found, create source with timer=0: (B-A)=0 */
865 source = source_new(group, *src_addr,
866 group->group_igmp_sock->interface->name);
867 if (!source) {
868 /* ugh, internal malloc failure, skip source */
869 continue;
870 }
871 zassert(!source->t_source_timer); /* (B-A) timer=0 */
872 }
873
874 } /* Scan received sources (B) */
875
876 group->group_filtermode_isexcl = 1; /* boolean=true */
877
878 /* Delete all sources marked with DELETE flag (A-B) */
879 source_delete_by_flag(group->group_source_list);
880
881 /* Send sources marked with SEND flag: Q(G,A*B) */
882 if (num_sources_tosend > 0) {
883 source_query_send_by_flag(group, num_sources_tosend);
884 }
885
886 zassert(group->group_filtermode_isexcl);
887
888 group_exclude_fwd_anysrc_ifempty(group);
889}
890
891static void toex_excl(struct igmp_group *group,
892 int num_sources, struct in_addr *sources)
893{
894 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200895 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300896
897 /* set DELETE flag for all known sources (X,Y) */
898 source_mark_delete_flag(group->group_source_list);
899
900 /* clear off SEND flag from all known sources (X,Y) */
901 source_clear_send_flag(group->group_source_list);
902
903 /* scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200904 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300905 struct igmp_source *source;
906 struct in_addr *src_addr;
907
908 src_addr = sources + i;
909
910 /* lookup reported source (A) in known sources (X,Y) */
911 source = igmp_find_source_by_addr(group, *src_addr);
912 if (source) {
913 /* if found, clear off DELETE flag from reported source (A) */
914 IGMP_SOURCE_DONT_DELETE(source->source_flags);
915 }
916 else {
917 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
918 long group_timer_msec;
919 source = source_new(group, *src_addr,
920 group->group_igmp_sock->interface->name);
921 if (!source) {
922 /* ugh, internal malloc failure, skip source */
923 continue;
924 }
925
926 zassert(!source->t_source_timer); /* timer == 0 */
927 group_timer_msec = igmp_group_timer_remain_msec(group);
928 igmp_source_timer_on(group, source, group_timer_msec);
929 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
930
931 /* make sure source is created with DELETE flag unset */
932 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
933 }
934
935 /* make sure reported source has DELETE flag unset */
936 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
937
938 if (source->t_source_timer) {
939 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
940 IGMP_SOURCE_DO_SEND(source->source_flags);
941 ++num_sources_tosend;
942 }
943
944 } /* scan received sources (A) */
945
946 /*
947 delete all sources marked with DELETE flag:
948 Delete (X-A)
949 Delete (Y-A)
950 */
951 source_delete_by_flag(group->group_source_list);
952
953 /* send sources marked with SEND flag: Q(G,A-Y) */
954 if (num_sources_tosend > 0) {
955 source_query_send_by_flag(group, num_sources_tosend);
956 }
957}
958
959void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
960 struct in_addr group_addr,
961 int num_sources, struct in_addr *sources)
962{
963 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300964 struct igmp_group *group;
965
966 on_trace(__PRETTY_FUNCTION__,
967 ifp, from, group_addr, num_sources, sources);
968
Everton Marques871dbcf2009-08-11 15:43:05 -0300969 /* non-existant group is created as INCLUDE {empty} */
970 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
971 if (!group) {
972 return;
973 }
974
975 if (group->group_filtermode_isexcl) {
976 /* EXCLUDE mode */
977 toex_excl(group, num_sources, sources);
978 }
979 else {
980 /* INCLUDE mode */
981 toex_incl(group, num_sources, sources);
982 zassert(group->group_filtermode_isexcl);
983 }
984 zassert(group->group_filtermode_isexcl);
985
986 /* Group Timer=GMI */
987 igmp_group_reset_gmi(group);
988}
989
990void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
991 struct in_addr group_addr,
992 int num_sources, struct in_addr *sources)
993{
994 on_trace(__PRETTY_FUNCTION__,
995 igmp->interface, from, group_addr, num_sources, sources);
996
997 allow(igmp, from, group_addr, num_sources, sources);
998}
999
1000/*
1001 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1002
1003 When transmitting a group specific query, if the group timer is
1004 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1005 in the query message.
1006*/
1007static void group_retransmit_group(struct igmp_group *group)
1008{
1009 char query_buf[PIM_IGMP_BUFSIZE_WRITE];
1010 struct igmp_sock *igmp;
1011 struct pim_interface *pim_ifp;
1012 long lmqc; /* Last Member Query Count */
1013 long lmqi_msec; /* Last Member Query Interval */
1014 long lmqt_msec; /* Last Member Query Time */
1015 int s_flag;
1016
1017 igmp = group->group_igmp_sock;
1018 pim_ifp = igmp->interface->info;
1019
1020 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001021 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001022 lmqt_msec = lmqc * lmqi_msec;
1023
1024 /*
1025 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1026
1027 When transmitting a group specific query, if the group timer is
1028 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1029 in the query message.
1030 */
1031 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1032
1033 if (PIM_DEBUG_IGMP_TRACE) {
1034 char group_str[100];
1035 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1036 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1037 group_str, igmp->interface->name, s_flag,
1038 group->group_specific_query_retransmit_count);
1039 }
1040
1041 /*
1042 RFC3376: 4.1.12. IP Destination Addresses for Queries
1043
1044 Group-Specific and Group-and-Source-Specific Queries are sent with
1045 an IP destination address equal to the multicast address of
1046 interest.
1047 */
1048
1049 pim_igmp_send_membership_query(group,
1050 igmp->fd,
1051 igmp->interface->name,
1052 query_buf,
1053 sizeof(query_buf),
1054 0 /* num_sources_tosend */,
1055 group->group_addr /* dst_addr */,
1056 group->group_addr /* group_addr */,
Leonard Herve236b0152009-08-11 15:51:52 -03001057 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001058 s_flag,
1059 igmp->querier_robustness_variable,
1060 igmp->querier_query_interval);
1061}
1062
1063/*
1064 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1065
1066 When building a group and source specific query for a group G, two
1067 separate query messages are sent for the group. The first one has
1068 the "Suppress Router-Side Processing" bit set and contains all the
1069 sources with retransmission state and timers greater than LMQT. The
1070 second has the "Suppress Router-Side Processing" bit clear and
1071 contains all the sources with retransmission state and timers lower
1072 or equal to LMQT. If either of the two calculated messages does not
1073 contain any sources, then its transmission is suppressed.
1074 */
1075static int group_retransmit_sources(struct igmp_group *group,
1076 int send_with_sflag_set)
1077{
1078 struct igmp_sock *igmp;
1079 struct pim_interface *pim_ifp;
1080 long lmqc; /* Last Member Query Count */
1081 long lmqi_msec; /* Last Member Query Interval */
1082 long lmqt_msec; /* Last Member Query Time */
1083 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1084 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1085 int query_buf1_max_sources;
1086 int query_buf2_max_sources;
1087 struct in_addr *source_addr1;
1088 struct in_addr *source_addr2;
1089 int num_sources_tosend1;
1090 int num_sources_tosend2;
1091 struct listnode *src_node;
1092 struct igmp_source *src;
1093 int num_retransmit_sources_left = 0;
1094
1095 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1096 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1097
1098 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1099 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1100
1101 igmp = group->group_igmp_sock;
1102 pim_ifp = igmp->interface->info;
1103
1104 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001105 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001106 lmqt_msec = lmqc * lmqi_msec;
1107
1108 /* Scan all group sources */
1109 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1110
1111 /* Source has retransmission state? */
1112 if (src->source_query_retransmit_count < 1)
1113 continue;
1114
1115 if (--src->source_query_retransmit_count > 0) {
1116 ++num_retransmit_sources_left;
1117 }
1118
1119 /* Copy source address into appropriate query buffer */
1120 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1121 *source_addr1 = src->source_addr;
1122 ++source_addr1;
1123 }
1124 else {
1125 *source_addr2 = src->source_addr;
1126 ++source_addr2;
1127 }
1128
1129 }
1130
1131 num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1132 num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1133
1134 if (PIM_DEBUG_IGMP_TRACE) {
1135 char group_str[100];
1136 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1137 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",
1138 group_str, igmp->interface->name,
1139 num_sources_tosend1,
1140 num_sources_tosend2,
1141 send_with_sflag_set,
1142 num_retransmit_sources_left);
1143 }
1144
1145 if (num_sources_tosend1 > 0) {
1146 /*
1147 Send group-and-source-specific query with s_flag set and all
1148 sources with timers greater than LMQT.
1149 */
1150
1151 if (send_with_sflag_set) {
1152
1153 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1154 if (num_sources_tosend1 > query_buf1_max_sources) {
1155 char group_str[100];
1156 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001157 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 -03001158 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1159 num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
1160 }
1161 else {
1162 /*
1163 RFC3376: 4.1.12. IP Destination Addresses for Queries
1164
1165 Group-Specific and Group-and-Source-Specific Queries are sent with
1166 an IP destination address equal to the multicast address of
1167 interest.
1168 */
1169
1170 pim_igmp_send_membership_query(group,
1171 igmp->fd,
1172 igmp->interface->name,
1173 query_buf1,
1174 sizeof(query_buf1),
1175 num_sources_tosend1,
1176 group->group_addr,
1177 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001178 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001179 1 /* s_flag */,
1180 igmp->querier_robustness_variable,
1181 igmp->querier_query_interval);
1182
1183 }
1184
1185 } /* send_with_sflag_set */
1186
1187 }
1188
1189 if (num_sources_tosend2 > 0) {
1190 /*
1191 Send group-and-source-specific query with s_flag clear and all
1192 sources with timers lower or equal to LMQT.
1193 */
1194
1195 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1196 if (num_sources_tosend2 > query_buf2_max_sources) {
1197 char group_str[100];
1198 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001199 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 -03001200 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1201 num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
1202 }
1203 else {
1204 /*
1205 RFC3376: 4.1.12. IP Destination Addresses for Queries
1206
1207 Group-Specific and Group-and-Source-Specific Queries are sent with
1208 an IP destination address equal to the multicast address of
1209 interest.
1210 */
1211
1212 pim_igmp_send_membership_query(group,
1213 igmp->fd,
1214 igmp->interface->name,
1215 query_buf2,
1216 sizeof(query_buf2),
1217 num_sources_tosend2,
1218 group->group_addr,
1219 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001220 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001221 0 /* s_flag */,
1222 igmp->querier_robustness_variable,
1223 igmp->querier_query_interval);
1224
1225 }
1226 }
1227
1228 return num_retransmit_sources_left;
1229}
1230
1231static int igmp_group_retransmit(struct thread *t)
1232{
1233 struct igmp_group *group;
1234 int num_retransmit_sources_left;
1235 int send_with_sflag_set; /* boolean */
1236
1237 zassert(t);
1238 group = THREAD_ARG(t);
1239 zassert(group);
1240
1241 if (PIM_DEBUG_IGMP_TRACE) {
1242 char group_str[100];
1243 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1244 zlog_debug("group_retransmit_timer: group %s on %s",
1245 group_str, group->group_igmp_sock->interface->name);
1246 }
1247
1248 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1249 if (group->group_specific_query_retransmit_count > 0) {
1250
1251 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1252 group_retransmit_group(group);
1253 --group->group_specific_query_retransmit_count;
1254
1255 /*
1256 RFC3376: 6.6.3.2
1257 If a group specific query is scheduled to be transmitted at the
1258 same time as a group and source specific query for the same group,
1259 then transmission of the group and source specific message with the
1260 "Suppress Router-Side Processing" bit set may be suppressed.
1261 */
1262 send_with_sflag_set = 0; /* boolean=false */
1263 }
1264 else {
1265 send_with_sflag_set = 1; /* boolean=true */
1266 }
1267
1268 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1269 num_retransmit_sources_left = group_retransmit_sources(group,
1270 send_with_sflag_set);
1271
1272 group->t_group_query_retransmit_timer = 0;
1273
1274 /*
1275 Keep group retransmit timer running if there is any retransmit
1276 counter pending
1277 */
1278 if ((num_retransmit_sources_left > 0) ||
1279 (group->group_specific_query_retransmit_count > 0)) {
1280 group_retransmit_timer_on(group);
1281 }
1282
1283 return 0;
1284}
1285
1286/*
1287 group_retransmit_timer_on:
1288 if group retransmit timer isn't running, starts it;
1289 otherwise, do nothing
1290*/
1291static void group_retransmit_timer_on(struct igmp_group *group)
1292{
1293 struct igmp_sock *igmp;
1294 struct pim_interface *pim_ifp;
1295 long lmqi_msec; /* Last Member Query Interval */
1296
1297 /* if group retransmit timer is running, do nothing */
1298 if (group->t_group_query_retransmit_timer) {
1299 return;
1300 }
1301
1302 igmp = group->group_igmp_sock;
1303 pim_ifp = igmp->interface->info;
1304
Leonard Herve236b0152009-08-11 15:51:52 -03001305 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001306
1307 if (PIM_DEBUG_IGMP_TRACE) {
1308 char group_str[100];
1309 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1310 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1311 lmqi_msec / 1000,
1312 lmqi_msec % 1000,
1313 group_str,
1314 igmp->interface->name);
1315 }
1316
1317 THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
1318 igmp_group_retransmit,
1319 group, lmqi_msec);
1320}
1321
1322static long igmp_group_timer_remain_msec(struct igmp_group *group)
1323{
1324 return pim_time_timer_remain_msec(group->t_group_timer);
1325}
1326
1327static long igmp_source_timer_remain_msec(struct igmp_source *source)
1328{
1329 return pim_time_timer_remain_msec(source->t_source_timer);
1330}
1331
1332/*
1333 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1334*/
1335static void group_query_send(struct igmp_group *group)
1336{
1337 long lmqc; /* Last Member Query Count */
1338
1339 lmqc = group->group_igmp_sock->querier_robustness_variable;
1340
1341 /* lower group timer to lmqt */
1342 igmp_group_timer_lower_to_lmqt(group);
1343
1344 /* reset retransmission counter */
1345 group->group_specific_query_retransmit_count = lmqc;
1346
1347 /* immediately send group specific query (decrease retransmit counter by 1)*/
1348 group_retransmit_group(group);
1349
1350 /* make sure group retransmit timer is running */
1351 group_retransmit_timer_on(group);
1352}
1353
1354/*
1355 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1356*/
1357static void source_query_send_by_flag(struct igmp_group *group,
1358 int num_sources_tosend)
1359{
1360 struct igmp_sock *igmp;
1361 struct pim_interface *pim_ifp;
1362 struct listnode *src_node;
1363 struct igmp_source *src;
1364 long lmqc; /* Last Member Query Count */
1365 long lmqi_msec; /* Last Member Query Interval */
1366 long lmqt_msec; /* Last Member Query Time */
1367
1368 zassert(num_sources_tosend > 0);
1369
1370 igmp = group->group_igmp_sock;
1371 pim_ifp = igmp->interface->info;
1372
1373 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001374 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001375 lmqt_msec = lmqc * lmqi_msec;
1376
1377 /*
1378 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1379
1380 (...) for each of the sources in X of group G, with source timer larger
1381 than LMQT:
1382 o Set number of retransmissions for each source to [Last Member
1383 Query Count].
1384 o Lower source timer to LMQT.
1385 */
1386 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1387 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1388 /* source "src" in X of group G */
1389 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1390 src->source_query_retransmit_count = lmqc;
1391 igmp_source_timer_lower_to_lmqt(src);
1392 }
1393 }
1394 }
1395
1396 /* send group-and-source specific queries */
1397 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1398
1399 /* make sure group retransmit timer is running */
1400 group_retransmit_timer_on(group);
1401}
1402
1403static void block_excl(struct igmp_group *group,
1404 int num_sources, struct in_addr *sources)
1405{
1406 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +02001407 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -03001408
1409 /* 1. clear off SEND flag from all known sources (X,Y) */
1410 source_clear_send_flag(group->group_source_list);
1411
1412 /* 2. scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +02001413 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -03001414 struct igmp_source *source;
1415 struct in_addr *src_addr;
1416
1417 src_addr = sources + i;
1418
1419 /* lookup reported source (A) in known sources (X,Y) */
1420 source = igmp_find_source_by_addr(group, *src_addr);
1421 if (!source) {
1422 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1423 long group_timer_msec;
1424 source = source_new(group, *src_addr,
1425 group->group_igmp_sock->interface->name);
1426 if (!source) {
1427 /* ugh, internal malloc failure, skip source */
1428 continue;
1429 }
1430
1431 zassert(!source->t_source_timer); /* timer == 0 */
1432 group_timer_msec = igmp_group_timer_remain_msec(group);
1433 igmp_source_timer_on(group, source, group_timer_msec);
1434 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1435 }
1436
1437 if (source->t_source_timer) {
1438 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1439 IGMP_SOURCE_DO_SEND(source->source_flags);
1440 ++num_sources_tosend;
1441 }
1442 }
1443
1444 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1445 if (num_sources_tosend > 0) {
1446 source_query_send_by_flag(group, num_sources_tosend);
1447 }
1448}
1449
1450static void block_incl(struct igmp_group *group,
1451 int num_sources, struct in_addr *sources)
1452{
1453 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +02001454 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -03001455
1456 /* 1. clear off SEND flag from all known sources (B) */
1457 source_clear_send_flag(group->group_source_list);
1458
1459 /* 2. scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +02001460 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -03001461 struct igmp_source *source;
1462 struct in_addr *src_addr;
1463
1464 src_addr = sources + i;
1465
1466 /* lookup reported source (A) in known sources (B) */
1467 source = igmp_find_source_by_addr(group, *src_addr);
1468 if (source) {
1469 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1470 IGMP_SOURCE_DO_SEND(source->source_flags);
1471 ++num_sources_tosend;
1472 }
1473 }
1474
1475 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1476 if (num_sources_tosend > 0) {
1477 source_query_send_by_flag(group, num_sources_tosend);
1478 }
1479}
1480
1481void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1482 struct in_addr group_addr,
1483 int num_sources, struct in_addr *sources)
1484{
1485 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -03001486 struct igmp_group *group;
1487
1488 on_trace(__PRETTY_FUNCTION__,
1489 ifp, from, group_addr, num_sources, sources);
1490
Everton Marques871dbcf2009-08-11 15:43:05 -03001491 /* 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 */
Klemen Sladic3defeb32014-02-07 16:23:44 +13001652 memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
1653
Everton Marques871dbcf2009-08-11 15:43:05 -03001654 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1655 query_buf[9] = qqic;
1656 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
1657
Everton Marques93911262014-09-18 11:10:58 -03001658 checksum = in_cksum(query_buf, msg_size);
Everton Marques871dbcf2009-08-11 15:43:05 -03001659 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
1660
1661 if (PIM_DEBUG_IGMP_PACKETS) {
1662 char dst_str[100];
1663 char group_str[100];
1664 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1665 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001666 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 -03001667 __PRETTY_FUNCTION__,
1668 dst_str, ifname, group_str, num_sources,
1669 msg_size, s_flag, querier_robustness_variable,
1670 querier_query_interval, qqic, checksum);
1671 }
1672
1673#if 0
1674 memset(&to, 0, sizeof(to));
1675#endif
1676 to.sin_family = AF_INET;
1677 to.sin_addr = dst_addr;
1678#if 0
1679 to.sin_port = htons(0);
1680#endif
1681 tolen = sizeof(to);
1682
David Lampartera2c7f4b2015-03-03 21:03:52 +01001683 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1684 (struct sockaddr *)&to, tolen);
Everton Marques871dbcf2009-08-11 15:43:05 -03001685 if (sent != (ssize_t) msg_size) {
1686 int e = errno;
1687 char dst_str[100];
1688 char group_str[100];
1689 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1690 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1691 if (sent < 0) {
David Lamparter5c697982012-02-16 04:47:56 +01001692 zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
Everton Marques871dbcf2009-08-11 15:43:05 -03001693 __PRETTY_FUNCTION__,
1694 dst_str, ifname, group_str, msg_size,
Everton Marquese96f0af2009-08-11 15:48:02 -03001695 e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -03001696 }
1697 else {
David Lamparter5c697982012-02-16 04:47:56 +01001698 zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
Everton Marques871dbcf2009-08-11 15:43:05 -03001699 __PRETTY_FUNCTION__,
1700 dst_str, ifname, group_str,
1701 msg_size, sent);
1702 }
1703 return;
1704 }
1705
1706 /*
1707 s_flag sanity test: s_flag must be set for general queries
1708
1709 RFC 3376: 6.6.1. Timer Updates
1710
1711 When a router sends or receives a query with a clear Suppress
1712 Router-Side Processing flag, it must update its timers to reflect
1713 the correct timeout values for the group or sources being queried.
1714
1715 General queries don't trigger timer update.
1716 */
1717 if (!s_flag) {
1718 /* general query? */
1719 if (PIM_INADDR_IS_ANY(group_addr)) {
1720 char dst_str[100];
1721 char group_str[100];
1722 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1723 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1724 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1725 __PRETTY_FUNCTION__,
1726 dst_str, ifname, group_str, num_sources);
1727 }
1728 }
1729
1730}