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