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