]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmpv3.c
pimd: Fix indentation for struct igmp_stats
[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 along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <zebra.h>
21 #include "log.h"
22 #include "memory.h"
23 #include "if.h"
24 #include "lib_errors.h"
25
26 #include "pimd.h"
27 #include "pim_iface.h"
28 #include "pim_igmp.h"
29 #include "pim_igmpv3.h"
30 #include "pim_str.h"
31 #include "pim_util.h"
32 #include "pim_time.h"
33 #include "pim_zebra.h"
34 #include "pim_oil.h"
35
36 static void group_retransmit_timer_on(struct gm_group *group);
37 static long igmp_group_timer_remain_msec(struct gm_group *group);
38 static long igmp_source_timer_remain_msec(struct gm_source *source);
39 static void group_query_send(struct gm_group *group);
40 static void source_query_send_by_flag(struct gm_group *group,
41 int num_sources_tosend);
42
43 static void on_trace(const char *label, struct interface *ifp,
44 struct in_addr from, struct in_addr group_addr,
45 int num_sources, struct in_addr *sources)
46 {
47 if (PIM_DEBUG_IGMP_TRACE) {
48 char from_str[INET_ADDRSTRLEN];
49 char group_str[INET_ADDRSTRLEN];
50
51 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
52 pim_inet4_dump("<group?>", group_addr, group_str,
53 sizeof(group_str));
54
55 zlog_debug("%s: from %s on %s: group=%s sources=%d", label,
56 from_str, ifp->name, group_str, num_sources);
57 }
58 }
59
60 static inline long igmp_gmi_msec(struct gm_group *group)
61 {
62 struct pim_interface *pim_ifp = group->interface->info;
63 struct gm_sock *igmp;
64 struct listnode *sock_node;
65
66 long qrv = 0, qqi = 0;
67
68 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
69 qrv = MAX(qrv, igmp->querier_robustness_variable);
70 qqi = MAX(qqi, igmp->querier_query_interval);
71 }
72 return PIM_IGMP_GMI_MSEC(qrv, qqi,
73 pim_ifp->gm_query_max_response_time_dsec);
74 }
75
76 void igmp_group_reset_gmi(struct gm_group *group)
77 {
78 long group_membership_interval_msec;
79 struct interface *ifp;
80
81 ifp = group->interface;
82
83 /*
84 RFC 3376: 8.4. Group Membership Interval
85
86 The Group Membership Interval is the amount of time that must pass
87 before a multicast router decides there are no more members of a
88 group or a particular source on a network.
89
90 This value MUST be ((the Robustness Variable) times (the Query
91 Interval)) plus (one Query Response Interval).
92
93 group_membership_interval_msec = querier_robustness_variable *
94 (1000 * querier_query_interval) +
95 100 * query_response_interval_dsec;
96 */
97 group_membership_interval_msec = igmp_gmi_msec(group);
98
99 if (PIM_DEBUG_IGMP_TRACE) {
100 char group_str[INET_ADDRSTRLEN];
101 pim_inet4_dump("<group?>", group->group_addr, group_str,
102 sizeof(group_str));
103 zlog_debug(
104 "Resetting group %s timer to GMI=%ld.%03ld sec on %s",
105 group_str, group_membership_interval_msec / 1000,
106 group_membership_interval_msec % 1000, ifp->name);
107 }
108
109 /*
110 RFC 3376: 6.2.2. Definition of Group Timers
111
112 The group timer is only used when a group is in EXCLUDE mode and
113 it represents the time for the *filter-mode* of the group to
114 expire and switch to INCLUDE mode.
115 */
116 assert(group->group_filtermode_isexcl);
117
118 igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
119 }
120
121 static void igmp_source_timer(struct thread *t)
122 {
123 struct gm_source *source;
124 struct gm_group *group;
125
126 source = THREAD_ARG(t);
127
128 group = source->source_group;
129
130 if (PIM_DEBUG_IGMP_TRACE) {
131 char group_str[INET_ADDRSTRLEN];
132 char source_str[INET_ADDRSTRLEN];
133 pim_inet4_dump("<group?>", group->group_addr, group_str,
134 sizeof(group_str));
135 pim_inet4_dump("<source?>", source->source_addr, source_str,
136 sizeof(source_str));
137 zlog_debug(
138 "%s: Source timer expired for group %s source %s on %s",
139 __func__, group_str, source_str,
140 group->interface->name);
141 }
142
143 /*
144 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
145
146 Group
147 Filter-Mode Source Timer Value Action
148 ----------- ------------------ ------
149 INCLUDE TIMER == 0 Suggest to stop forwarding
150 traffic from source and
151 remove source record. If
152 there are no more source
153 records for the group, delete
154 group record.
155
156 EXCLUDE TIMER == 0 Suggest to not forward
157 traffic from source
158 (DO NOT remove record)
159
160 Source timer switched from (T > 0) to (T == 0): disable forwarding.
161 */
162
163 if (group->group_filtermode_isexcl) {
164 /* EXCLUDE mode */
165
166 igmp_source_forward_stop(source);
167 } else {
168 /* INCLUDE mode */
169
170 /* igmp_source_delete() will stop forwarding source */
171 igmp_source_delete(source);
172
173 /*
174 If there are no more source records for the group, delete
175 group
176 record.
177 */
178 if (!listcount(group->group_source_list)) {
179 igmp_group_delete_empty_include(group);
180 }
181 }
182 }
183
184 static void source_timer_off(struct gm_group *group, struct gm_source *source)
185 {
186 if (!source->t_source_timer)
187 return;
188
189 if (PIM_DEBUG_IGMP_TRACE) {
190 char group_str[INET_ADDRSTRLEN];
191 char source_str[INET_ADDRSTRLEN];
192 pim_inet4_dump("<group?>", group->group_addr, group_str,
193 sizeof(group_str));
194 pim_inet4_dump("<source?>", source->source_addr, source_str,
195 sizeof(source_str));
196 zlog_debug(
197 "Cancelling TIMER event for group %s source %s on %s",
198 group_str, source_str, group->interface->name);
199 }
200
201 THREAD_OFF(source->t_source_timer);
202 }
203
204 static void igmp_source_timer_on(struct gm_group *group,
205 struct gm_source *source, long interval_msec)
206 {
207 source_timer_off(group, source);
208 struct pim_interface *pim_ifp = group->interface->info;
209
210 if (PIM_DEBUG_IGMP_EVENTS) {
211 char group_str[INET_ADDRSTRLEN];
212 char source_str[INET_ADDRSTRLEN];
213 pim_inet4_dump("<group?>", group->group_addr, group_str,
214 sizeof(group_str));
215 pim_inet4_dump("<source?>", source->source_addr, source_str,
216 sizeof(source_str));
217 zlog_debug(
218 "Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
219 interval_msec / 1000, interval_msec % 1000, group_str,
220 source_str, group->interface->name);
221 }
222
223 thread_add_timer_msec(router->master, igmp_source_timer, source,
224 interval_msec, &source->t_source_timer);
225
226 /*
227 RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
228
229 Source timer switched from (T == 0) to (T > 0): enable forwarding.
230 */
231 igmp_source_forward_start(pim_ifp->pim, source);
232 }
233
234 void igmp_source_reset_gmi(struct gm_group *group, struct gm_source *source)
235 {
236 long group_membership_interval_msec;
237 struct interface *ifp;
238
239 ifp = group->interface;
240
241 group_membership_interval_msec = igmp_gmi_msec(group);
242
243 if (PIM_DEBUG_IGMP_TRACE) {
244 char group_str[INET_ADDRSTRLEN];
245 char source_str[INET_ADDRSTRLEN];
246
247 pim_inet4_dump("<group?>", group->group_addr, group_str,
248 sizeof(group_str));
249 pim_inet4_dump("<source?>", source->source_addr, source_str,
250 sizeof(source_str));
251
252 zlog_debug(
253 "Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
254 source_str, group_membership_interval_msec / 1000,
255 group_membership_interval_msec % 1000, group_str,
256 ifp->name);
257 }
258
259 igmp_source_timer_on(group, source, group_membership_interval_msec);
260 }
261
262 static void source_mark_delete_flag(struct gm_group *group)
263 {
264 struct listnode *src_node;
265 struct gm_source *src;
266
267 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
268 IGMP_SOURCE_DO_DELETE(src->source_flags);
269 }
270 }
271
272 static void source_mark_send_flag(struct gm_group *group)
273 {
274 struct listnode *src_node;
275 struct gm_source *src;
276
277 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
278 IGMP_SOURCE_DO_SEND(src->source_flags);
279 }
280 }
281
282 static int source_mark_send_flag_by_timer(struct gm_group *group)
283 {
284 struct listnode *src_node;
285 struct gm_source *src;
286 int num_marked_sources = 0;
287
288 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
289 /* Is source timer running? */
290 if (src->t_source_timer) {
291 IGMP_SOURCE_DO_SEND(src->source_flags);
292 ++num_marked_sources;
293 } else {
294 IGMP_SOURCE_DONT_SEND(src->source_flags);
295 }
296 }
297
298 return num_marked_sources;
299 }
300
301 static void source_clear_send_flag(struct list *source_list)
302 {
303 struct listnode *src_node;
304 struct gm_source *src;
305
306 for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
307 IGMP_SOURCE_DONT_SEND(src->source_flags);
308 }
309 }
310
311 /*
312 Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
313 */
314 static void group_exclude_fwd_anysrc_ifempty(struct gm_group *group)
315 {
316 struct pim_interface *pim_ifp = group->interface->info;
317
318 assert(group->group_filtermode_isexcl);
319
320 if (listcount(group->group_source_list) < 1) {
321 igmp_anysource_forward_start(pim_ifp->pim, group);
322 }
323 }
324
325 void igmp_source_free(struct gm_source *source)
326 {
327 /* make sure there is no source timer running */
328 assert(!source->t_source_timer);
329
330 XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
331 }
332
333 static void source_channel_oil_detach(struct gm_source *source)
334 {
335 if (source->source_channel_oil) {
336 pim_channel_oil_del(source->source_channel_oil, __func__);
337 source->source_channel_oil = NULL;
338 }
339 }
340
341 /*
342 igmp_source_delete: stop fowarding, and delete the source
343 igmp_source_forward_stop: stop fowarding, but keep the source
344 */
345 void igmp_source_delete(struct gm_source *source)
346 {
347 struct gm_group *group;
348 struct in_addr src;
349
350 group = source->source_group;
351
352 if (PIM_DEBUG_IGMP_TRACE) {
353 char group_str[INET_ADDRSTRLEN];
354 char source_str[INET_ADDRSTRLEN];
355 pim_inet4_dump("<group?>", group->group_addr, group_str,
356 sizeof(group_str));
357 pim_inet4_dump("<source?>", source->source_addr, source_str,
358 sizeof(source_str));
359 zlog_debug(
360 "Deleting IGMP source %s for group %s from interface %s c_oil ref_count %d",
361 source_str, group_str, group->interface->name,
362 source->source_channel_oil
363 ? source->source_channel_oil->oil_ref_count
364 : 0);
365 }
366
367 source_timer_off(group, source);
368 igmp_source_forward_stop(source);
369
370 /* sanity check that forwarding has been disabled */
371 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
372 char group_str[INET_ADDRSTRLEN];
373 char source_str[INET_ADDRSTRLEN];
374 pim_inet4_dump("<group?>", group->group_addr, group_str,
375 sizeof(group_str));
376 pim_inet4_dump("<source?>", source->source_addr, source_str,
377 sizeof(source_str));
378 zlog_warn(
379 "%s: forwarding=ON(!) IGMP source %s for group %s from interface %s",
380 __func__, source_str, group_str,
381 group->interface->name);
382 /* warning only */
383 }
384
385 source_channel_oil_detach(source);
386
387 /*
388 notice that listnode_delete() can't be moved
389 into igmp_source_free() because the later is
390 called by list_delete_all_node()
391 */
392 listnode_delete(group->group_source_list, source);
393
394 src.s_addr = source->source_addr.s_addr;
395 igmp_source_free(source);
396
397 /* Group source list is empty and current source is * then
398 *,G group going away so do not trigger start */
399 if (group->group_filtermode_isexcl
400 && (listcount(group->group_source_list) != 0)
401 && src.s_addr != INADDR_ANY) {
402 group_exclude_fwd_anysrc_ifempty(group);
403 }
404 }
405
406 static void source_delete_by_flag(struct list *source_list)
407 {
408 struct listnode *src_node;
409 struct listnode *src_nextnode;
410 struct gm_source *src;
411
412 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
413 if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
414 igmp_source_delete(src);
415 }
416
417 void igmp_source_delete_expired(struct list *source_list)
418 {
419 struct listnode *src_node;
420 struct listnode *src_nextnode;
421 struct gm_source *src;
422
423 for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
424 if (!src->t_source_timer)
425 igmp_source_delete(src);
426 }
427
428 struct gm_source *igmp_find_source_by_addr(struct gm_group *group,
429 struct in_addr src_addr)
430 {
431 struct listnode *src_node;
432 struct gm_source *src;
433
434 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
435 if (src_addr.s_addr == src->source_addr.s_addr)
436 return src;
437
438 return 0;
439 }
440
441 struct gm_source *igmp_get_source_by_addr(struct gm_group *group,
442 struct in_addr src_addr, bool *new)
443 {
444 struct gm_source *src;
445
446 if (new)
447 *new = false;
448
449 src = igmp_find_source_by_addr(group, src_addr);
450 if (src)
451 return src;
452
453 if (PIM_DEBUG_IGMP_TRACE) {
454 char group_str[INET_ADDRSTRLEN];
455 char source_str[INET_ADDRSTRLEN];
456 pim_inet4_dump("<group?>", group->group_addr, group_str,
457 sizeof(group_str));
458 pim_inet4_dump("<source?>", src_addr, source_str,
459 sizeof(source_str));
460 zlog_debug(
461 "Creating new IGMP source %s for group %s on interface %s",
462 source_str, group_str, group->interface->name);
463 }
464
465 src = XCALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
466
467 src->t_source_timer = NULL;
468 src->source_group = group; /* back pointer */
469 src->source_addr = src_addr;
470 src->source_creation = pim_time_monotonic_sec();
471 src->source_flags = 0;
472 src->source_query_retransmit_count = 0;
473 src->source_channel_oil = NULL;
474
475 listnode_add(group->group_source_list, src);
476
477 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
478 igmp_anysource_forward_stop(group);
479 return src;
480 }
481
482 static void allow(struct gm_sock *igmp, struct in_addr from,
483 struct in_addr group_addr, int num_sources,
484 struct in_addr *sources)
485 {
486 struct gm_source *source;
487 struct gm_group *group;
488 int i;
489
490 if (num_sources == 0) {
491 /*
492 RFC 3376: 3.1. Socket-State
493 If the requested filter mode is INCLUDE *and* the requested
494 source list is empty, then the entry corresponding to the
495 requested interface and multicast address is deleted if
496 present. If no such entry is present, the request is ignored.
497 So, deleting the group present.
498 */
499 group = find_group_by_addr(igmp, group_addr);
500 if (!group) {
501 return;
502 }
503 if (group->group_filtermode_isexcl) {
504 if (listcount(group->group_source_list) == 1) {
505 struct in_addr star = {.s_addr = INADDR_ANY};
506
507 source = igmp_find_source_by_addr(group, star);
508 if (source)
509 igmp_source_reset_gmi(group, source);
510 }
511 } else {
512 igmp_group_delete(group);
513 }
514
515 return;
516 }
517
518 /* non-existant group is created as INCLUDE {empty} */
519 group = igmp_add_group_by_addr(igmp, group_addr);
520 if (!group) {
521 return;
522 }
523
524 /* scan received sources */
525 for (i = 0; i < num_sources; ++i) {
526 struct in_addr *src_addr;
527
528 src_addr = sources + i;
529
530 source = igmp_get_source_by_addr(group, *src_addr, NULL);
531 if (!source)
532 continue;
533
534 /*
535 RFC 3376: 6.4.1. Reception of Current-State Records
536
537 When receiving IS_IN reports for groups in EXCLUDE mode is
538 sources should be moved from set with (timers = 0) to set with
539 (timers > 0).
540
541 igmp_source_reset_gmi() below, resetting the source timers to
542 GMI, accomplishes this.
543 */
544 igmp_source_reset_gmi(group, source);
545
546 } /* scan received sources */
547 }
548
549 void igmpv3_report_isin(struct gm_sock *igmp, struct in_addr from,
550 struct in_addr group_addr, int num_sources,
551 struct in_addr *sources)
552 {
553 on_trace(__func__, igmp->interface, from, group_addr, num_sources,
554 sources);
555
556 allow(igmp, from, group_addr, num_sources, sources);
557 }
558
559 static void isex_excl(struct gm_group *group, int num_sources,
560 struct in_addr *sources)
561 {
562 struct gm_source *source;
563 int i;
564
565 /* EXCLUDE mode */
566 assert(group->group_filtermode_isexcl);
567
568 /* E.1: set deletion flag for known sources (X,Y) */
569 source_mark_delete_flag(group);
570
571 /* scan received sources (A) */
572 for (i = 0; i < num_sources; ++i) {
573 struct in_addr *src_addr;
574 bool new;
575
576 src_addr = sources + i;
577
578 /* E.2: lookup reported source from (A) in (X,Y) */
579 source = igmp_get_source_by_addr(group, *src_addr, &new);
580 if (!source)
581 continue;
582
583 if (!new) {
584 /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
585 IGMP_SOURCE_DONT_DELETE(source->source_flags);
586 } else {
587 /* E.4: if not found, create source with timer=GMI:
588 * (A-X-Y) */
589 assert(!source->t_source_timer); /* timer == 0 */
590 igmp_source_reset_gmi(group, source);
591 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
592 }
593
594 } /* scan received sources */
595
596 /*
597 * If we are in isexcl mode and num_sources == 0
598 * than that means we have a *,g entry that
599 * needs to be handled
600 */
601 if (group->group_filtermode_isexcl && num_sources == 0) {
602 struct in_addr star = {.s_addr = INADDR_ANY};
603 source = igmp_find_source_by_addr(group, star);
604 if (source) {
605 IGMP_SOURCE_DONT_DELETE(source->source_flags);
606 igmp_source_reset_gmi(group, source);
607 }
608 }
609
610 /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
611 source_delete_by_flag(group->group_source_list);
612 }
613
614 static void isex_incl(struct gm_group *group, int num_sources,
615 struct in_addr *sources)
616 {
617 int i;
618
619 /* INCLUDE mode */
620 assert(!group->group_filtermode_isexcl);
621
622 /* I.1: set deletion flag for known sources (A) */
623 source_mark_delete_flag(group);
624
625 /* scan received sources (B) */
626 for (i = 0; i < num_sources; ++i) {
627 struct gm_source *source;
628 struct in_addr *src_addr;
629 bool new;
630
631 src_addr = sources + i;
632
633 /* I.2: lookup reported source (B) */
634 source = igmp_get_source_by_addr(group, *src_addr, &new);
635 if (!source)
636 continue;
637
638 if (!new) {
639 /* I.3: if found, clear deletion flag (A*B) */
640 IGMP_SOURCE_DONT_DELETE(source->source_flags);
641 } else {
642 /* I.4: if not found, create source with timer=0 (B-A)
643 */
644 assert(!source->t_source_timer); /* (B-A) timer=0 */
645 }
646
647 } /* scan received sources */
648
649 /* I.5: delete all sources marked with deletion flag (A-B) */
650 source_delete_by_flag(group->group_source_list);
651
652 group->group_filtermode_isexcl = 1; /* boolean=true */
653
654 assert(group->group_filtermode_isexcl);
655
656 group_exclude_fwd_anysrc_ifempty(group);
657 }
658
659 void igmpv3_report_isex(struct gm_sock *igmp, struct in_addr from,
660 struct in_addr group_addr, int num_sources,
661 struct in_addr *sources, int from_igmp_v2_report)
662 {
663 struct interface *ifp = igmp->interface;
664 struct gm_group *group;
665
666 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
667
668 if (pim_is_group_filtered(ifp->info, &group_addr))
669 return;
670
671 /* non-existant group is created as INCLUDE {empty} */
672 group = igmp_add_group_by_addr(igmp, group_addr);
673 if (!group) {
674 return;
675 }
676
677 /* So we can display how we learned the group in our show command output
678 */
679 if (from_igmp_v2_report)
680 group->igmp_version = 2;
681
682 if (group->group_filtermode_isexcl) {
683 /* EXCLUDE mode */
684 isex_excl(group, num_sources, sources);
685 } else {
686 /* INCLUDE mode */
687 isex_incl(group, num_sources, sources);
688 assert(group->group_filtermode_isexcl);
689 }
690
691 assert(group->group_filtermode_isexcl);
692
693 igmp_group_reset_gmi(group);
694 }
695
696 static void toin_incl(struct gm_group *group, int num_sources,
697 struct in_addr *sources)
698 {
699 int num_sources_tosend = listcount(group->group_source_list);
700 int i;
701
702 /* Set SEND flag for all known sources (A) */
703 source_mark_send_flag(group);
704
705 /* Scan received sources (B) */
706 for (i = 0; i < num_sources; ++i) {
707 struct gm_source *source;
708 struct in_addr *src_addr;
709 bool new;
710
711 src_addr = sources + i;
712
713 /* Lookup reported source (B) */
714 source = igmp_get_source_by_addr(group, *src_addr, &new);
715 if (!source)
716 continue;
717
718 if (!new) {
719 /* If found, clear SEND flag (A*B) */
720 IGMP_SOURCE_DONT_SEND(source->source_flags);
721 --num_sources_tosend;
722 }
723
724 /* (B)=GMI */
725 igmp_source_reset_gmi(group, source);
726 }
727
728 /* Send sources marked with SEND flag: Q(G,A-B) */
729 if (num_sources_tosend > 0) {
730 source_query_send_by_flag(group, num_sources_tosend);
731 }
732 }
733
734 static void toin_excl(struct gm_group *group, int num_sources,
735 struct in_addr *sources)
736 {
737 int num_sources_tosend;
738 int i;
739
740 /* Set SEND flag for X (sources with timer > 0) */
741 num_sources_tosend = source_mark_send_flag_by_timer(group);
742
743 /* Scan received sources (A) */
744 for (i = 0; i < num_sources; ++i) {
745 struct gm_source *source;
746 struct in_addr *src_addr;
747 bool new;
748
749 src_addr = sources + i;
750
751 /* Lookup reported source (A) */
752 source = igmp_get_source_by_addr(group, *src_addr, &new);
753 if (!source)
754 continue;
755
756 if (source->t_source_timer) {
757 /* If found and timer running, clear SEND flag
758 * (X*A) */
759 IGMP_SOURCE_DONT_SEND(source->source_flags);
760 --num_sources_tosend;
761 }
762
763 /* (A)=GMI */
764 igmp_source_reset_gmi(group, source);
765 }
766
767 /* Send sources marked with SEND flag: Q(G,X-A) */
768 if (num_sources_tosend > 0) {
769 source_query_send_by_flag(group, num_sources_tosend);
770 }
771
772 /* Send Q(G) */
773 group_query_send(group);
774 }
775
776 void igmpv3_report_toin(struct gm_sock *igmp, struct in_addr from,
777 struct in_addr group_addr, int num_sources,
778 struct in_addr *sources)
779 {
780 struct interface *ifp = igmp->interface;
781 struct gm_group *group;
782
783 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
784
785 /*
786 * If the requested filter mode is INCLUDE *and* the requested source
787 * list is empty, then the entry corresponding to the requested
788 * interface and multicast address is deleted if present. If no such
789 * entry is present, the request is ignored.
790 */
791 if (num_sources) {
792 /* non-existant group is created as INCLUDE {empty} */
793 group = igmp_add_group_by_addr(igmp, group_addr);
794 if (!group) {
795 return;
796 }
797 } else {
798 group = find_group_by_addr(igmp, group_addr);
799 if (!group)
800 return;
801 }
802
803 if (group->group_filtermode_isexcl) {
804 /* EXCLUDE mode */
805 toin_excl(group, num_sources, sources);
806 } else {
807 /* INCLUDE mode */
808 toin_incl(group, num_sources, sources);
809 }
810 }
811
812 static void toex_incl(struct gm_group *group, int num_sources,
813 struct in_addr *sources)
814 {
815 int num_sources_tosend = 0;
816 int i;
817
818 assert(!group->group_filtermode_isexcl);
819
820 /* Set DELETE flag for all known sources (A) */
821 source_mark_delete_flag(group);
822
823 /* Clear off SEND flag from all known sources (A) */
824 source_clear_send_flag(group->group_source_list);
825
826 /* Scan received sources (B) */
827 for (i = 0; i < num_sources; ++i) {
828 struct gm_source *source;
829 struct in_addr *src_addr;
830 bool new;
831
832 src_addr = sources + i;
833
834 /* Lookup reported source (B) */
835 source = igmp_get_source_by_addr(group, *src_addr, &new);
836 if (!new) {
837 /* If found, clear deletion flag: (A*B) */
838 IGMP_SOURCE_DONT_DELETE(source->source_flags);
839 /* and set SEND flag (A*B) */
840 IGMP_SOURCE_DO_SEND(source->source_flags);
841 ++num_sources_tosend;
842 }
843
844 } /* Scan received sources (B) */
845
846 group->group_filtermode_isexcl = 1; /* boolean=true */
847
848 /* Delete all sources marked with DELETE flag (A-B) */
849 source_delete_by_flag(group->group_source_list);
850
851 /* Send sources marked with SEND flag: Q(G,A*B) */
852 if (num_sources_tosend > 0) {
853 source_query_send_by_flag(group, num_sources_tosend);
854 }
855
856 assert(group->group_filtermode_isexcl);
857
858 group_exclude_fwd_anysrc_ifempty(group);
859 }
860
861 static void toex_excl(struct gm_group *group, int num_sources,
862 struct in_addr *sources)
863 {
864 int num_sources_tosend = 0;
865 int i;
866
867 /* set DELETE flag for all known sources (X,Y) */
868 source_mark_delete_flag(group);
869
870 /* clear off SEND flag from all known sources (X,Y) */
871 source_clear_send_flag(group->group_source_list);
872
873 if (num_sources == 0) {
874 struct gm_source *source;
875 struct in_addr any = {.s_addr = INADDR_ANY};
876
877 source = igmp_find_source_by_addr(group, any);
878 if (source)
879 IGMP_SOURCE_DONT_DELETE(source->source_flags);
880 }
881
882 /* scan received sources (A) */
883 for (i = 0; i < num_sources; ++i) {
884 struct gm_source *source;
885 struct in_addr *src_addr;
886 bool new;
887
888 src_addr = sources + i;
889
890 /* lookup reported source (A) in known sources (X,Y) */
891 source = igmp_get_source_by_addr(group, *src_addr, &new);
892 if (!source)
893 continue;
894
895 if (!new) {
896 /* if found, clear off DELETE flag from reported source
897 * (A) */
898 IGMP_SOURCE_DONT_DELETE(source->source_flags);
899 } else {
900 /* if not found, create source with Group Timer:
901 * (A-X-Y)=Group Timer */
902 long group_timer_msec;
903
904 assert(!source->t_source_timer); /* timer == 0 */
905 group_timer_msec = igmp_group_timer_remain_msec(group);
906 igmp_source_timer_on(group, source, group_timer_msec);
907 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
908
909 /* make sure source is created with DELETE flag unset */
910 assert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
911 }
912
913 /* make sure reported source has DELETE flag unset */
914 assert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
915
916 if (source->t_source_timer) {
917 /* if source timer>0 mark SEND flag: Q(G,A-Y) */
918 IGMP_SOURCE_DO_SEND(source->source_flags);
919 ++num_sources_tosend;
920 }
921
922 } /* scan received sources (A) */
923
924 /*
925 delete all sources marked with DELETE flag:
926 Delete (X-A)
927 Delete (Y-A)
928 */
929 source_delete_by_flag(group->group_source_list);
930
931 /* send sources marked with SEND flag: Q(G,A-Y) */
932 if (num_sources_tosend > 0) {
933 source_query_send_by_flag(group, num_sources_tosend);
934 }
935 }
936
937 void igmpv3_report_toex(struct gm_sock *igmp, struct in_addr from,
938 struct in_addr group_addr, int num_sources,
939 struct in_addr *sources)
940 {
941 struct interface *ifp = igmp->interface;
942 struct gm_group *group;
943
944 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
945
946 /* non-existant group is created as INCLUDE {empty} */
947 group = igmp_add_group_by_addr(igmp, group_addr);
948 if (!group) {
949 return;
950 }
951
952 if (group->group_filtermode_isexcl) {
953 /* EXCLUDE mode */
954 toex_excl(group, num_sources, sources);
955 } else {
956 /* INCLUDE mode */
957 toex_incl(group, num_sources, sources);
958 assert(group->group_filtermode_isexcl);
959 }
960 assert(group->group_filtermode_isexcl);
961
962 /* Group Timer=GMI */
963 igmp_group_reset_gmi(group);
964 }
965
966 void igmpv3_report_allow(struct gm_sock *igmp, struct in_addr from,
967 struct in_addr group_addr, int num_sources,
968 struct in_addr *sources)
969 {
970 on_trace(__func__, igmp->interface, from, group_addr, num_sources,
971 sources);
972
973 allow(igmp, from, group_addr, num_sources, sources);
974 }
975
976 static void igmp_send_query_group(struct gm_group *group, char *query_buf,
977 size_t query_buf_size, int num_sources,
978 int s_flag)
979 {
980 struct interface *ifp = group->interface;
981 struct pim_interface *pim_ifp = ifp->info;
982 struct gm_sock *igmp;
983 struct listnode *sock_node;
984
985 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
986 igmp_send_query(
987 pim_ifp->igmp_version, group, igmp->fd, ifp->name,
988 query_buf, query_buf_size, num_sources,
989 group->group_addr, group->group_addr,
990 pim_ifp->gm_specific_query_max_response_time_dsec,
991 s_flag, igmp->querier_robustness_variable,
992 igmp->querier_query_interval);
993 }
994 }
995
996 /*
997 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
998
999 When transmitting a group specific query, if the group timer is
1000 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1001 in the query message.
1002 */
1003 static void group_retransmit_group(struct gm_group *group)
1004 {
1005 struct pim_interface *pim_ifp;
1006 long lmqc; /* Last Member Query Count */
1007 long lmqi_msec; /* Last Member Query Interval */
1008 long lmqt_msec; /* Last Member Query Time */
1009 int s_flag;
1010 int query_buf_size;
1011
1012 pim_ifp = group->interface->info;
1013
1014 if (pim_ifp->igmp_version == 3) {
1015 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1016 } else {
1017 query_buf_size = IGMP_V12_MSG_SIZE;
1018 }
1019
1020 char query_buf[query_buf_size];
1021
1022 lmqc = pim_ifp->gm_last_member_query_count;
1023 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
1024 lmqt_msec = lmqc * lmqi_msec;
1025
1026 /*
1027 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1028
1029 When transmitting a group specific query, if the group timer is
1030 larger than LMQT, the "Suppress Router-Side Processing" bit is set
1031 in the query message.
1032 */
1033 s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
1034
1035 if (PIM_DEBUG_IGMP_TRACE) {
1036 char group_str[INET_ADDRSTRLEN];
1037 pim_inet4_dump("<group?>", group->group_addr, group_str,
1038 sizeof(group_str));
1039 zlog_debug(
1040 "retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
1041 group_str, group->interface->name, s_flag,
1042 group->group_specific_query_retransmit_count);
1043 }
1044
1045 /*
1046 RFC3376: 4.1.12. IP Destination Addresses for Queries
1047
1048 Group-Specific and Group-and-Source-Specific Queries are sent with
1049 an IP destination address equal to the multicast address of
1050 interest.
1051 */
1052
1053 igmp_send_query_group(group, query_buf, sizeof(query_buf), 0, s_flag);
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 gm_group *group,
1069 int send_with_sflag_set)
1070 {
1071 struct pim_interface *pim_ifp;
1072 long lmqc; /* Last Member Query Count */
1073 long lmqi_msec; /* Last Member Query Interval */
1074 long lmqt_msec; /* Last Member Query Time */
1075 char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
1076 char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
1077 int query_buf1_max_sources;
1078 int query_buf2_max_sources;
1079 struct in_addr *source_addr1;
1080 struct in_addr *source_addr2;
1081 int num_sources_tosend1;
1082 int num_sources_tosend2;
1083 struct listnode *src_node;
1084 struct gm_source *src;
1085 int num_retransmit_sources_left = 0;
1086
1087 source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1088 source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1089
1090 pim_ifp = group->interface->info;
1091
1092 lmqc = pim_ifp->gm_last_member_query_count;
1093 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
1094 lmqt_msec = lmqc * lmqi_msec;
1095
1096 /* Scan all group sources */
1097 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1098
1099 /* Source has retransmission state? */
1100 if (src->source_query_retransmit_count < 1)
1101 continue;
1102
1103 if (--src->source_query_retransmit_count > 0) {
1104 ++num_retransmit_sources_left;
1105 }
1106
1107 /* Copy source address into appropriate query buffer */
1108 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1109 *source_addr1 = src->source_addr;
1110 ++source_addr1;
1111 } else {
1112 *source_addr2 = src->source_addr;
1113 ++source_addr2;
1114 }
1115 }
1116
1117 num_sources_tosend1 =
1118 source_addr1
1119 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
1120 num_sources_tosend2 =
1121 source_addr2
1122 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
1123
1124 if (PIM_DEBUG_IGMP_TRACE) {
1125 char group_str[INET_ADDRSTRLEN];
1126 pim_inet4_dump("<group?>", group->group_addr, group_str,
1127 sizeof(group_str));
1128 zlog_debug(
1129 "retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
1130 group_str, group->interface->name, num_sources_tosend1,
1131 num_sources_tosend2, send_with_sflag_set,
1132 num_retransmit_sources_left);
1133 }
1134
1135 if (num_sources_tosend1 > 0) {
1136 /*
1137 Send group-and-source-specific query with s_flag set and all
1138 sources with timers greater than LMQT.
1139 */
1140
1141 if (send_with_sflag_set) {
1142
1143 query_buf1_max_sources =
1144 (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET)
1145 >> 2;
1146 if (num_sources_tosend1 > query_buf1_max_sources) {
1147 char group_str[INET_ADDRSTRLEN];
1148 pim_inet4_dump("<group?>", group->group_addr,
1149 group_str, sizeof(group_str));
1150 zlog_warn(
1151 "%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1152 __func__, group_str,
1153 group->interface->name,
1154 num_sources_tosend1, sizeof(query_buf1),
1155 query_buf1_max_sources);
1156 } else {
1157 /*
1158 RFC3376: 4.1.12. IP Destination Addresses for
1159 Queries
1160
1161 Group-Specific and Group-and-Source-Specific
1162 Queries are sent with
1163 an IP destination address equal to the
1164 multicast address of
1165 interest.
1166 */
1167
1168 igmp_send_query_group(
1169 group, query_buf1, sizeof(query_buf1),
1170 num_sources_tosend1, 1 /* s_flag */);
1171 }
1172
1173 } /* send_with_sflag_set */
1174 }
1175
1176 if (num_sources_tosend2 > 0) {
1177 /*
1178 Send group-and-source-specific query with s_flag clear and all
1179 sources with timers lower or equal to LMQT.
1180 */
1181
1182 query_buf2_max_sources =
1183 (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
1184 if (num_sources_tosend2 > query_buf2_max_sources) {
1185 char group_str[INET_ADDRSTRLEN];
1186 pim_inet4_dump("<group?>", group->group_addr, group_str,
1187 sizeof(group_str));
1188 zlog_warn(
1189 "%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
1190 __func__, group_str, group->interface->name,
1191 num_sources_tosend2, sizeof(query_buf2),
1192 query_buf2_max_sources);
1193 } else {
1194 /*
1195 RFC3376: 4.1.12. IP Destination Addresses for Queries
1196
1197 Group-Specific and Group-and-Source-Specific Queries
1198 are sent with
1199 an IP destination address equal to the multicast
1200 address of
1201 interest.
1202 */
1203
1204 igmp_send_query_group(
1205 group, query_buf2, sizeof(query_buf2),
1206 num_sources_tosend2, 0 /* s_flag */);
1207 }
1208 }
1209
1210 return num_retransmit_sources_left;
1211 }
1212
1213 static void igmp_group_retransmit(struct thread *t)
1214 {
1215 struct gm_group *group;
1216 int num_retransmit_sources_left;
1217 int send_with_sflag_set; /* boolean */
1218
1219 group = THREAD_ARG(t);
1220
1221 if (PIM_DEBUG_IGMP_TRACE) {
1222 char group_str[INET_ADDRSTRLEN];
1223 pim_inet4_dump("<group?>", group->group_addr, group_str,
1224 sizeof(group_str));
1225 zlog_debug("group_retransmit_timer: group %s on %s", group_str,
1226 group->interface->name);
1227 }
1228
1229 /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
1230 if (group->group_specific_query_retransmit_count > 0) {
1231
1232 /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
1233 group_retransmit_group(group);
1234 --group->group_specific_query_retransmit_count;
1235
1236 /*
1237 RFC3376: 6.6.3.2
1238 If a group specific query is scheduled to be transmitted at
1239 the
1240 same time as a group and source specific query for the same
1241 group,
1242 then transmission of the group and source specific message
1243 with the
1244 "Suppress Router-Side Processing" bit set may be suppressed.
1245 */
1246 send_with_sflag_set = 0; /* boolean=false */
1247 } else {
1248 send_with_sflag_set = 1; /* boolean=true */
1249 }
1250
1251 /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
1252 num_retransmit_sources_left =
1253 group_retransmit_sources(group, send_with_sflag_set);
1254
1255 /*
1256 Keep group retransmit timer running if there is any retransmit
1257 counter pending
1258 */
1259 if ((num_retransmit_sources_left > 0)
1260 || (group->group_specific_query_retransmit_count > 0)) {
1261 group_retransmit_timer_on(group);
1262 }
1263 }
1264
1265 /*
1266 group_retransmit_timer_on:
1267 if group retransmit timer isn't running, starts it;
1268 otherwise, do nothing
1269 */
1270 static void group_retransmit_timer_on(struct gm_group *group)
1271 {
1272 struct pim_interface *pim_ifp;
1273 long lmqi_msec; /* Last Member Query Interval */
1274
1275 /* if group retransmit timer is running, do nothing */
1276 if (group->t_group_query_retransmit_timer) {
1277 return;
1278 }
1279
1280 pim_ifp = group->interface->info;
1281
1282 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
1283
1284 if (PIM_DEBUG_IGMP_TRACE) {
1285 char group_str[INET_ADDRSTRLEN];
1286 pim_inet4_dump("<group?>", group->group_addr, group_str,
1287 sizeof(group_str));
1288 zlog_debug(
1289 "Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
1290 lmqi_msec / 1000, lmqi_msec % 1000, group_str,
1291 group->interface->name);
1292 }
1293
1294 thread_add_timer_msec(router->master, igmp_group_retransmit, group,
1295 lmqi_msec,
1296 &group->t_group_query_retransmit_timer);
1297 }
1298
1299 static long igmp_group_timer_remain_msec(struct gm_group *group)
1300 {
1301 return pim_time_timer_remain_msec(group->t_group_timer);
1302 }
1303
1304 static long igmp_source_timer_remain_msec(struct gm_source *source)
1305 {
1306 return pim_time_timer_remain_msec(source->t_source_timer);
1307 }
1308
1309 /*
1310 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1311 */
1312 static void group_query_send(struct gm_group *group)
1313 {
1314 struct pim_interface *pim_ifp;
1315 long lmqc; /* Last Member Query Count */
1316
1317 pim_ifp = group->interface->info;
1318 lmqc = pim_ifp->gm_last_member_query_count;
1319
1320 /* lower group timer to lmqt */
1321 igmp_group_timer_lower_to_lmqt(group);
1322
1323 /* reset retransmission counter */
1324 group->group_specific_query_retransmit_count = lmqc;
1325
1326 /* immediately send group specific query (decrease retransmit counter by
1327 * 1)*/
1328 group_retransmit_group(group);
1329
1330 /* make sure group retransmit timer is running */
1331 group_retransmit_timer_on(group);
1332 }
1333
1334 /*
1335 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1336 */
1337 static void source_query_send_by_flag(struct gm_group *group,
1338 int num_sources_tosend)
1339 {
1340 struct pim_interface *pim_ifp;
1341 struct listnode *src_node;
1342 struct gm_source *src;
1343 long lmqc; /* Last Member Query Count */
1344 long lmqi_msec; /* Last Member Query Interval */
1345 long lmqt_msec; /* Last Member Query Time */
1346
1347 assert(num_sources_tosend > 0);
1348
1349 pim_ifp = group->interface->info;
1350
1351 lmqc = pim_ifp->gm_last_member_query_count;
1352 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
1353 lmqt_msec = lmqc * lmqi_msec;
1354
1355 /*
1356 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1357 Queries
1358
1359 (...) for each of the sources in X of group G, with source timer
1360 larger
1361 than LMQT:
1362 o Set number of retransmissions for each source to [Last Member
1363 Query Count].
1364 o Lower source timer to LMQT.
1365 */
1366 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1367 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1368 /* source "src" in X of group G */
1369 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1370 src->source_query_retransmit_count = lmqc;
1371 igmp_source_timer_lower_to_lmqt(src);
1372 }
1373 }
1374 }
1375
1376 /* send group-and-source specific queries */
1377 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1378
1379 /* make sure group retransmit timer is running */
1380 group_retransmit_timer_on(group);
1381 }
1382
1383 static void block_excl(struct gm_group *group, int num_sources,
1384 struct in_addr *sources)
1385 {
1386 int num_sources_tosend = 0;
1387 int i;
1388
1389 /* 1. clear off SEND flag from all known sources (X,Y) */
1390 source_clear_send_flag(group->group_source_list);
1391
1392 /* 2. scan received sources (A) */
1393 for (i = 0; i < num_sources; ++i) {
1394 struct gm_source *source;
1395 struct in_addr *src_addr;
1396 bool new;
1397
1398 src_addr = sources + i;
1399
1400 /* lookup reported source (A) in known sources (X,Y) */
1401 source = igmp_get_source_by_addr(group, *src_addr, &new);
1402 if (!source)
1403 continue;
1404
1405 if (new) {
1406 /* 3: if not found, create source with Group Timer:
1407 * (A-X-Y)=Group Timer */
1408 long group_timer_msec;
1409
1410 assert(!source->t_source_timer); /* timer == 0 */
1411 group_timer_msec = igmp_group_timer_remain_msec(group);
1412 igmp_source_timer_on(group, source, group_timer_msec);
1413 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
1414 }
1415
1416 if (source->t_source_timer) {
1417 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1418 IGMP_SOURCE_DO_SEND(source->source_flags);
1419 ++num_sources_tosend;
1420 }
1421 }
1422
1423 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1424 if (num_sources_tosend > 0) {
1425 source_query_send_by_flag(group, num_sources_tosend);
1426 }
1427 }
1428
1429 static void block_incl(struct gm_group *group, int num_sources,
1430 struct in_addr *sources)
1431 {
1432 int num_sources_tosend = 0;
1433 int i;
1434
1435 /* 1. clear off SEND flag from all known sources (B) */
1436 source_clear_send_flag(group->group_source_list);
1437
1438 /* 2. scan received sources (A) */
1439 for (i = 0; i < num_sources; ++i) {
1440 struct gm_source *source;
1441 struct in_addr *src_addr;
1442
1443 src_addr = sources + i;
1444
1445 /* lookup reported source (A) in known sources (B) */
1446 source = igmp_find_source_by_addr(group, *src_addr);
1447 if (source) {
1448 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1449 IGMP_SOURCE_DO_SEND(source->source_flags);
1450 ++num_sources_tosend;
1451 }
1452 }
1453
1454 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1455 if (num_sources_tosend > 0) {
1456 source_query_send_by_flag(group, num_sources_tosend);
1457 }
1458 }
1459
1460 void igmpv3_report_block(struct gm_sock *igmp, struct in_addr from,
1461 struct in_addr group_addr, int num_sources,
1462 struct in_addr *sources)
1463 {
1464 struct interface *ifp = igmp->interface;
1465 struct gm_group *group;
1466
1467 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
1468
1469 /* non-existant group is created as INCLUDE {empty} */
1470 group = igmp_add_group_by_addr(igmp, group_addr);
1471 if (!group) {
1472 return;
1473 }
1474
1475 if (group->group_filtermode_isexcl) {
1476 /* EXCLUDE mode */
1477 block_excl(group, num_sources, sources);
1478 } else {
1479 /* INCLUDE mode */
1480 block_incl(group, num_sources, sources);
1481 }
1482 }
1483
1484 void igmp_group_timer_lower_to_lmqt(struct gm_group *group)
1485 {
1486 struct interface *ifp;
1487 struct pim_interface *pim_ifp;
1488 char *ifname;
1489 int lmqi_dsec; /* Last Member Query Interval */
1490 int lmqc; /* Last Member Query Count */
1491 int lmqt_msec; /* Last Member Query Time */
1492
1493 /*
1494 RFC 3376: 6.2.2. Definition of Group Timers
1495
1496 The group timer is only used when a group is in EXCLUDE mode and
1497 it represents the time for the *filter-mode* of the group to
1498 expire and switch to INCLUDE mode.
1499 */
1500 if (!group->group_filtermode_isexcl) {
1501 return;
1502 }
1503
1504 ifp = group->interface;
1505 pim_ifp = ifp->info;
1506 ifname = ifp->name;
1507
1508 lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
1509 lmqc = pim_ifp->gm_last_member_query_count;
1510 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1511 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1512
1513 if (PIM_DEBUG_IGMP_TRACE) {
1514 char group_str[INET_ADDRSTRLEN];
1515 pim_inet4_dump("<group?>", group->group_addr, group_str,
1516 sizeof(group_str));
1517 zlog_debug(
1518 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1519 __func__, group_str, ifname, lmqc, lmqi_dsec,
1520 lmqt_msec);
1521 }
1522
1523 assert(group->group_filtermode_isexcl);
1524
1525 igmp_group_timer_on(group, lmqt_msec, ifname);
1526 }
1527
1528 void igmp_source_timer_lower_to_lmqt(struct gm_source *source)
1529 {
1530 struct gm_group *group;
1531 struct interface *ifp;
1532 struct pim_interface *pim_ifp;
1533 char *ifname;
1534 int lmqi_dsec; /* Last Member Query Interval */
1535 int lmqc; /* Last Member Query Count */
1536 int lmqt_msec; /* Last Member Query Time */
1537
1538 group = source->source_group;
1539 ifp = group->interface;
1540 pim_ifp = ifp->info;
1541 ifname = ifp->name;
1542
1543 lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
1544 lmqc = pim_ifp->gm_last_member_query_count;
1545 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1546 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1547
1548 if (PIM_DEBUG_IGMP_TRACE) {
1549 char group_str[INET_ADDRSTRLEN];
1550 char source_str[INET_ADDRSTRLEN];
1551 pim_inet4_dump("<group?>", group->group_addr, group_str,
1552 sizeof(group_str));
1553 pim_inet4_dump("<source?>", source->source_addr, source_str,
1554 sizeof(source_str));
1555 zlog_debug(
1556 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
1557 __func__, group_str, source_str, ifname, lmqc,
1558 lmqi_dsec, lmqt_msec);
1559 }
1560
1561 igmp_source_timer_on(group, source, lmqt_msec);
1562 }
1563
1564 void igmp_v3_send_query(struct gm_group *group, int fd, const char *ifname,
1565 char *query_buf, int query_buf_size, int num_sources,
1566 struct in_addr dst_addr, struct in_addr group_addr,
1567 int query_max_response_time_dsec, uint8_t s_flag,
1568 uint8_t querier_robustness_variable,
1569 uint16_t querier_query_interval)
1570 {
1571 ssize_t msg_size;
1572 uint8_t max_resp_code;
1573 uint8_t qqic;
1574 ssize_t sent;
1575 struct sockaddr_in to;
1576 socklen_t tolen;
1577 uint16_t checksum;
1578
1579 assert(num_sources >= 0);
1580
1581 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1582 if (msg_size > query_buf_size) {
1583 flog_err(
1584 EC_LIB_DEVELOPMENT,
1585 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
1586 __FILE__, __func__, msg_size, query_buf_size);
1587 return;
1588 }
1589
1590 s_flag = PIM_FORCE_BOOLEAN(s_flag);
1591 assert((s_flag == 0) || (s_flag == 1));
1592
1593 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1594 qqic = igmp_msg_encode16to8(querier_query_interval);
1595
1596 /*
1597 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1598
1599 If non-zero, the QRV field contains the [Robustness Variable]
1600 value used by the querier, i.e., the sender of the Query. If the
1601 querier's [Robustness Variable] exceeds 7, the maximum value of
1602 the QRV field, the QRV is set to zero.
1603 */
1604 if (querier_robustness_variable > 7) {
1605 querier_robustness_variable = 0;
1606 }
1607
1608 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1609 query_buf[1] = max_resp_code;
1610 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
1611 0; /* for computing checksum */
1612 memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
1613
1614 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1615 query_buf[9] = qqic;
1616 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) =
1617 htons(num_sources);
1618
1619 checksum = in_cksum(query_buf, msg_size);
1620 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
1621
1622 if (PIM_DEBUG_IGMP_PACKETS) {
1623 char dst_str[INET_ADDRSTRLEN];
1624 char group_str[INET_ADDRSTRLEN];
1625 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1626 pim_inet4_dump("<group?>", group_addr, group_str,
1627 sizeof(group_str));
1628 zlog_debug(
1629 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1630 dst_str, ifname, group_str, num_sources, msg_size,
1631 s_flag, querier_robustness_variable,
1632 querier_query_interval, qqic);
1633 }
1634
1635 memset(&to, 0, sizeof(to));
1636 to.sin_family = AF_INET;
1637 to.sin_addr = dst_addr;
1638 tolen = sizeof(to);
1639
1640 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1641 (struct sockaddr *)&to, tolen);
1642 if (sent != (ssize_t)msg_size) {
1643 char dst_str[INET_ADDRSTRLEN];
1644 char group_str[INET_ADDRSTRLEN];
1645 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1646 pim_inet4_dump("<group?>", group_addr, group_str,
1647 sizeof(group_str));
1648 if (sent < 0) {
1649 zlog_warn(
1650 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1651 dst_str, ifname, group_str, msg_size, errno,
1652 safe_strerror(errno));
1653 } else {
1654 zlog_warn(
1655 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1656 dst_str, ifname, group_str, msg_size, sent);
1657 }
1658 return;
1659 }
1660
1661 /*
1662 s_flag sanity test: s_flag must be set for general queries
1663
1664 RFC 3376: 6.6.1. Timer Updates
1665
1666 When a router sends or receives a query with a clear Suppress
1667 Router-Side Processing flag, it must update its timers to reflect
1668 the correct timeout values for the group or sources being queried.
1669
1670 General queries don't trigger timer update.
1671 */
1672 if (!s_flag) {
1673 /* general query? */
1674 if (group_addr.s_addr == INADDR_ANY) {
1675 char dst_str[INET_ADDRSTRLEN];
1676 char group_str[INET_ADDRSTRLEN];
1677 pim_inet4_dump("<dst?>", dst_addr, dst_str,
1678 sizeof(dst_str));
1679 pim_inet4_dump("<group?>", group_addr, group_str,
1680 sizeof(group_str));
1681 zlog_warn(
1682 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
1683 __func__, dst_str, ifname, group_str,
1684 num_sources);
1685 }
1686 }
1687 }
1688
1689 void igmp_v3_recv_query(struct gm_sock *igmp, const char *from_str,
1690 char *igmp_msg)
1691 {
1692 struct interface *ifp;
1693 struct pim_interface *pim_ifp;
1694 struct in_addr group_addr;
1695 uint8_t resv_s_qrv = 0;
1696 uint8_t s_flag = 0;
1697 uint8_t qrv = 0;
1698 int i;
1699
1700 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
1701 ifp = igmp->interface;
1702 pim_ifp = ifp->info;
1703
1704 /*
1705 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1706 *
1707 * Routers adopt the QRV value from the most recently received Query
1708 * as their own [Robustness Variable] value, unless that most
1709 * recently received QRV was zero, in which case the receivers use
1710 * the default [Robustness Variable] value specified in section 8.1
1711 * or a statically configured value.
1712 */
1713 resv_s_qrv = igmp_msg[8];
1714 qrv = 7 & resv_s_qrv;
1715 igmp->querier_robustness_variable =
1716 qrv ? qrv : pim_ifp->gm_default_robustness_variable;
1717
1718 /*
1719 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1720 *
1721 * Multicast routers that are not the current querier adopt the QQI
1722 * value from the most recently received Query as their own [Query
1723 * Interval] value, unless that most recently received QQI was zero,
1724 * in which case the receiving routers use the default.
1725 */
1726 if (igmp->t_other_querier_timer) {
1727 /* other querier present */
1728 uint8_t qqic;
1729 uint16_t qqi;
1730 qqic = igmp_msg[9];
1731 qqi = igmp_msg_decode8to16(qqic);
1732 igmp->querier_query_interval =
1733 qqi ? qqi : pim_ifp->gm_default_query_interval;
1734
1735 if (PIM_DEBUG_IGMP_TRACE) {
1736 char ifaddr_str[INET_ADDRSTRLEN];
1737 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
1738 sizeof(ifaddr_str));
1739 zlog_debug(
1740 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1741 ifaddr_str,
1742 qqi ? "recv-non-default" : "default",
1743 igmp->querier_query_interval, qqic, from_str);
1744 }
1745 }
1746
1747 /*
1748 * RFC 3376: 6.6.1. Timer Updates
1749 *
1750 * When a router sends or receives a query with a clear Suppress
1751 * Router-Side Processing flag, it must update its timers to reflect
1752 * the correct timeout values for the group or sources being queried.
1753 *
1754 * General queries don't trigger timer update.
1755 */
1756 s_flag = (1 << 3) & resv_s_qrv;
1757
1758 if (!s_flag) {
1759 /* s_flag is clear */
1760
1761 if (group_addr.s_addr == INADDR_ANY) {
1762 /* this is a general query */
1763 /* log that general query should have the s_flag set */
1764 zlog_warn(
1765 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1766 from_str, ifp->name);
1767 } else {
1768 struct gm_group *group;
1769
1770 /* this is a non-general query: perform timer updates */
1771
1772 group = find_group_by_addr(igmp, group_addr);
1773 if (group) {
1774 int recv_num_sources = ntohs(*(
1775 uint16_t
1776 *)(igmp_msg
1777 + IGMP_V3_NUMSOURCES_OFFSET));
1778
1779 /*
1780 * RFC 3376: 6.6.1. Timer Updates
1781 * Query Q(G,A): Source Timer for sources in A
1782 * are lowered to LMQT
1783 * Query Q(G): Group Timer is lowered to LMQT
1784 */
1785 if (recv_num_sources < 1) {
1786 /* Query Q(G): Group Timer is lowered to
1787 * LMQT */
1788
1789 igmp_group_timer_lower_to_lmqt(group);
1790 } else {
1791 /* Query Q(G,A): Source Timer for
1792 * sources in A are lowered to LMQT */
1793
1794 /* Scan sources in query and lower their
1795 * timers to LMQT */
1796 struct in_addr *sources =
1797 (struct in_addr
1798 *)(igmp_msg
1799 + IGMP_V3_SOURCES_OFFSET);
1800 for (i = 0; i < recv_num_sources; ++i) {
1801 struct in_addr src_addr;
1802 struct gm_source *src;
1803 memcpy(&src_addr, sources + i,
1804 sizeof(struct in_addr));
1805 src = igmp_find_source_by_addr(
1806 group, src_addr);
1807 if (src) {
1808 igmp_source_timer_lower_to_lmqt(
1809 src);
1810 }
1811 }
1812 }
1813 } else {
1814 char group_str[INET_ADDRSTRLEN];
1815 pim_inet4_dump("<group?>", group_addr,
1816 group_str, sizeof(group_str));
1817 zlog_warn(
1818 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1819 from_str, ifp->name, group_str);
1820 }
1821 }
1822 } /* s_flag is clear: timer updates */
1823 }
1824
1825 int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
1826 const char *from_str, char *igmp_msg, int igmp_msg_len)
1827 {
1828 int num_groups;
1829 uint8_t *group_record;
1830 uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len;
1831 struct interface *ifp = igmp->interface;
1832 int i;
1833 int local_ncb = 0;
1834 struct pim_interface *pim_ifp;
1835
1836 if (igmp->mtrace_only)
1837 return 0;
1838
1839 pim_ifp = igmp->interface->info;
1840
1841 if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
1842 zlog_warn(
1843 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1844 from_str, ifp->name, igmp_msg_len,
1845 IGMP_V3_MSG_MIN_SIZE);
1846 return -1;
1847 }
1848
1849 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
1850 zlog_warn(
1851 "Recv IGMPv3 report from %s on %s with invalid checksum",
1852 from_str, ifp->name);
1853 return -1;
1854 }
1855
1856 /* Collecting IGMP Rx stats */
1857 igmp->igmp_stats.report_v3++;
1858
1859 num_groups = ntohs(
1860 *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
1861 if (num_groups < 1) {
1862 zlog_warn(
1863 "Recv IGMP report v3 from %s on %s: missing group records",
1864 from_str, ifp->name);
1865 return -1;
1866 }
1867
1868 if (PIM_DEBUG_IGMP_PACKETS) {
1869 zlog_debug(
1870 "Recv IGMP report v3 from %s on %s: size=%d groups=%d",
1871 from_str, ifp->name, igmp_msg_len, num_groups);
1872 }
1873
1874 group_record = (uint8_t *)igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
1875
1876 /* Scan groups */
1877 for (i = 0; i < num_groups; ++i) {
1878 struct in_addr rec_group;
1879 uint8_t *sources;
1880 uint8_t *src;
1881 int rec_type;
1882 int rec_auxdatalen;
1883 int rec_num_sources;
1884 int j;
1885 struct prefix lncb;
1886 struct prefix g;
1887 bool filtered = false;
1888
1889 if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE)
1890 > report_pastend) {
1891 zlog_warn(
1892 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1893 from_str, ifp->name);
1894 return -1;
1895 }
1896
1897 rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
1898 rec_auxdatalen =
1899 group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
1900 rec_num_sources = ntohs(*(
1901 uint16_t *)(group_record
1902 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
1903
1904 memcpy(&rec_group,
1905 group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET,
1906 sizeof(struct in_addr));
1907
1908 if (PIM_DEBUG_IGMP_PACKETS) {
1909 zlog_debug(
1910 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
1911 from_str, ifp->name, i, rec_type,
1912 rec_auxdatalen, rec_num_sources,
1913 &rec_group);
1914 }
1915
1916 /* Scan sources */
1917
1918 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
1919
1920 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
1921
1922 if ((src + 4) > report_pastend) {
1923 zlog_warn(
1924 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1925 from_str, ifp->name);
1926 return -1;
1927 }
1928
1929 if (PIM_DEBUG_IGMP_PACKETS) {
1930 char src_str[200];
1931
1932 if (!inet_ntop(AF_INET, src, src_str,
1933 sizeof(src_str)))
1934 snprintf(src_str, sizeof(src_str),
1935 "<source?>");
1936
1937 zlog_debug(
1938 " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s",
1939 from_str, ifp->name, i,
1940 &rec_group, src_str);
1941 }
1942 } /* for (sources) */
1943
1944
1945 lncb.family = AF_INET;
1946 lncb.u.prefix4.s_addr = 0x000000E0;
1947 lncb.prefixlen = 24;
1948
1949 g.family = AF_INET;
1950 g.u.prefix4 = rec_group;
1951 g.prefixlen = IPV4_MAX_BITLEN;
1952
1953 /* determine filtering status for group */
1954 filtered = pim_is_group_filtered(ifp->info, &rec_group);
1955
1956 if (PIM_DEBUG_IGMP_PACKETS && filtered)
1957 zlog_debug(
1958 "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
1959 &rec_group, from_str, ifp->name,
1960 pim_ifp->boundary_oil_plist);
1961
1962 /*
1963 * If we receive a igmp report with the group in 224.0.0.0/24
1964 * then we should ignore it
1965 */
1966 if (prefix_match(&lncb, &g))
1967 local_ncb = 1;
1968
1969 if (!local_ncb && !filtered)
1970 switch (rec_type) {
1971 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
1972 igmpv3_report_isin(igmp, from, rec_group,
1973 rec_num_sources,
1974 (struct in_addr *)sources);
1975 break;
1976 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
1977 igmpv3_report_isex(
1978 igmp, from, rec_group, rec_num_sources,
1979 (struct in_addr *)sources, 0);
1980 break;
1981 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
1982 igmpv3_report_toin(igmp, from, rec_group,
1983 rec_num_sources,
1984 (struct in_addr *)sources);
1985 break;
1986 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
1987 igmpv3_report_toex(igmp, from, rec_group,
1988 rec_num_sources,
1989 (struct in_addr *)sources);
1990 break;
1991 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
1992 igmpv3_report_allow(igmp, from, rec_group,
1993 rec_num_sources,
1994 (struct in_addr *)sources);
1995 break;
1996 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
1997 igmpv3_report_block(igmp, from, rec_group,
1998 rec_num_sources,
1999 (struct in_addr *)sources);
2000 break;
2001 default:
2002 zlog_warn(
2003 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2004 from_str, ifp->name, rec_type);
2005 }
2006
2007 group_record +=
2008 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
2009 local_ncb = 0;
2010
2011 } /* for (group records) */
2012
2013 return 0;
2014 }