blob: 3da1dd1ec868c97cdfed6eb82c2a20e9159e4722 [file] [log] [blame]
Everton Marques871dbcf2009-08-11 15:43:05 -03001/*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
19
20 $QuaggaId: $Format:%an, %ai, %h$ $
21*/
22
23#include <zebra.h>
24#include "log.h"
25#include "memory.h"
26
27#include "pimd.h"
28#include "pim_iface.h"
29#include "pim_igmp.h"
30#include "pim_igmpv3.h"
31#include "pim_str.h"
32#include "pim_util.h"
33#include "pim_time.h"
34#include "pim_zebra.h"
35#include "pim_oil.h"
36
37static void group_retransmit_timer_on(struct igmp_group *group);
38static long igmp_group_timer_remain_msec(struct igmp_group *group);
39static long igmp_source_timer_remain_msec(struct igmp_source *source);
40static void group_query_send(struct igmp_group *group);
41static void source_query_send_by_flag(struct igmp_group *group,
42 int num_sources_tosend);
43
44static void on_trace(const char *label,
45 struct interface *ifp, struct in_addr from,
46 struct in_addr group_addr,
47 int num_sources, struct in_addr *sources)
48{
49 if (PIM_DEBUG_IGMP_TRACE) {
50 char from_str[100];
51 char group_str[100];
52
53 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
54 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
55
56 zlog_debug("%s: from %s on %s: group=%s sources=%d",
57 label, from_str, ifp->name, group_str, num_sources);
58 }
59}
60
61int igmp_group_compat_mode(const struct igmp_sock *igmp,
62 const struct igmp_group *group)
63{
64 struct pim_interface *pim_ifp;
65 int64_t now_dsec;
66 long older_host_present_interval_dsec;
67
68 zassert(igmp);
69 zassert(igmp->interface);
70 zassert(igmp->interface->info);
71
72 pim_ifp = igmp->interface->info;
73
74 /*
75 RFC 3376: 8.13. Older Host Present Interval
76
77 This value MUST be ((the Robustness Variable) times (the Query
78 Interval)) plus (one Query Response Interval).
79
80 older_host_present_interval_dsec = \
81 igmp->querier_robustness_variable * \
82 10 * igmp->querier_query_interval + \
83 pim_ifp->query_max_response_time_dsec;
84 */
85 older_host_present_interval_dsec =
86 PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
87 igmp->querier_query_interval,
88 pim_ifp->igmp_query_max_response_time_dsec);
89
90 now_dsec = pim_time_monotonic_dsec();
91 if (now_dsec < 1) {
92 /* broken timer logged by pim_time_monotonic_dsec() */
93 return 3;
94 }
95
96 if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
97 return 1; /* IGMPv1 */
98
99 if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
100 return 2; /* IGMPv2 */
101
102 return 3; /* IGMPv3 */
103}
104
105void igmp_group_reset_gmi(struct igmp_group *group)
106{
107 long group_membership_interval_msec;
108 struct pim_interface *pim_ifp;
109 struct igmp_sock *igmp;
110 struct interface *ifp;
111
112 igmp = group->group_igmp_sock;
113 ifp = igmp->interface;
114 pim_ifp = ifp->info;
115
116 /*
117 RFC 3376: 8.4. Group Membership Interval
118
119 The Group Membership Interval is the amount of time that must pass
120 before a multicast router decides there are no more members of a
121 group or a particular source on a network.
122
123 This value MUST be ((the Robustness Variable) times (the Query
124 Interval)) plus (one Query Response Interval).
125
126 group_membership_interval_msec = querier_robustness_variable *
127 (1000 * querier_query_interval) +
128 100 * query_response_interval_dsec;
129 */
130 group_membership_interval_msec =
131 PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
132 igmp->querier_query_interval,
133 pim_ifp->igmp_query_max_response_time_dsec);
134
135 if (PIM_DEBUG_IGMP_TRACE) {
136 char group_str[100];
137 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
138 zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
139 group_str,
140 group_membership_interval_msec / 1000,
141 group_membership_interval_msec % 1000,
142 ifp->name);
143 }
144
145 /*
146 RFC 3376: 6.2.2. Definition of Group Timers
147
148 The group timer is only used when a group is in EXCLUDE mode and
149 it represents the time for the *filter-mode* of the group to
150 expire and switch to INCLUDE mode.
151 */
152 zassert(group->group_filtermode_isexcl);
153
154 igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
155}
156
157static int igmp_source_timer(struct thread *t)
158{
159 struct igmp_source *source;
160 struct igmp_group *group;
161
162 zassert(t);
163 source = THREAD_ARG(t);
164 zassert(source);
165
166 group = source->source_group;
167
168 if (PIM_DEBUG_IGMP_TRACE) {
169 char group_str[100];
170 char source_str[100];
171 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
172 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
173 zlog_debug("%s: Source timer expired for group %s source %s on %s",
174 __PRETTY_FUNCTION__,
175 group_str, source_str,
176 group->group_igmp_sock->interface->name);
177 }
178
179 zassert(source->t_source_timer);
180 source->t_source_timer = 0;
181
182 /*
183 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
184
185 Group
186 Filter-Mode Source Timer Value Action
187 ----------- ------------------ ------
188 INCLUDE TIMER == 0 Suggest to stop forwarding
189 traffic from source and
190 remove source record. If
191 there are no more source
192 records for the group, delete
193 group record.
194
195 EXCLUDE TIMER == 0 Suggest to not forward
196 traffic from source
197 (DO NOT remove record)
198
199 Source timer switched from (T > 0) to (T == 0): disable forwarding.
200 */
201
202 zassert(!source->t_source_timer);
203
204 if (group->group_filtermode_isexcl) {
205 /* EXCLUDE mode */
206
207 igmp_source_forward_stop(source);
208 }
209 else {
210 /* INCLUDE mode */
211
212 /* igmp_source_delete() will stop forwarding source */
213 igmp_source_delete(source);
214
215 /*
216 If there are no more source records for the group, delete group
217 record.
218 */
219 if (!listcount(group->group_source_list)) {
220 igmp_group_delete_empty_include(group);
221 }
222 }
223
224 return 0;
225}
226
227static void source_timer_off(struct igmp_group *group,
228 struct igmp_source *source)
229{
230 if (!source->t_source_timer)
231 return;
232
233 if (PIM_DEBUG_IGMP_TRACE) {
234 char group_str[100];
235 char source_str[100];
236 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
237 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
238 zlog_debug("Cancelling TIMER event for group %s source %s on %s",
239 group_str, source_str,
240 group->group_igmp_sock->interface->name);
241 }
242
243 THREAD_OFF(source->t_source_timer);
244 zassert(!source->t_source_timer);
245}
246
247static void igmp_source_timer_on(struct igmp_group *group,
248 struct igmp_source *source,
249 long interval_msec)
250{
251 source_timer_off(group, source);
252
253 if (PIM_DEBUG_IGMP_EVENTS) {
254 char group_str[100];
255 char source_str[100];
256 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
257 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
258 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
259 interval_msec / 1000,
260 interval_msec % 1000,
261 group_str, source_str,
262 group->group_igmp_sock->interface->name);
263 }
264
265 THREAD_TIMER_MSEC_ON(master, source->t_source_timer,
266 igmp_source_timer,
267 source, interval_msec);
268 zassert(source->t_source_timer);
269
270 /*
271 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
272
273 Source timer switched from (T == 0) to (T > 0): enable forwarding.
274 */
275 igmp_source_forward_start(source);
276}
277
278void igmp_source_reset_gmi(struct igmp_sock *igmp,
279 struct igmp_group *group,
280 struct igmp_source *source)
281{
282 long group_membership_interval_msec;
283 struct pim_interface *pim_ifp;
284 struct interface *ifp;
285
286 ifp = igmp->interface;
287 pim_ifp = ifp->info;
288
289 group_membership_interval_msec =
290 PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
291 igmp->querier_query_interval,
292 pim_ifp->igmp_query_max_response_time_dsec);
293
294 if (PIM_DEBUG_IGMP_TRACE) {
295 char group_str[100];
296 char source_str[100];
297
298 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
299 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
300
301 zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
302 source_str,
303 group_membership_interval_msec / 1000,
304 group_membership_interval_msec % 1000,
305 group_str,
306 ifp->name);
307 }
308
309 igmp_source_timer_on(group, source,
310 group_membership_interval_msec);
311}
312
313static void source_mark_delete_flag(struct list *source_list)
314{
315 struct listnode *src_node;
316 struct igmp_source *src;
317
318 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
319 IGMP_SOURCE_DO_DELETE(src->source_flags);
320 }
321}
322
323static void source_mark_send_flag(struct list *source_list)
324{
325 struct listnode *src_node;
326 struct igmp_source *src;
327
328 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
329 IGMP_SOURCE_DO_SEND(src->source_flags);
330 }
331}
332
333static int source_mark_send_flag_by_timer(struct list *source_list)
334{
335 struct listnode *src_node;
336 struct igmp_source *src;
337 int num_marked_sources = 0;
338
339 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
340 /* Is source timer running? */
341 if (src->t_source_timer) {
342 IGMP_SOURCE_DO_SEND(src->source_flags);
343 ++num_marked_sources;
344 }
345 else {
346 IGMP_SOURCE_DONT_SEND(src->source_flags);
347 }
348 }
349
350 return num_marked_sources;
351}
352
353static void source_clear_send_flag(struct list *source_list)
354{
355 struct listnode *src_node;
356 struct igmp_source *src;
357
358 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
359 IGMP_SOURCE_DONT_SEND(src->source_flags);
360 }
361}
362
363/*
364 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
365*/
366static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
367{
368 zassert(group->group_filtermode_isexcl);
369
370 if (listcount(group->group_source_list) < 1) {
371 igmp_anysource_forward_start(group);
372 }
373}
374
375void igmp_source_free(struct igmp_source *source)
376{
377 /* make sure there is no source timer running */
378 zassert(!source->t_source_timer);
379
380 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
381}
382
383static void source_channel_oil_detach(struct igmp_source *source)
384{
385 if (source->source_channel_oil) {
386 pim_channel_oil_del(source->source_channel_oil);
387 source->source_channel_oil = 0;
388 }
389}
390
391void igmp_source_delete(struct igmp_source *source)
392{
393 struct igmp_group *group;
394
395 group = source->source_group;
396
397 if (PIM_DEBUG_IGMP_TRACE) {
398 char group_str[100];
399 char source_str[100];
400 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
401 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
402 zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
403 source_str, group_str,
404 group->group_igmp_sock->fd,
405 group->group_igmp_sock->interface->name);
406 }
407
408 source_timer_off(group, source);
409 igmp_source_forward_stop(source);
410
411 /* make sure forwarding is disabled */
412 zassert(!IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
413
414 source_channel_oil_detach(source);
415
416 /*
417 notice that listnode_delete() can't be moved
418 into igmp_source_free() because the later is
419 called by list_delete_all_node()
420 */
421 listnode_delete(group->group_source_list, source);
422
423 igmp_source_free(source);
424
425 if (group->group_filtermode_isexcl) {
426 group_exclude_fwd_anysrc_ifempty(group);
427 }
428}
429
430static void source_delete_by_flag(struct list *source_list)
431{
432 struct listnode *src_node;
433 struct listnode *src_nextnode;
434 struct igmp_source *src;
435
436 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
437 if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
438 igmp_source_delete(src);
439}
440
441void igmp_source_delete_expired(struct list *source_list)
442{
443 struct listnode *src_node;
444 struct listnode *src_nextnode;
445 struct igmp_source *src;
446
447 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
448 if (!src->t_source_timer)
449 igmp_source_delete(src);
450}
451
452struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
453 struct in_addr src_addr)
454{
455 struct listnode *src_node;
456 struct igmp_source *src;
457
458 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
459 if (src_addr.s_addr == src->source_addr.s_addr)
460 return src;
461
462 return 0;
463}
464
465static struct igmp_source *source_new(struct igmp_group *group,
466 struct in_addr src_addr,
467 const char *ifname)
468{
469 struct igmp_source *src;
470
471 if (PIM_DEBUG_IGMP_TRACE) {
472 char group_str[100];
473 char source_str[100];
474 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
475 pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
476 zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
477 source_str, group_str,
478 group->group_igmp_sock->fd,
479 ifname);
480 }
481
482 src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
483 if (!src) {
484 zlog_warn("%s %s: XMALLOC() failure",
485 __FILE__, __PRETTY_FUNCTION__);
486 return 0; /* error, not found, could not create */
487 }
488
489 src->t_source_timer = 0;
490 src->source_group = group; /* back pointer */
491 src->source_addr = src_addr;
492 src->source_creation = pim_time_monotonic_sec();
493 src->source_flags = 0;
494 src->source_query_retransmit_count = 0;
495 src->source_channel_oil = 0;
496
497 listnode_add(group->group_source_list, src);
498
499 zassert(!src->t_source_timer); /* source timer == 0 */
500
501 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
502 igmp_anysource_forward_stop(group);
503
504 return src;
505}
506
507static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
508 struct igmp_group *group,
509 struct in_addr src_addr,
510 const char *ifname)
511{
512 struct igmp_source *src;
513
514 src = igmp_find_source_by_addr(group, src_addr);
515 if (src) {
516 return src;
517 }
518
519 src = source_new(group, src_addr, ifname);
520 if (!src) {
521 return 0;
522 }
523
524 return src;
525}
526
527static void allow(struct igmp_sock *igmp, struct in_addr from,
528 struct in_addr group_addr,
529 int num_sources, struct in_addr *sources)
530{
531 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300532 struct igmp_group *group;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200533 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300534
Everton Marques871dbcf2009-08-11 15:43:05 -0300535 /* non-existant group is created as INCLUDE {empty} */
536 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
537 if (!group) {
538 return;
539 }
540
541 /* scan received sources */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200542 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300543 struct igmp_source *source;
544 struct in_addr *src_addr;
545
546 src_addr = sources + i;
547
548 source = add_source_by_addr(igmp, group, *src_addr, ifp->name);
549 if (!source) {
550 continue;
551 }
552
553 /*
554 RFC 3376: 6.4.1. Reception of Current-State Records
555
556 When receiving IS_IN reports for groups in EXCLUDE mode is
557 sources should be moved from set with (timers = 0) to set with
558 (timers > 0).
559
560 igmp_source_reset_gmi() below, resetting the source timers to
561 GMI, accomplishes this.
562 */
563 igmp_source_reset_gmi(igmp, group, source);
564
565 } /* scan received sources */
566}
567
568void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
569 struct in_addr group_addr,
570 int num_sources, struct in_addr *sources)
571{
572 on_trace(__PRETTY_FUNCTION__,
573 igmp->interface, from, group_addr, num_sources, sources);
574
575 allow(igmp, from, group_addr, num_sources, sources);
576}
577
578static void isex_excl(struct igmp_group *group,
579 int num_sources, struct in_addr *sources)
580{
Leonard Herve942b0fd2009-08-14 15:49:06 +0200581 int i;
582
Everton Marques871dbcf2009-08-11 15:43:05 -0300583 /* EXCLUDE mode */
584 zassert(group->group_filtermode_isexcl);
585
586 /* E.1: set deletion flag for known sources (X,Y) */
587 source_mark_delete_flag(group->group_source_list);
588
589 /* scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200590 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300591 struct igmp_source *source;
592 struct in_addr *src_addr;
593
594 src_addr = sources + i;
595
596 /* E.2: lookup reported source from (A) in (X,Y) */
597 source = igmp_find_source_by_addr(group, *src_addr);
598 if (source) {
599 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
600 IGMP_SOURCE_DONT_DELETE(source->source_flags);
601 }
602 else {
603 /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
604 source = source_new(group, *src_addr,
605 group->group_igmp_sock->interface->name);
606 if (!source) {
607 /* ugh, internal malloc failure, skip source */
608 continue;
609 }
610 zassert(!source->t_source_timer); /* timer == 0 */
611 igmp_source_reset_gmi(group->group_igmp_sock, group, source);
612 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
613 }
614
615 } /* scan received sources */
616
617 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
618 source_delete_by_flag(group->group_source_list);
619}
620
621static void isex_incl(struct igmp_group *group,
622 int num_sources, struct in_addr *sources)
623{
Leonard Herve942b0fd2009-08-14 15:49:06 +0200624 int i;
625
Everton Marques871dbcf2009-08-11 15:43:05 -0300626 /* INCLUDE mode */
627 zassert(!group->group_filtermode_isexcl);
628
629 /* I.1: set deletion flag for known sources (A) */
630 source_mark_delete_flag(group->group_source_list);
631
632 /* scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200633 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300634 struct igmp_source *source;
635 struct in_addr *src_addr;
636
637 src_addr = sources + i;
638
639 /* I.2: lookup reported source (B) */
640 source = igmp_find_source_by_addr(group, *src_addr);
641 if (source) {
642 /* I.3: if found, clear deletion flag (A*B) */
643 IGMP_SOURCE_DONT_DELETE(source->source_flags);
644 }
645 else {
646 /* I.4: if not found, create source with timer=0 (B-A) */
647 source = source_new(group, *src_addr,
648 group->group_igmp_sock->interface->name);
649 if (!source) {
650 /* ugh, internal malloc failure, skip source */
651 continue;
652 }
653 zassert(!source->t_source_timer); /* (B-A) timer=0 */
654 }
655
656 } /* scan received sources */
657
658 /* I.5: delete all sources marked with deletion flag (A-B) */
659 source_delete_by_flag(group->group_source_list);
660
661 group->group_filtermode_isexcl = 1; /* boolean=true */
662
663 zassert(group->group_filtermode_isexcl);
664
665 group_exclude_fwd_anysrc_ifempty(group);
666}
667
668void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
669 struct in_addr group_addr,
670 int num_sources, struct in_addr *sources)
671{
672 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300673 struct igmp_group *group;
674
675 on_trace(__PRETTY_FUNCTION__,
676 ifp, from, group_addr, num_sources, sources);
677
Everton Marques871dbcf2009-08-11 15:43:05 -0300678 /* non-existant group is created as INCLUDE {empty} */
679 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
680 if (!group) {
681 return;
682 }
683
684 if (group->group_filtermode_isexcl) {
685 /* EXCLUDE mode */
686 isex_excl(group, num_sources, sources);
687 }
688 else {
689 /* INCLUDE mode */
690 isex_incl(group, num_sources, sources);
691 zassert(group->group_filtermode_isexcl);
692 }
693
694 zassert(group->group_filtermode_isexcl);
695
696 igmp_group_reset_gmi(group);
697}
698
699static void toin_incl(struct igmp_group *group,
700 int num_sources, struct in_addr *sources)
701{
702 struct igmp_sock *igmp = group->group_igmp_sock;
703 int num_sources_tosend = listcount(group->group_source_list);
Leonard Herve942b0fd2009-08-14 15:49:06 +0200704 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300705
706 /* Set SEND flag for all known sources (A) */
707 source_mark_send_flag(group->group_source_list);
708
709 /* Scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200710 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300711 struct igmp_source *source;
712 struct in_addr *src_addr;
713
714 src_addr = sources + i;
715
716 /* Lookup reported source (B) */
717 source = igmp_find_source_by_addr(group, *src_addr);
718 if (source) {
719 /* If found, clear SEND flag (A*B) */
720 IGMP_SOURCE_DONT_SEND(source->source_flags);
721 --num_sources_tosend;
722 }
723 else {
724 /* If not found, create new source */
725 source = source_new(group, *src_addr,
726 group->group_igmp_sock->interface->name);
727 if (!source) {
728 /* ugh, internal malloc failure, skip source */
729 continue;
730 }
731 }
732
733 /* (B)=GMI */
734 igmp_source_reset_gmi(igmp, group, source);
735 }
736
737 /* Send sources marked with SEND flag: Q(G,A-B) */
738 if (num_sources_tosend > 0) {
739 source_query_send_by_flag(group, num_sources_tosend);
740 }
741}
742
743static void toin_excl(struct igmp_group *group,
744 int num_sources, struct in_addr *sources)
745{
746 struct igmp_sock *igmp = group->group_igmp_sock;
747 int num_sources_tosend;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200748 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300749
750 /* Set SEND flag for X (sources with timer > 0) */
751 num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
752
753 /* Scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200754 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300755 struct igmp_source *source;
756 struct in_addr *src_addr;
757
758 src_addr = sources + i;
759
760 /* Lookup reported source (A) */
761 source = igmp_find_source_by_addr(group, *src_addr);
762 if (source) {
763 if (source->t_source_timer) {
764 /* If found and timer running, clear SEND flag (X*A) */
765 IGMP_SOURCE_DONT_SEND(source->source_flags);
766 --num_sources_tosend;
767 }
768 }
769 else {
770 /* If not found, create new source */
771 source = source_new(group, *src_addr,
772 group->group_igmp_sock->interface->name);
773 if (!source) {
774 /* ugh, internal malloc failure, skip source */
775 continue;
776 }
777 }
778
779 /* (A)=GMI */
780 igmp_source_reset_gmi(igmp, group, source);
781 }
782
783 /* Send sources marked with SEND flag: Q(G,X-A) */
784 if (num_sources_tosend > 0) {
785 source_query_send_by_flag(group, num_sources_tosend);
786 }
787
788 /* Send Q(G) */
789 group_query_send(group);
790}
791
792void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
793 struct in_addr group_addr,
794 int num_sources, struct in_addr *sources)
795{
796 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300797 struct igmp_group *group;
798
799 on_trace(__PRETTY_FUNCTION__,
800 ifp, from, group_addr, num_sources, sources);
801
Everton Marques871dbcf2009-08-11 15:43:05 -0300802 /* non-existant group is created as INCLUDE {empty} */
803 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
804 if (!group) {
805 return;
806 }
807
808 if (group->group_filtermode_isexcl) {
809 /* EXCLUDE mode */
810 toin_excl(group, num_sources, sources);
811 }
812 else {
813 /* INCLUDE mode */
814 toin_incl(group, num_sources, sources);
815 }
816}
817
818static void toex_incl(struct igmp_group *group,
819 int num_sources, struct in_addr *sources)
820{
821 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200822 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300823
824 zassert(!group->group_filtermode_isexcl);
825
826 /* Set DELETE flag for all known sources (A) */
827 source_mark_delete_flag(group->group_source_list);
828
829 /* Clear off SEND flag from all known sources (A) */
830 source_clear_send_flag(group->group_source_list);
831
832 /* Scan received sources (B) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200833 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300834 struct igmp_source *source;
835 struct in_addr *src_addr;
836
837 src_addr = sources + i;
838
839 /* Lookup reported source (B) */
840 source = igmp_find_source_by_addr(group, *src_addr);
841 if (source) {
842 /* If found, clear deletion flag: (A*B) */
843 IGMP_SOURCE_DONT_DELETE(source->source_flags);
844 /* and set SEND flag (A*B) */
845 IGMP_SOURCE_DO_SEND(source->source_flags);
846 ++num_sources_tosend;
847 }
848 else {
849 /* If source not found, create source with timer=0: (B-A)=0 */
850 source = source_new(group, *src_addr,
851 group->group_igmp_sock->interface->name);
852 if (!source) {
853 /* ugh, internal malloc failure, skip source */
854 continue;
855 }
856 zassert(!source->t_source_timer); /* (B-A) timer=0 */
857 }
858
859 } /* Scan received sources (B) */
860
861 group->group_filtermode_isexcl = 1; /* boolean=true */
862
863 /* Delete all sources marked with DELETE flag (A-B) */
864 source_delete_by_flag(group->group_source_list);
865
866 /* Send sources marked with SEND flag: Q(G,A*B) */
867 if (num_sources_tosend > 0) {
868 source_query_send_by_flag(group, num_sources_tosend);
869 }
870
871 zassert(group->group_filtermode_isexcl);
872
873 group_exclude_fwd_anysrc_ifempty(group);
874}
875
876static void toex_excl(struct igmp_group *group,
877 int num_sources, struct in_addr *sources)
878{
879 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +0200880 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -0300881
882 /* set DELETE flag for all known sources (X,Y) */
883 source_mark_delete_flag(group->group_source_list);
884
885 /* clear off SEND flag from all known sources (X,Y) */
886 source_clear_send_flag(group->group_source_list);
887
888 /* scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +0200889 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -0300890 struct igmp_source *source;
891 struct in_addr *src_addr;
892
893 src_addr = sources + i;
894
895 /* lookup reported source (A) in known sources (X,Y) */
896 source = igmp_find_source_by_addr(group, *src_addr);
897 if (source) {
898 /* if found, clear off DELETE flag from reported source (A) */
899 IGMP_SOURCE_DONT_DELETE(source->source_flags);
900 }
901 else {
902 /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
903 long group_timer_msec;
904 source = source_new(group, *src_addr,
905 group->group_igmp_sock->interface->name);
906 if (!source) {
907 /* ugh, internal malloc failure, skip source */
908 continue;
909 }
910
911 zassert(!source->t_source_timer); /* timer == 0 */
912 group_timer_msec = igmp_group_timer_remain_msec(group);
913 igmp_source_timer_on(group, source, group_timer_msec);
914 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
915
916 /* make sure source is created with DELETE flag unset */
917 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
918 }
919
920 /* make sure reported source has DELETE flag unset */
921 zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
922
923 if (source->t_source_timer) {
924 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
925 IGMP_SOURCE_DO_SEND(source->source_flags);
926 ++num_sources_tosend;
927 }
928
929 } /* scan received sources (A) */
930
931 /*
932 delete all sources marked with DELETE flag:
933 Delete (X-A)
934 Delete (Y-A)
935 */
936 source_delete_by_flag(group->group_source_list);
937
938 /* send sources marked with SEND flag: Q(G,A-Y) */
939 if (num_sources_tosend > 0) {
940 source_query_send_by_flag(group, num_sources_tosend);
941 }
942}
943
944void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
945 struct in_addr group_addr,
946 int num_sources, struct in_addr *sources)
947{
948 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -0300949 struct igmp_group *group;
950
951 on_trace(__PRETTY_FUNCTION__,
952 ifp, from, group_addr, num_sources, sources);
953
Everton Marques871dbcf2009-08-11 15:43:05 -0300954 /* non-existant group is created as INCLUDE {empty} */
955 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
956 if (!group) {
957 return;
958 }
959
960 if (group->group_filtermode_isexcl) {
961 /* EXCLUDE mode */
962 toex_excl(group, num_sources, sources);
963 }
964 else {
965 /* INCLUDE mode */
966 toex_incl(group, num_sources, sources);
967 zassert(group->group_filtermode_isexcl);
968 }
969 zassert(group->group_filtermode_isexcl);
970
971 /* Group Timer=GMI */
972 igmp_group_reset_gmi(group);
973}
974
975void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
976 struct in_addr group_addr,
977 int num_sources, struct in_addr *sources)
978{
979 on_trace(__PRETTY_FUNCTION__,
980 igmp->interface, from, group_addr, num_sources, sources);
981
982 allow(igmp, from, group_addr, num_sources, sources);
983}
984
985/*
986 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
987
988 When transmitting a group specific query, if the group timer is
989 larger than LMQT, the "Suppress Router-Side Processing" bit is set
990 in the query message.
991*/
992static void group_retransmit_group(struct igmp_group *group)
993{
994 char query_buf[PIM_IGMP_BUFSIZE_WRITE];
995 struct igmp_sock *igmp;
996 struct pim_interface *pim_ifp;
997 long lmqc; /* Last Member Query Count */
998 long lmqi_msec; /* Last Member Query Interval */
999 long lmqt_msec; /* Last Member Query Time */
1000 int s_flag;
1001
1002 igmp = group->group_igmp_sock;
1003 pim_ifp = igmp->interface->info;
1004
1005 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001006 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001007 lmqt_msec = lmqc * lmqi_msec;
1008
1009 /*
1010 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1011
1012 When transmitting a group specific query, if the group timer is
1013 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1014 in the query message.
1015 */
1016 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1017
1018 if (PIM_DEBUG_IGMP_TRACE) {
1019 char group_str[100];
1020 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1021 zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1022 group_str, igmp->interface->name, s_flag,
1023 group->group_specific_query_retransmit_count);
1024 }
1025
1026 /*
1027 RFC3376: 4.1.12. IP Destination Addresses for Queries
1028
1029 Group-Specific and Group-and-Source-Specific Queries are sent with
1030 an IP destination address equal to the multicast address of
1031 interest.
1032 */
1033
1034 pim_igmp_send_membership_query(group,
1035 igmp->fd,
1036 igmp->interface->name,
1037 query_buf,
1038 sizeof(query_buf),
1039 0 /* num_sources_tosend */,
1040 group->group_addr /* dst_addr */,
1041 group->group_addr /* group_addr */,
Leonard Herve236b0152009-08-11 15:51:52 -03001042 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001043 s_flag,
1044 igmp->querier_robustness_variable,
1045 igmp->querier_query_interval);
1046}
1047
1048/*
1049 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1050
1051 When building a group and source specific query for a group G, two
1052 separate query messages are sent for the group. The first one has
1053 the "Suppress Router-Side Processing" bit set and contains all the
1054 sources with retransmission state and timers greater than LMQT. The
1055 second has the "Suppress Router-Side Processing" bit clear and
1056 contains all the sources with retransmission state and timers lower
1057 or equal to LMQT. If either of the two calculated messages does not
1058 contain any sources, then its transmission is suppressed.
1059 */
1060static int group_retransmit_sources(struct igmp_group *group,
1061 int send_with_sflag_set)
1062{
1063 struct igmp_sock *igmp;
1064 struct pim_interface *pim_ifp;
1065 long lmqc; /* Last Member Query Count */
1066 long lmqi_msec; /* Last Member Query Interval */
1067 long lmqt_msec; /* Last Member Query Time */
1068 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1069 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1070 int query_buf1_max_sources;
1071 int query_buf2_max_sources;
1072 struct in_addr *source_addr1;
1073 struct in_addr *source_addr2;
1074 int num_sources_tosend1;
1075 int num_sources_tosend2;
1076 struct listnode *src_node;
1077 struct igmp_source *src;
1078 int num_retransmit_sources_left = 0;
1079
1080 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1081 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1082
1083 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1084 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1085
1086 igmp = group->group_igmp_sock;
1087 pim_ifp = igmp->interface->info;
1088
1089 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001090 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001091 lmqt_msec = lmqc * lmqi_msec;
1092
1093 /* Scan all group sources */
1094 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1095
1096 /* Source has retransmission state? */
1097 if (src->source_query_retransmit_count < 1)
1098 continue;
1099
1100 if (--src->source_query_retransmit_count > 0) {
1101 ++num_retransmit_sources_left;
1102 }
1103
1104 /* Copy source address into appropriate query buffer */
1105 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1106 *source_addr1 = src->source_addr;
1107 ++source_addr1;
1108 }
1109 else {
1110 *source_addr2 = src->source_addr;
1111 ++source_addr2;
1112 }
1113
1114 }
1115
1116 num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1117 num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1118
1119 if (PIM_DEBUG_IGMP_TRACE) {
1120 char group_str[100];
1121 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1122 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",
1123 group_str, igmp->interface->name,
1124 num_sources_tosend1,
1125 num_sources_tosend2,
1126 send_with_sflag_set,
1127 num_retransmit_sources_left);
1128 }
1129
1130 if (num_sources_tosend1 > 0) {
1131 /*
1132 Send group-and-source-specific query with s_flag set and all
1133 sources with timers greater than LMQT.
1134 */
1135
1136 if (send_with_sflag_set) {
1137
1138 query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
1139 if (num_sources_tosend1 > query_buf1_max_sources) {
1140 char group_str[100];
1141 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001142 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 -03001143 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1144 num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
1145 }
1146 else {
1147 /*
1148 RFC3376: 4.1.12. IP Destination Addresses for Queries
1149
1150 Group-Specific and Group-and-Source-Specific Queries are sent with
1151 an IP destination address equal to the multicast address of
1152 interest.
1153 */
1154
1155 pim_igmp_send_membership_query(group,
1156 igmp->fd,
1157 igmp->interface->name,
1158 query_buf1,
1159 sizeof(query_buf1),
1160 num_sources_tosend1,
1161 group->group_addr,
1162 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001163 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001164 1 /* s_flag */,
1165 igmp->querier_robustness_variable,
1166 igmp->querier_query_interval);
1167
1168 }
1169
1170 } /* send_with_sflag_set */
1171
1172 }
1173
1174 if (num_sources_tosend2 > 0) {
1175 /*
1176 Send group-and-source-specific query with s_flag clear and all
1177 sources with timers lower or equal to LMQT.
1178 */
1179
1180 query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1181 if (num_sources_tosend2 > query_buf2_max_sources) {
1182 char group_str[100];
1183 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001184 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 -03001185 __PRETTY_FUNCTION__, group_str, igmp->interface->name,
1186 num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
1187 }
1188 else {
1189 /*
1190 RFC3376: 4.1.12. IP Destination Addresses for Queries
1191
1192 Group-Specific and Group-and-Source-Specific Queries are sent with
1193 an IP destination address equal to the multicast address of
1194 interest.
1195 */
1196
1197 pim_igmp_send_membership_query(group,
1198 igmp->fd,
1199 igmp->interface->name,
1200 query_buf2,
1201 sizeof(query_buf2),
1202 num_sources_tosend2,
1203 group->group_addr,
1204 group->group_addr,
Leonard Herve236b0152009-08-11 15:51:52 -03001205 pim_ifp->igmp_specific_query_max_response_time_dsec,
Everton Marques871dbcf2009-08-11 15:43:05 -03001206 0 /* s_flag */,
1207 igmp->querier_robustness_variable,
1208 igmp->querier_query_interval);
1209
1210 }
1211 }
1212
1213 return num_retransmit_sources_left;
1214}
1215
1216static int igmp_group_retransmit(struct thread *t)
1217{
1218 struct igmp_group *group;
1219 int num_retransmit_sources_left;
1220 int send_with_sflag_set; /* boolean */
1221
1222 zassert(t);
1223 group = THREAD_ARG(t);
1224 zassert(group);
1225
1226 if (PIM_DEBUG_IGMP_TRACE) {
1227 char group_str[100];
1228 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1229 zlog_debug("group_retransmit_timer: group %s on %s",
1230 group_str, group->group_igmp_sock->interface->name);
1231 }
1232
1233 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1234 if (group->group_specific_query_retransmit_count > 0) {
1235
1236 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1237 group_retransmit_group(group);
1238 --group->group_specific_query_retransmit_count;
1239
1240 /*
1241 RFC3376: 6.6.3.2
1242 If a group specific query is scheduled to be transmitted at the
1243 same time as a group and source specific query for the same group,
1244 then transmission of the group and source specific message with the
1245 "Suppress Router-Side Processing" bit set may be suppressed.
1246 */
1247 send_with_sflag_set = 0; /* boolean=false */
1248 }
1249 else {
1250 send_with_sflag_set = 1; /* boolean=true */
1251 }
1252
1253 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1254 num_retransmit_sources_left = group_retransmit_sources(group,
1255 send_with_sflag_set);
1256
1257 group->t_group_query_retransmit_timer = 0;
1258
1259 /*
1260 Keep group retransmit timer running if there is any retransmit
1261 counter pending
1262 */
1263 if ((num_retransmit_sources_left > 0) ||
1264 (group->group_specific_query_retransmit_count > 0)) {
1265 group_retransmit_timer_on(group);
1266 }
1267
1268 return 0;
1269}
1270
1271/*
1272 group_retransmit_timer_on:
1273 if group retransmit timer isn't running, starts it;
1274 otherwise, do nothing
1275*/
1276static void group_retransmit_timer_on(struct igmp_group *group)
1277{
1278 struct igmp_sock *igmp;
1279 struct pim_interface *pim_ifp;
1280 long lmqi_msec; /* Last Member Query Interval */
1281
1282 /* if group retransmit timer is running, do nothing */
1283 if (group->t_group_query_retransmit_timer) {
1284 return;
1285 }
1286
1287 igmp = group->group_igmp_sock;
1288 pim_ifp = igmp->interface->info;
1289
Leonard Herve236b0152009-08-11 15:51:52 -03001290 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001291
1292 if (PIM_DEBUG_IGMP_TRACE) {
1293 char group_str[100];
1294 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1295 zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1296 lmqi_msec / 1000,
1297 lmqi_msec % 1000,
1298 group_str,
1299 igmp->interface->name);
1300 }
1301
1302 THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
1303 igmp_group_retransmit,
1304 group, lmqi_msec);
1305}
1306
1307static long igmp_group_timer_remain_msec(struct igmp_group *group)
1308{
1309 return pim_time_timer_remain_msec(group->t_group_timer);
1310}
1311
1312static long igmp_source_timer_remain_msec(struct igmp_source *source)
1313{
1314 return pim_time_timer_remain_msec(source->t_source_timer);
1315}
1316
1317/*
1318 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1319*/
1320static void group_query_send(struct igmp_group *group)
1321{
1322 long lmqc; /* Last Member Query Count */
1323
1324 lmqc = group->group_igmp_sock->querier_robustness_variable;
1325
1326 /* lower group timer to lmqt */
1327 igmp_group_timer_lower_to_lmqt(group);
1328
1329 /* reset retransmission counter */
1330 group->group_specific_query_retransmit_count = lmqc;
1331
1332 /* immediately send group specific query (decrease retransmit counter by 1)*/
1333 group_retransmit_group(group);
1334
1335 /* make sure group retransmit timer is running */
1336 group_retransmit_timer_on(group);
1337}
1338
1339/*
1340 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1341*/
1342static void source_query_send_by_flag(struct igmp_group *group,
1343 int num_sources_tosend)
1344{
1345 struct igmp_sock *igmp;
1346 struct pim_interface *pim_ifp;
1347 struct listnode *src_node;
1348 struct igmp_source *src;
1349 long lmqc; /* Last Member Query Count */
1350 long lmqi_msec; /* Last Member Query Interval */
1351 long lmqt_msec; /* Last Member Query Time */
1352
1353 zassert(num_sources_tosend > 0);
1354
1355 igmp = group->group_igmp_sock;
1356 pim_ifp = igmp->interface->info;
1357
1358 lmqc = igmp->querier_robustness_variable;
Leonard Herve236b0152009-08-11 15:51:52 -03001359 lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001360 lmqt_msec = lmqc * lmqi_msec;
1361
1362 /*
1363 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1364
1365 (...) for each of the sources in X of group G, with source timer larger
1366 than LMQT:
1367 o Set number of retransmissions for each source to [Last Member
1368 Query Count].
1369 o Lower source timer to LMQT.
1370 */
1371 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1372 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1373 /* source "src" in X of group G */
1374 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1375 src->source_query_retransmit_count = lmqc;
1376 igmp_source_timer_lower_to_lmqt(src);
1377 }
1378 }
1379 }
1380
1381 /* send group-and-source specific queries */
1382 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1383
1384 /* make sure group retransmit timer is running */
1385 group_retransmit_timer_on(group);
1386}
1387
1388static void block_excl(struct igmp_group *group,
1389 int num_sources, struct in_addr *sources)
1390{
1391 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +02001392 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -03001393
1394 /* 1. clear off SEND flag from all known sources (X,Y) */
1395 source_clear_send_flag(group->group_source_list);
1396
1397 /* 2. scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +02001398 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -03001399 struct igmp_source *source;
1400 struct in_addr *src_addr;
1401
1402 src_addr = sources + i;
1403
1404 /* lookup reported source (A) in known sources (X,Y) */
1405 source = igmp_find_source_by_addr(group, *src_addr);
1406 if (!source) {
1407 /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
1408 long group_timer_msec;
1409 source = source_new(group, *src_addr,
1410 group->group_igmp_sock->interface->name);
1411 if (!source) {
1412 /* ugh, internal malloc failure, skip source */
1413 continue;
1414 }
1415
1416 zassert(!source->t_source_timer); /* timer == 0 */
1417 group_timer_msec = igmp_group_timer_remain_msec(group);
1418 igmp_source_timer_on(group, source, group_timer_msec);
1419 zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1420 }
1421
1422 if (source->t_source_timer) {
1423 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1424 IGMP_SOURCE_DO_SEND(source->source_flags);
1425 ++num_sources_tosend;
1426 }
1427 }
1428
1429 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1430 if (num_sources_tosend > 0) {
1431 source_query_send_by_flag(group, num_sources_tosend);
1432 }
1433}
1434
1435static void block_incl(struct igmp_group *group,
1436 int num_sources, struct in_addr *sources)
1437{
1438 int num_sources_tosend = 0;
Leonard Herve942b0fd2009-08-14 15:49:06 +02001439 int i;
Everton Marques871dbcf2009-08-11 15:43:05 -03001440
1441 /* 1. clear off SEND flag from all known sources (B) */
1442 source_clear_send_flag(group->group_source_list);
1443
1444 /* 2. scan received sources (A) */
Leonard Herve942b0fd2009-08-14 15:49:06 +02001445 for (i = 0; i < num_sources; ++i) {
Everton Marques871dbcf2009-08-11 15:43:05 -03001446 struct igmp_source *source;
1447 struct in_addr *src_addr;
1448
1449 src_addr = sources + i;
1450
1451 /* lookup reported source (A) in known sources (B) */
1452 source = igmp_find_source_by_addr(group, *src_addr);
1453 if (source) {
1454 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1455 IGMP_SOURCE_DO_SEND(source->source_flags);
1456 ++num_sources_tosend;
1457 }
1458 }
1459
1460 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1461 if (num_sources_tosend > 0) {
1462 source_query_send_by_flag(group, num_sources_tosend);
1463 }
1464}
1465
1466void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
1467 struct in_addr group_addr,
1468 int num_sources, struct in_addr *sources)
1469{
1470 struct interface *ifp = igmp->interface;
Everton Marques871dbcf2009-08-11 15:43:05 -03001471 struct igmp_group *group;
1472
1473 on_trace(__PRETTY_FUNCTION__,
1474 ifp, from, group_addr, num_sources, sources);
1475
Everton Marques871dbcf2009-08-11 15:43:05 -03001476 /* non-existant group is created as INCLUDE {empty} */
1477 group = igmp_add_group_by_addr(igmp, group_addr, ifp->name);
1478 if (!group) {
1479 return;
1480 }
1481
1482 if (group->group_filtermode_isexcl) {
1483 /* EXCLUDE mode */
1484 block_excl(group, num_sources, sources);
1485 }
1486 else {
1487 /* INCLUDE mode */
1488 block_incl(group, num_sources, sources);
1489 }
1490}
1491
1492void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
1493{
1494 struct igmp_sock *igmp;
1495 struct interface *ifp;
1496 struct pim_interface *pim_ifp;
1497 char *ifname;
1498 int lmqi_dsec; /* Last Member Query Interval */
1499 int lmqc; /* Last Member Query Count */
1500 int lmqt_msec; /* Last Member Query Time */
1501
1502 /*
1503 RFC 3376: 6.2.2. Definition of Group Timers
1504
1505 The group timer is only used when a group is in EXCLUDE mode and
1506 it represents the time for the *filter-mode* of the group to
1507 expire and switch to INCLUDE mode.
1508 */
1509 if (!group->group_filtermode_isexcl) {
1510 return;
1511 }
1512
1513 igmp = group->group_igmp_sock;
1514 ifp = igmp->interface;
1515 pim_ifp = ifp->info;
1516 ifname = ifp->name;
1517
Leonard Herve236b0152009-08-11 15:51:52 -03001518 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001519 lmqc = igmp->querier_robustness_variable;
1520 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1521
1522 if (PIM_DEBUG_IGMP_TRACE) {
1523 char group_str[100];
1524 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1525 zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1526 __PRETTY_FUNCTION__,
1527 group_str, ifname,
1528 lmqc, lmqi_dsec, lmqt_msec);
1529 }
1530
1531 zassert(group->group_filtermode_isexcl);
1532
1533 igmp_group_timer_on(group, lmqt_msec, ifname);
1534}
1535
1536void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
1537{
1538 struct igmp_group *group;
1539 struct igmp_sock *igmp;
1540 struct interface *ifp;
1541 struct pim_interface *pim_ifp;
1542 char *ifname;
1543 int lmqi_dsec; /* Last Member Query Interval */
1544 int lmqc; /* Last Member Query Count */
1545 int lmqt_msec; /* Last Member Query Time */
1546
1547 group = source->source_group;
1548 igmp = group->group_igmp_sock;
1549 ifp = igmp->interface;
1550 pim_ifp = ifp->info;
1551 ifname = ifp->name;
1552
Leonard Herve236b0152009-08-11 15:51:52 -03001553 lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
Everton Marques871dbcf2009-08-11 15:43:05 -03001554 lmqc = igmp->querier_robustness_variable;
1555 lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1556
1557 if (PIM_DEBUG_IGMP_TRACE) {
1558 char group_str[100];
1559 char source_str[100];
1560 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1561 pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
1562 zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1563 __PRETTY_FUNCTION__,
1564 group_str, source_str, ifname,
1565 lmqc, lmqi_dsec, lmqt_msec);
1566 }
1567
1568 igmp_source_timer_on(group, source, lmqt_msec);
1569}
1570
1571/*
1572 Copy sources to message:
1573
1574 struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
1575 if (num_sources > 0) {
1576 struct listnode *node;
1577 struct igmp_source *src;
1578 int i = 0;
1579
1580 for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
1581 sources[i++] = src->source_addr;
1582 }
1583 }
1584*/
1585void pim_igmp_send_membership_query(struct igmp_group *group,
1586 int fd,
1587 const char *ifname,
1588 char *query_buf,
1589 int query_buf_size,
1590 int num_sources,
1591 struct in_addr dst_addr,
1592 struct in_addr group_addr,
1593 int query_max_response_time_dsec,
1594 uint8_t s_flag,
1595 uint8_t querier_robustness_variable,
1596 uint16_t querier_query_interval)
1597{
1598 ssize_t msg_size;
1599 uint8_t max_resp_code;
1600 uint8_t qqic;
1601 ssize_t sent;
1602 struct sockaddr_in to;
1603 socklen_t tolen;
1604 uint16_t checksum;
1605
1606 zassert(num_sources >= 0);
1607
1608 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1609 if (msg_size > query_buf_size) {
David Lamparter5c697982012-02-16 04:47:56 +01001610 zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
Everton Marques871dbcf2009-08-11 15:43:05 -03001611 __FILE__, __PRETTY_FUNCTION__,
1612 msg_size, query_buf_size);
1613 return;
1614 }
1615
1616 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1617 zassert((s_flag == 0) || (s_flag == 1));
1618
1619 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1620 qqic = igmp_msg_encode16to8(querier_query_interval);
1621
1622 /*
1623 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1624
1625 If non-zero, the QRV field contains the [Robustness Variable]
1626 value used by the querier, i.e., the sender of the Query. If the
1627 querier's [Robustness Variable] exceeds 7, the maximum value of
1628 the QRV field, the QRV is set to zero.
1629 */
1630 if (querier_robustness_variable > 7) {
1631 querier_robustness_variable = 0;
1632 }
1633
1634 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1635 query_buf[1] = max_resp_code;
1636 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
Klemen Sladic3defeb32014-02-07 16:23:44 +13001637 memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
1638
Everton Marques871dbcf2009-08-11 15:43:05 -03001639 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1640 query_buf[9] = qqic;
1641 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
1642
1643 checksum = pim_inet_checksum(query_buf, msg_size);
1644 *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
1645
1646 if (PIM_DEBUG_IGMP_PACKETS) {
1647 char dst_str[100];
1648 char group_str[100];
1649 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1650 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
David Lamparter5c697982012-02-16 04:47:56 +01001651 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 -03001652 __PRETTY_FUNCTION__,
1653 dst_str, ifname, group_str, num_sources,
1654 msg_size, s_flag, querier_robustness_variable,
1655 querier_query_interval, qqic, checksum);
1656 }
1657
1658#if 0
1659 memset(&to, 0, sizeof(to));
1660#endif
1661 to.sin_family = AF_INET;
1662 to.sin_addr = dst_addr;
1663#if 0
1664 to.sin_port = htons(0);
1665#endif
1666 tolen = sizeof(to);
1667
1668 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT, &to, tolen);
1669 if (sent != (ssize_t) msg_size) {
1670 int e = errno;
1671 char dst_str[100];
1672 char group_str[100];
1673 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1674 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1675 if (sent < 0) {
David Lamparter5c697982012-02-16 04:47:56 +01001676 zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
Everton Marques871dbcf2009-08-11 15:43:05 -03001677 __PRETTY_FUNCTION__,
1678 dst_str, ifname, group_str, msg_size,
Everton Marquese96f0af2009-08-11 15:48:02 -03001679 e, safe_strerror(e));
Everton Marques871dbcf2009-08-11 15:43:05 -03001680 }
1681 else {
David Lamparter5c697982012-02-16 04:47:56 +01001682 zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
Everton Marques871dbcf2009-08-11 15:43:05 -03001683 __PRETTY_FUNCTION__,
1684 dst_str, ifname, group_str,
1685 msg_size, sent);
1686 }
1687 return;
1688 }
1689
1690 /*
1691 s_flag sanity test: s_flag must be set for general queries
1692
1693 RFC 3376: 6.6.1. Timer Updates
1694
1695 When a router sends or receives a query with a clear Suppress
1696 Router-Side Processing flag, it must update its timers to reflect
1697 the correct timeout values for the group or sources being queried.
1698
1699 General queries don't trigger timer update.
1700 */
1701 if (!s_flag) {
1702 /* general query? */
1703 if (PIM_INADDR_IS_ANY(group_addr)) {
1704 char dst_str[100];
1705 char group_str[100];
1706 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1707 pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
1708 zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1709 __PRETTY_FUNCTION__,
1710 dst_str, ifname, group_str, num_sources);
1711 }
1712 }
1713
1714}