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