blob: 8a32a3272953918bf1648e43786897bd785faf5c [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
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400480struct igmp_source *
481source_new (struct igmp_group *group,
482 struct in_addr src_addr)
Everton Marques871dbcf2009-08-11 15:43:05 -0300483{
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,
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400494 group->group_igmp_sock->interface->name);
Everton Marques871dbcf2009-08-11 15:43:05 -0300495 }
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
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400504 src->t_source_timer = NULL;
Everton Marques871dbcf2009-08-11 15:43:05 -0300505 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;
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400510 src->source_channel_oil = NULL;
Everton Marques871dbcf2009-08-11 15:43:05 -0300511
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,
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400524 struct in_addr src_addr)
Everton Marques871dbcf2009-08-11 15:43:05 -0300525{
526 struct igmp_source *src;
527
528 src = igmp_find_source_by_addr(group, src_addr);
529 if (src) {
530 return src;
531 }
532
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400533 src = source_new(group, src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300534 if (!src) {
535 return 0;
536 }
537
538 return src;
539}
540
541static void allow(struct igmp_sock *igmp, struct in_addr from,
542 struct in_addr group_addr,
543 int num_sources, struct in_addr *sources)
544{
Everton Marques871dbcf2009-08-11 15:43:05 -0300545 struct igmp_group *group;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200546 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300547
Everton Marques871dbcf2009-08-11 15:43:05 -0300548 /* non-existant group is created as INCLUDE {empty} */
Donald Sharpf8aef5f2016-06-02 02:20:29 -0400549 group = igmp_add_group_by_addr(igmp, group_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300550 if (!group) {
551 return;
552 }
553
554 /* scan received sources */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200555 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300556 struct igmp_source *source;
557 struct in_addr *src_addr;
558
559 src_addr = sources + i;
560
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400561 source = add_source_by_addr(igmp, group, *src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300562 if (!source) {
563 continue;
564 }
565
566 /*
567 RFC 3376: 6.4.1. Reception of Current-State Records
568
569 When receiving IS_IN reports for groups in EXCLUDE mode is
570 sources should be moved from set with (timers = 0) to set with
571 (timers > 0).
572
573 igmp_source_reset_gmi() below, resetting the source timers to
574 GMI, accomplishes this.
575 */
576 igmp_source_reset_gmi(igmp, group, source);
577
578 } /* scan received sources */
579}
580
581void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
582 struct in_addr group_addr,
583 int num_sources, struct in_addr *sources)
584{
585 on_trace(__PRETTY_FUNCTION__,
586 igmp->interface, from, group_addr, num_sources, sources);
587
588 allow(igmp, from, group_addr, num_sources, sources);
589}
590
591static void isex_excl(struct igmp_group *group,
592 int num_sources, struct in_addr *sources)
593{
Leonard Herve942b0fd2009-08-14 15:49:06 +0200594 int i;
595
Everton Marques871dbcf2009-08-11 15:43:05 -0300596 /* EXCLUDE mode */
597 zassert(group->group_filtermode_isexcl);
598
599 /* E.1: set deletion flag for known sources (X,Y) */
600 source_mark_delete_flag(group->group_source_list);
601
602 /* scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200603 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300604 struct igmp_source *source;
605 struct in_addr *src_addr;
606
607 src_addr = sources + i;
608
609 /* E.2: lookup reported source from (A) in (X,Y) */
610 source = igmp_find_source_by_addr(group, *src_addr);
611 if (source) {
612 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
613 IGMP_SOURCE_DONT_DELETE(source->source_flags);
614 }
615 else {
616 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400617 source = source_new(group, *src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300618 if (!source) {
619 /* ugh, internal malloc failure, skip source */
620 continue;
621 }
622 zassert(!source->t_source_timer); /* timer == 0 */
623 igmp_source_reset_gmi(group->group_igmp_sock, group, source);
624 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
625 }
626
627 } /* scan received sources */
628
629 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
630 source_delete_by_flag(group->group_source_list);
631}
632
633static void isex_incl(struct igmp_group *group,
634 int num_sources, struct in_addr *sources)
635{
Leonard Herve942b0fd2009-08-14 15:49:06 +0200636 int i;
637
Everton Marques871dbcf2009-08-11 15:43:05 -0300638 /* INCLUDE mode */
639 zassert(!group->group_filtermode_isexcl);
640
641 /* I.1: set deletion flag for known sources (A) */
642 source_mark_delete_flag(group->group_source_list);
643
644 /* scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200645 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300646 struct igmp_source *source;
647 struct in_addr *src_addr;
648
649 src_addr = sources + i;
650
651 /* I.2: lookup reported source (B) */
652 source = igmp_find_source_by_addr(group, *src_addr);
653 if (source) {
654 /* I.3: if found, clear deletion flag (A*B) */
655 IGMP_SOURCE_DONT_DELETE(source->source_flags);
656 }
657 else {
658 /* I.4: if not found, create source with timer=0 (B-A) */
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400659 source = source_new(group, *src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300660 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} */
Donald Sharpf8aef5f2016-06-02 02:20:29 -0400690 group = igmp_add_group_by_addr(igmp, group_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300691 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 */
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400736 source = source_new(group, *src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300737 if (!source) {
738 /* ugh, internal malloc failure, skip source */
739 continue;
740 }
741 }
742
743 /* (B)=GMI */
744 igmp_source_reset_gmi(igmp, group, source);
745 }
746
747 /* Send sources marked with SEND flag: Q(G,A-B) */
748 if (num_sources_tosend > 0) {
749 source_query_send_by_flag(group, num_sources_tosend);
750 }
751}
752
753static void toin_excl(struct igmp_group *group,
754 int num_sources, struct in_addr *sources)
755{
756 struct igmp_sock *igmp = group->group_igmp_sock;
757 int num_sources_tosend;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200758 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300759
760 /* Set SEND flag for X (sources with timer > 0) */
761 num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
762
763 /* Scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200764 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300765 struct igmp_source *source;
766 struct in_addr *src_addr;
767
768 src_addr = sources + i;
769
770 /* Lookup reported source (A) */
771 source = igmp_find_source_by_addr(group, *src_addr);
772 if (source) {
773 if (source->t_source_timer) {
774 /* If found and timer running, clear SEND flag (X*A) */
775 IGMP_SOURCE_DONT_SEND(source->source_flags);
776 --num_sources_tosend;
777 }
778 }
779 else {
780 /* If not found, create new source */
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400781 source = source_new(group, *src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300782 if (!source) {
783 /* ugh, internal malloc failure, skip source */
784 continue;
785 }
786 }
787
788 /* (A)=GMI */
789 igmp_source_reset_gmi(igmp, group, source);
790 }
791
792 /* Send sources marked with SEND flag: Q(G,X-A) */
793 if (num_sources_tosend > 0) {
794 source_query_send_by_flag(group, num_sources_tosend);
795 }
796
797 /* Send Q(G) */
798 group_query_send(group);
799}
800
801void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
802 struct in_addr group_addr,
803 int num_sources, struct in_addr *sources)
804{
805 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300806 struct igmp_group *group;
807
808 on_trace(__PRETTY_FUNCTION__,
809 ifp, from, group_addr, num_sources, sources);
810
Everton Marques871dbcf2009-08-11 15:43:05 -0300811 /* non-existant group is created as INCLUDE {empty} */
Donald Sharpf8aef5f2016-06-02 02:20:29 -0400812 group = igmp_add_group_by_addr(igmp, group_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300813 if (!group) {
814 return;
815 }
816
817 if (group->group_filtermode_isexcl) {
818 /* EXCLUDE mode */
819 toin_excl(group, num_sources, sources);
820 }
821 else {
822 /* INCLUDE mode */
823 toin_incl(group, num_sources, sources);
824 }
825}
826
827static void toex_incl(struct igmp_group *group,
828 int num_sources, struct in_addr *sources)
829{
830 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200831 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300832
833 zassert(!group->group_filtermode_isexcl);
834
835 /* Set DELETE flag for all known sources (A) */
836 source_mark_delete_flag(group->group_source_list);
837
838 /* Clear off SEND flag from all known sources (A) */
839 source_clear_send_flag(group->group_source_list);
840
841 /* Scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200842 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300843 struct igmp_source *source;
844 struct in_addr *src_addr;
845
846 src_addr = sources + i;
847
848 /* Lookup reported source (B) */
849 source = igmp_find_source_by_addr(group, *src_addr);
850 if (source) {
851 /* If found, clear deletion flag: (A*B) */
852 IGMP_SOURCE_DONT_DELETE(source->source_flags);
853 /* and set SEND flag (A*B) */
854 IGMP_SOURCE_DO_SEND(source->source_flags);
855 ++num_sources_tosend;
856 }
857 else {
858 /* If source not found, create source with timer=0: (B-A)=0 */
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400859 source = source_new(group, *src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300860 if (!source) {
861 /* ugh, internal malloc failure, skip source */
862 continue;
863 }
864 zassert(!source->t_source_timer); /* (B-A) timer=0 */
865 }
866
867 } /* Scan received sources (B) */
868
869 group->group_filtermode_isexcl = 1; /* boolean=true */
870
871 /* Delete all sources marked with DELETE flag (A-B) */
872 source_delete_by_flag(group->group_source_list);
873
874 /* Send sources marked with SEND flag: Q(G,A*B) */
875 if (num_sources_tosend > 0) {
876 source_query_send_by_flag(group, num_sources_tosend);
877 }
878
879 zassert(group->group_filtermode_isexcl);
880
881 group_exclude_fwd_anysrc_ifempty(group);
882}
883
884static void toex_excl(struct igmp_group *group,
885 int num_sources, struct in_addr *sources)
886{
887 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200888 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300889
890 /* set DELETE flag for all known sources (X,Y) */
891 source_mark_delete_flag(group->group_source_list);
892
893 /* clear off SEND flag from all known sources (X,Y) */
894 source_clear_send_flag(group->group_source_list);
895
896 /* scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200897 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300898 struct igmp_source *source;
899 struct in_addr *src_addr;
900
901 src_addr = sources + i;
902
903 /* lookup reported source (A) in known sources (X,Y) */
904 source = igmp_find_source_by_addr(group, *src_addr);
905 if (source) {
906 /* if found, clear off DELETE flag from reported source (A) */
907 IGMP_SOURCE_DONT_DELETE(source->source_flags);
908 }
909 else {
910 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
911 long group_timer_msec;
Donald Sharp3e6a3a62016-06-02 02:20:28 -0400912 source = source_new(group, *src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300913 if (!source) {
914 /* ugh, internal malloc failure, skip source */
915 continue;
916 }
917
918 zassert(!source->t_source_timer); /* timer == 0 */
919 group_timer_msec = igmp_group_timer_remain_msec(group);
920 igmp_source_timer_on(group, source, group_timer_msec);
921 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
922
923 /* make sure source is created with DELETE flag unset */
924 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
925 }
926
927 /* make sure reported source has DELETE flag unset */
928 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
929
930 if (source->t_source_timer) {
931 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
932 IGMP_SOURCE_DO_SEND(source->source_flags);
933 ++num_sources_tosend;
934 }
935
936 } /* scan received sources (A) */
937
938 /*
939 delete all sources marked with DELETE flag:
940 Delete (X-A)
941 Delete (Y-A)
942 */
943 source_delete_by_flag(group->group_source_list);
944
945 /* send sources marked with SEND flag: Q(G,A-Y) */
946 if (num_sources_tosend > 0) {
947 source_query_send_by_flag(group, num_sources_tosend);
948 }
949}
950
951void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
952 struct in_addr group_addr,
953 int num_sources, struct in_addr *sources)
954{
955 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300956 struct igmp_group *group;
957
958 on_trace(__PRETTY_FUNCTION__,
959 ifp, from, group_addr, num_sources, sources);
960
Everton Marques871dbcf2009-08-11 15:43:05 -0300961 /* non-existant group is created as INCLUDE {empty} */
Donald Sharpf8aef5f2016-06-02 02:20:29 -0400962 group = igmp_add_group_by_addr(igmp, group_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -0300963 if (!group) {
964 return;
965 }
966
967 if (group->group_filtermode_isexcl) {
968 /* EXCLUDE mode */
969 toex_excl(group, num_sources, sources);
970 }
971 else {
972 /* INCLUDE mode */
973 toex_incl(group, num_sources, sources);
974 zassert(group->group_filtermode_isexcl);
975 }
976 zassert(group->group_filtermode_isexcl);
977
978 /* Group Timer=GMI */
979 igmp_group_reset_gmi(group);
980}
981
982void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
983 struct in_addr group_addr,
984 int num_sources, struct in_addr *sources)
985{
986 on_trace(__PRETTY_FUNCTION__,
987 igmp->interface, from, group_addr, num_sources, sources);
988
989 allow(igmp, from, group_addr, num_sources, sources);
990}
991
992/*
993 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
994
995 When transmitting a group specific query, if the group timer is
996 larger than LMQT, the "Suppress Router-Side Processing" bit is set
997 in the query message.
998*/
999static void group_retransmit_group(struct igmp_group *group)
1000{
1001 char query_buf[PIM_IGMP_BUFSIZE_WRITE];
1002 struct igmp_sock *igmp;
1003 struct pim_interface *pim_ifp;
1004 long lmqc; /* Last Member Query Count */
1005 long lmqi_msec; /* Last Member Query Interval */
1006 long lmqt_msec; /* Last Member Query Time */
1007 int s_flag;
1008
1009 igmp = group->group_igmp_sock;
1010 pim_ifp = igmp->interface->info;
1011
1012 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001013 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001014 lmqt_msec = lmqc * lmqi_msec;
1015
1016 /*
1017 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1018
1019 When transmitting a group specific query, if the group timer is
1020 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1021 in the query message.
1022 */
1023 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1024
1025 if (PIM_DEBUG_IGMP_TRACE) {
1026 char group_str[100];
1027 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1028 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1029 group_str, igmp->interface->name, s_flag,
1030 group->group_specific_query_retransmit_count);
1031 }
1032
1033 /*
1034 RFC3376: 4.1.12. IP Destination Addresses for Queries
1035
1036 Group-Specific and Group-and-Source-Specific Queries are sent with
1037 an IP destination address equal to the multicast address of
1038 interest.
1039 */
1040
1041 pim_igmp_send_membership_query(group,
1042 igmp->fd,
1043 igmp->interface->name,
1044 query_buf,
1045 sizeof(query_buf),
1046 0 /* num_sources_tosend */,
1047 group->group_addr /* dst_addr */,
1048 group->group_addr /* group_addr */,
Leonard Herve236b0152009-08-11 15:51:52 -03001049 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001050 s_flag,
1051 igmp->querier_robustness_variable,
1052 igmp->querier_query_interval);
1053}
1054
1055/*
1056 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1057
1058 When building a group and source specific query for a group G, two
1059 separate query messages are sent for the group. The first one has
1060 the "Suppress Router-Side Processing" bit set and contains all the
1061 sources with retransmission state and timers greater than LMQT. The
1062 second has the "Suppress Router-Side Processing" bit clear and
1063 contains all the sources with retransmission state and timers lower
1064 or equal to LMQT. If either of the two calculated messages does not
1065 contain any sources, then its transmission is suppressed.
1066 */
1067static int group_retransmit_sources(struct igmp_group *group,
1068 int send_with_sflag_set)
1069{
1070 struct igmp_sock *igmp;
1071 struct pim_interface *pim_ifp;
1072 long lmqc; /* Last Member Query Count */
1073 long lmqi_msec; /* Last Member Query Interval */
1074 long lmqt_msec; /* Last Member Query Time */
1075 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1076 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1077 int query_buf1_max_sources;
1078 int query_buf2_max_sources;
1079 struct in_addr *source_addr1;
1080 struct in_addr *source_addr2;
1081 int num_sources_tosend1;
1082 int num_sources_tosend2;
1083 struct listnode *src_node;
1084 struct igmp_source *src;
1085 int num_retransmit_sources_left = 0;
1086
1087 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1088 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1089
1090 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1091 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1092
1093 igmp = group->group_igmp_sock;
1094 pim_ifp = igmp->interface->info;
1095
1096 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001097 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001098 lmqt_msec = lmqc * lmqi_msec;
1099
1100 /* Scan all group sources */
1101 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1102
1103 /* Source has retransmission state? */
1104 if (src->source_query_retransmit_count < 1)
1105 continue;
1106
1107 if (--src->source_query_retransmit_count > 0) {
1108 ++num_retransmit_sources_left;
1109 }
1110
1111 /* Copy source address into appropriate query buffer */
1112 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1113 *source_addr1 = src->source_addr;
1114 ++source_addr1;
1115 }
1116 else {
1117 *source_addr2 = src->source_addr;
1118 ++source_addr2;
1119 }
1120
1121 }
1122
1123 num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1124 num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1125
1126 if (PIM_DEBUG_IGMP_TRACE) {
1127 char group_str[100];
1128 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1129 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",
1130 group_str, igmp->interface->name,
1131 num_sources_tosend1,
1132 num_sources_tosend2,
1133 send_with_sflag_set,
1134 num_retransmit_sources_left);
1135 }
1136
1137 if (num_sources_tosend1 > 0) {
1138 /*
1139 Send group-and-source-specific query with s_flag set and all
1140 sources with timers greater than LMQT.
1141 */
1142
1143 if (send_with_sflag_set) {
1144
1145 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1146 if (num_sources_tosend1 > query_buf1_max_sources) {
1147 char group_str[100];
1148 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001149 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 -03001150 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1151 num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
1152 }
1153 else {
1154 /*
1155 RFC3376: 4.1.12. IP Destination Addresses for Queries
1156
1157 Group-Specific and Group-and-Source-Specific Queries are sent with
1158 an IP destination address equal to the multicast address of
1159 interest.
1160 */
1161
1162 pim_igmp_send_membership_query(group,
1163 igmp->fd,
1164 igmp->interface->name,
1165 query_buf1,
1166 sizeof(query_buf1),
1167 num_sources_tosend1,
1168 group->group_addr,
1169 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001170 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001171 1 /* s_flag */,
1172 igmp->querier_robustness_variable,
1173 igmp->querier_query_interval);
1174
1175 }
1176
1177 } /* send_with_sflag_set */
1178
1179 }
1180
1181 if (num_sources_tosend2 > 0) {
1182 /*
1183 Send group-and-source-specific query with s_flag clear and all
1184 sources with timers lower or equal to LMQT.
1185 */
1186
1187 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1188 if (num_sources_tosend2 > query_buf2_max_sources) {
1189 char group_str[100];
1190 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001191 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 -03001192 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1193 num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
1194 }
1195 else {
1196 /*
1197 RFC3376: 4.1.12. IP Destination Addresses for Queries
1198
1199 Group-Specific and Group-and-Source-Specific Queries are sent with
1200 an IP destination address equal to the multicast address of
1201 interest.
1202 */
1203
1204 pim_igmp_send_membership_query(group,
1205 igmp->fd,
1206 igmp->interface->name,
1207 query_buf2,
1208 sizeof(query_buf2),
1209 num_sources_tosend2,
1210 group->group_addr,
1211 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001212 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001213 0 /* s_flag */,
1214 igmp->querier_robustness_variable,
1215 igmp->querier_query_interval);
1216
1217 }
1218 }
1219
1220 return num_retransmit_sources_left;
1221}
1222
1223static int igmp_group_retransmit(struct thread *t)
1224{
1225 struct igmp_group *group;
1226 int num_retransmit_sources_left;
1227 int send_with_sflag_set; /* boolean */
1228
1229 zassert(t);
1230 group = THREAD_ARG(t);
1231 zassert(group);
1232
1233 if (PIM_DEBUG_IGMP_TRACE) {
1234 char group_str[100];
1235 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1236 zlog_debug("group_retransmit_timer: group %s on %s",
1237 group_str, group->group_igmp_sock->interface->name);
1238 }
1239
1240 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1241 if (group->group_specific_query_retransmit_count > 0) {
1242
1243 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1244 group_retransmit_group(group);
1245 --group->group_specific_query_retransmit_count;
1246
1247 /*
1248 RFC3376: 6.6.3.2
1249 If a group specific query is scheduled to be transmitted at the
1250 same time as a group and source specific query for the same group,
1251 then transmission of the group and source specific message with the
1252 "Suppress Router-Side Processing" bit set may be suppressed.
1253 */
1254 send_with_sflag_set = 0; /* boolean=false */
1255 }
1256 else {
1257 send_with_sflag_set = 1; /* boolean=true */
1258 }
1259
1260 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1261 num_retransmit_sources_left = group_retransmit_sources(group,
1262 send_with_sflag_set);
1263
1264 group->t_group_query_retransmit_timer = 0;
1265
1266 /*
1267 Keep group retransmit timer running if there is any retransmit
1268 counter pending
1269 */
1270 if ((num_retransmit_sources_left > 0) ||
1271 (group->group_specific_query_retransmit_count > 0)) {
1272 group_retransmit_timer_on(group);
1273 }
1274
1275 return 0;
1276}
1277
1278/*
1279 group_retransmit_timer_on:
1280 if group retransmit timer isn't running, starts it;
1281 otherwise, do nothing
1282*/
1283static void group_retransmit_timer_on(struct igmp_group *group)
1284{
1285 struct igmp_sock *igmp;
1286 struct pim_interface *pim_ifp;
1287 long lmqi_msec; /* Last Member Query Interval */
1288
1289 /* if group retransmit timer is running, do nothing */
1290 if (group->t_group_query_retransmit_timer) {
1291 return;
1292 }
1293
1294 igmp = group->group_igmp_sock;
1295 pim_ifp = igmp->interface->info;
1296
Leonard Herve236b0152009-08-11 15:51:52 -03001297 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001298
1299 if (PIM_DEBUG_IGMP_TRACE) {
1300 char group_str[100];
1301 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1302 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1303 lmqi_msec / 1000,
1304 lmqi_msec % 1000,
1305 group_str,
1306 igmp->interface->name);
1307 }
1308
1309 THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
1310 igmp_group_retransmit,
1311 group, lmqi_msec);
1312}
1313
1314static long igmp_group_timer_remain_msec(struct igmp_group *group)
1315{
1316 return pim_time_timer_remain_msec(group->t_group_timer);
1317}
1318
1319static long igmp_source_timer_remain_msec(struct igmp_source *source)
1320{
1321 return pim_time_timer_remain_msec(source->t_source_timer);
1322}
1323
1324/*
1325 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1326*/
1327static void group_query_send(struct igmp_group *group)
1328{
1329 long lmqc; /* Last Member Query Count */
1330
1331 lmqc = group->group_igmp_sock->querier_robustness_variable;
1332
1333 /* lower group timer to lmqt */
1334 igmp_group_timer_lower_to_lmqt(group);
1335
1336 /* reset retransmission counter */
1337 group->group_specific_query_retransmit_count = lmqc;
1338
1339 /* immediately send group specific query (decrease retransmit counter by 1)*/
1340 group_retransmit_group(group);
1341
1342 /* make sure group retransmit timer is running */
1343 group_retransmit_timer_on(group);
1344}
1345
1346/*
1347 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1348*/
1349static void source_query_send_by_flag(struct igmp_group *group,
1350 int num_sources_tosend)
1351{
1352 struct igmp_sock *igmp;
1353 struct pim_interface *pim_ifp;
1354 struct listnode *src_node;
1355 struct igmp_source *src;
1356 long lmqc; /* Last Member Query Count */
1357 long lmqi_msec; /* Last Member Query Interval */
1358 long lmqt_msec; /* Last Member Query Time */
1359
1360 zassert(num_sources_tosend > 0);
1361
1362 igmp = group->group_igmp_sock;
1363 pim_ifp = igmp->interface->info;
1364
1365 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001366 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001367 lmqt_msec = lmqc * lmqi_msec;
1368
1369 /*
1370 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1371
1372 (...) for each of the sources in X of group G, with source timer larger
1373 than LMQT:
1374 o Set number of retransmissions for each source to [Last Member
1375 Query Count].
1376 o Lower source timer to LMQT.
1377 */
1378 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1379 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1380 /* source "src" in X of group G */
1381 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1382 src->source_query_retransmit_count = lmqc;
1383 igmp_source_timer_lower_to_lmqt(src);
1384 }
1385 }
1386 }
1387
1388 /* send group-and-source specific queries */
1389 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1390
1391 /* make sure group retransmit timer is running */
1392 group_retransmit_timer_on(group);
1393}
1394
1395static void block_excl(struct igmp_group *group,
1396 int num_sources, struct in_addr *sources)
1397{
1398 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +02001399 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -03001400
1401 /* 1. clear off SEND flag from all known sources (X,Y) */
1402 source_clear_send_flag(group->group_source_list);
1403
1404 /* 2. scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +02001405 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -03001406 struct igmp_source *source;
1407 struct in_addr *src_addr;
1408
1409 src_addr = sources + i;
1410
1411 /* lookup reported source (A) in known sources (X,Y) */
1412 source = igmp_find_source_by_addr(group, *src_addr);
1413 if (!source) {
1414 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1415 long group_timer_msec;
Donald Sharp3e6a3a62016-06-02 02:20:28 -04001416 source = source_new(group, *src_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -03001417 if (!source) {
1418 /* ugh, internal malloc failure, skip source */
1419 continue;
1420 }
1421
1422 zassert(!source->t_source_timer); /* timer == 0 */
1423 group_timer_msec = igmp_group_timer_remain_msec(group);
1424 igmp_source_timer_on(group, source, group_timer_msec);
1425 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1426 }
1427
1428 if (source->t_source_timer) {
1429 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1430 IGMP_SOURCE_DO_SEND(source->source_flags);
1431 ++num_sources_tosend;
1432 }
1433 }
1434
1435 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1436 if (num_sources_tosend > 0) {
1437 source_query_send_by_flag(group, num_sources_tosend);
1438 }
1439}
1440
1441static void block_incl(struct igmp_group *group,
1442 int num_sources, struct in_addr *sources)
1443{
1444 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +02001445 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -03001446
1447 /* 1. clear off SEND flag from all known sources (B) */
1448 source_clear_send_flag(group->group_source_list);
1449
1450 /* 2. scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +02001451 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -03001452 struct igmp_source *source;
1453 struct in_addr *src_addr;
1454
1455 src_addr = sources + i;
1456
1457 /* lookup reported source (A) in known sources (B) */
1458 source = igmp_find_source_by_addr(group, *src_addr);
1459 if (source) {
1460 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1461 IGMP_SOURCE_DO_SEND(source->source_flags);
1462 ++num_sources_tosend;
1463 }
1464 }
1465
1466 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1467 if (num_sources_tosend > 0) {
1468 source_query_send_by_flag(group, num_sources_tosend);
1469 }
1470}
1471
1472void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1473 struct in_addr group_addr,
1474 int num_sources, struct in_addr *sources)
1475{
1476 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -03001477 struct igmp_group *group;
1478
1479 on_trace(__PRETTY_FUNCTION__,
1480 ifp, from, group_addr, num_sources, sources);
1481
Everton Marques871dbcf2009-08-11 15:43:05 -03001482 /* non-existant group is created as INCLUDE {empty} */
Donald Sharpf8aef5f2016-06-02 02:20:29 -04001483 group = igmp_add_group_by_addr(igmp, group_addr);
Everton Marques871dbcf2009-08-11 15:43:05 -03001484 if (!group) {
1485 return;
1486 }
1487
1488 if (group->group_filtermode_isexcl) {
1489 /* EXCLUDE mode */
1490 block_excl(group, num_sources, sources);
1491 }
1492 else {
1493 /* INCLUDE mode */
1494 block_incl(group, num_sources, sources);
1495 }
1496}
1497
1498void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1499{
1500 struct igmp_sock *igmp;
1501 struct interface *ifp;
1502 struct pim_interface *pim_ifp;
1503 char *ifname;
1504 int lmqi_dsec; /* Last Member Query Interval */
1505 int lmqc; /* Last Member Query Count */
1506 int lmqt_msec; /* Last Member Query Time */
1507
1508 /*
1509 RFC 3376: 6.2.2. Definition of Group Timers
1510
1511 The group timer is only used when a group is in EXCLUDE mode and
1512 it represents the time for the *filter-mode* of the group to
1513 expire and switch to INCLUDE mode.
1514 */
1515 if (!group->group_filtermode_isexcl) {
1516 return;
1517 }
1518
1519 igmp = group->group_igmp_sock;
1520 ifp = igmp->interface;
1521 pim_ifp = ifp->info;
1522 ifname = ifp->name;
1523
Leonard Herve236b0152009-08-11 15:51:52 -03001524 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001525 lmqc = igmp->querier_robustness_variable;
1526 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1527
1528 if (PIM_DEBUG_IGMP_TRACE) {
1529 char group_str[100];
1530 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1531 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1532 __PRETTY_FUNCTION__,
1533 group_str, ifname,
1534 lmqc, lmqi_dsec, lmqt_msec);
1535 }
1536
1537 zassert(group->group_filtermode_isexcl);
1538
1539 igmp_group_timer_on(group, lmqt_msec, ifname);
1540}
1541
1542void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1543{
1544 struct igmp_group *group;
1545 struct igmp_sock *igmp;
1546 struct interface *ifp;
1547 struct pim_interface *pim_ifp;
1548 char *ifname;
1549 int lmqi_dsec; /* Last Member Query Interval */
1550 int lmqc; /* Last Member Query Count */
1551 int lmqt_msec; /* Last Member Query Time */
1552
1553 group = source->source_group;
1554 igmp = group->group_igmp_sock;
1555 ifp = igmp->interface;
1556 pim_ifp = ifp->info;
1557 ifname = ifp->name;
1558
Leonard Herve236b0152009-08-11 15:51:52 -03001559 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001560 lmqc = igmp->querier_robustness_variable;
1561 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1562
1563 if (PIM_DEBUG_IGMP_TRACE) {
1564 char group_str[100];
1565 char source_str[100];
1566 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1567 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1568 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1569 __PRETTY_FUNCTION__,
1570 group_str, source_str, ifname,
1571 lmqc, lmqi_dsec, lmqt_msec);
1572 }
1573
1574 igmp_source_timer_on(group, source, lmqt_msec);
1575}
1576
1577/*
1578 Copy sources to message:
1579
1580 struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
1581 if (num_sources > 0) {
1582 struct listnode *node;
1583 struct igmp_source *src;
1584 int i = 0;
1585
1586 for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
1587 sources[i++] = src->source_addr;
1588 }
1589 }
1590*/
1591void pim_igmp_send_membership_query(struct igmp_group *group,
1592 int fd,
1593 const char *ifname,
1594 char *query_buf,
1595 int query_buf_size,
1596 int num_sources,
1597 struct in_addr dst_addr,
1598 struct in_addr group_addr,
1599 int query_max_response_time_dsec,
1600 uint8_t s_flag,
1601 uint8_t querier_robustness_variable,
1602 uint16_t querier_query_interval)
1603{
1604 ssize_t msg_size;
1605 uint8_t max_resp_code;
1606 uint8_t qqic;
1607 ssize_t sent;
1608 struct sockaddr_in to;
1609 socklen_t tolen;
1610 uint16_t checksum;
1611
1612 zassert(num_sources >= 0);
1613
1614 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1615 if (msg_size > query_buf_size) {
David Lamparter5c697982012-02-16 04:47:56 +01001616 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
Everton Marques871dbcf2009-08-11 15:43:05 -03001617 __FILE__, __PRETTY_FUNCTION__,
1618 msg_size, query_buf_size);
1619 return;
1620 }
1621
1622 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1623 zassert((s_flag == 0) || (s_flag == 1));
1624
1625 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1626 qqic = igmp_msg_encode16to8(querier_query_interval);
1627
1628 /*
1629 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1630
1631 If non-zero, the QRV field contains the [Robustness Variable]
1632 value used by the querier, i.e., the sender of the Query. If the
1633 querier's [Robustness Variable] exceeds 7, the maximum value of
1634 the QRV field, the QRV is set to zero.
1635 */
1636 if (querier_robustness_variable > 7) {
1637 querier_robustness_variable = 0;
1638 }
1639
1640 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1641 query_buf[1] = max_resp_code;
1642 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
Klemen Sladic3defeb32014-02-07 16:23:44 +13001643 memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
1644
Everton Marques871dbcf2009-08-11 15:43:05 -03001645 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1646 query_buf[9] = qqic;
1647 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
1648
Everton Marques93911262014-09-18 11:10:58 -03001649 checksum = in_cksum(query_buf, msg_size);
Everton Marques871dbcf2009-08-11 15:43:05 -03001650 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
1651
1652 if (PIM_DEBUG_IGMP_PACKETS) {
1653 char dst_str[100];
1654 char group_str[100];
1655 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1656 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001657 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 -03001658 __PRETTY_FUNCTION__,
1659 dst_str, ifname, group_str, num_sources,
1660 msg_size, s_flag, querier_robustness_variable,
1661 querier_query_interval, qqic, checksum);
1662 }
1663
1664#if 0
1665 memset(&to, 0, sizeof(to));
1666#endif
1667 to.sin_family = AF_INET;
1668 to.sin_addr = dst_addr;
1669#if 0
1670 to.sin_port = htons(0);
1671#endif
1672 tolen = sizeof(to);
1673
David Lampartera2c7f4b2015-03-03 21:03:52 +01001674 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1675 (struct sockaddr *)&to, tolen);
Everton Marques871dbcf2009-08-11 15:43:05 -03001676 if (sent != (ssize_t) msg_size) {
1677 int e = errno;
1678 char dst_str[100];
1679 char group_str[100];
1680 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1681 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1682 if (sent < 0) {
David Lamparter5c697982012-02-16 04:47:56 +01001683 zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
Everton Marques871dbcf2009-08-11 15:43:05 -03001684 __PRETTY_FUNCTION__,
1685 dst_str, ifname, group_str, msg_size,
Everton Marquese96f0af2009-08-11 15:48:02 -03001686 e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -03001687 }
1688 else {
David Lamparter5c697982012-02-16 04:47:56 +01001689 zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
Everton Marques871dbcf2009-08-11 15:43:05 -03001690 __PRETTY_FUNCTION__,
1691 dst_str, ifname, group_str,
1692 msg_size, sent);
1693 }
1694 return;
1695 }
1696
1697 /*
1698 s_flag sanity test: s_flag must be set for general queries
1699
1700 RFC 3376: 6.6.1. Timer Updates
1701
1702 When a router sends or receives a query with a clear Suppress
1703 Router-Side Processing flag, it must update its timers to reflect
1704 the correct timeout values for the group or sources being queried.
1705
1706 General queries don't trigger timer update.
1707 */
1708 if (!s_flag) {
1709 /* general query? */
1710 if (PIM_INADDR_IS_ANY(group_addr)) {
1711 char dst_str[100];
1712 char group_str[100];
1713 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1714 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1715 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1716 __PRETTY_FUNCTION__,
1717 dst_str, ifname, group_str, num_sources);
1718 }
1719 }
1720
1721}