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