]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmpv3.c
Merge pull request #13522 from LabNConsulting/chopps/fix-bgp-test
[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
e6685141 110static void igmp_source_timer(struct event *t)
12e41d03 111{
51700107 112 struct gm_source *source;
a16db099 113 struct gm_group *group;
d62a17ae 114
e16d030c 115 source = EVENT_ARG(t);
d62a17ae 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
e16d030c 190 EVENT_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
907a2395
DS
212 event_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
e6685141 1203static void igmp_group_retransmit(struct event *t)
12e41d03 1204{
a16db099 1205 struct gm_group *group;
d62a17ae 1206 int num_retransmit_sources_left;
1207 int send_with_sflag_set; /* boolean */
1208
e16d030c 1209 group = EVENT_ARG(t);
d62a17ae 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
907a2395
DS
1284 event_add_timer_msec(router->master, igmp_group_retransmit, group,
1285 lmqi_msec, &group->t_group_query_retransmit_timer);
12e41d03
DL
1286}
1287
a16db099 1288static long igmp_group_timer_remain_msec(struct gm_group *group)
12e41d03 1289{
d62a17ae 1290 return pim_time_timer_remain_msec(group->t_group_timer);
12e41d03
DL
1291}
1292
51700107 1293static long igmp_source_timer_remain_msec(struct gm_source *source)
12e41d03 1294{
d62a17ae 1295 return pim_time_timer_remain_msec(source->t_source_timer);
12e41d03
DL
1296}
1297
1298/*
1299 RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
1300*/
a16db099 1301static void group_query_send(struct gm_group *group)
12e41d03 1302{
59115451 1303 struct pim_interface *pim_ifp;
d62a17ae 1304 long lmqc; /* Last Member Query Count */
12e41d03 1305
dda4d23c 1306 pim_ifp = group->interface->info;
18adcff1 1307 lmqc = pim_ifp->gm_last_member_query_count;
12e41d03 1308
d62a17ae 1309 /* lower group timer to lmqt */
1310 igmp_group_timer_lower_to_lmqt(group);
12e41d03 1311
d62a17ae 1312 /* reset retransmission counter */
1313 group->group_specific_query_retransmit_count = lmqc;
12e41d03 1314
d62a17ae 1315 /* immediately send group specific query (decrease retransmit counter by
1316 * 1)*/
1317 group_retransmit_group(group);
12e41d03 1318
d62a17ae 1319 /* make sure group retransmit timer is running */
1320 group_retransmit_timer_on(group);
12e41d03
DL
1321}
1322
1323/*
1324 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
1325*/
a16db099 1326static void source_query_send_by_flag(struct gm_group *group,
12e41d03
DL
1327 int num_sources_tosend)
1328{
d62a17ae 1329 struct pim_interface *pim_ifp;
1330 struct listnode *src_node;
51700107 1331 struct gm_source *src;
d62a17ae 1332 long lmqc; /* Last Member Query Count */
1333 long lmqi_msec; /* Last Member Query Interval */
1334 long lmqt_msec; /* Last Member Query Time */
1335
df5dfb77 1336 assert(num_sources_tosend > 0);
d62a17ae 1337
dda4d23c 1338 pim_ifp = group->interface->info;
d62a17ae 1339
18adcff1
SG
1340 lmqc = pim_ifp->gm_last_member_query_count;
1341 lmqi_msec = 100 * pim_ifp->gm_specific_query_max_response_time_dsec;
d62a17ae 1342 lmqt_msec = lmqc * lmqi_msec;
1343
1344 /*
1345 RFC3376: 6.6.3.2. Building and Sending Group and Source Specific
1346 Queries
1347
1348 (...) for each of the sources in X of group G, with source timer
1349 larger
1350 than LMQT:
1351 o Set number of retransmissions for each source to [Last Member
1352 Query Count].
1353 o Lower source timer to LMQT.
1354 */
1355 for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
1356 if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
1357 /* source "src" in X of group G */
1358 if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
1359 src->source_query_retransmit_count = lmqc;
1360 igmp_source_timer_lower_to_lmqt(src);
1361 }
1362 }
1363 }
1364
1365 /* send group-and-source specific queries */
1366 group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
1367
1368 /* make sure group retransmit timer is running */
1369 group_retransmit_timer_on(group);
12e41d03
DL
1370}
1371
a16db099 1372static void block_excl(struct gm_group *group, int num_sources,
d62a17ae 1373 struct in_addr *sources)
12e41d03 1374{
d62a17ae 1375 int num_sources_tosend = 0;
1376 int i;
1377
1378 /* 1. clear off SEND flag from all known sources (X,Y) */
1379 source_clear_send_flag(group->group_source_list);
1380
1381 /* 2. scan received sources (A) */
1382 for (i = 0; i < num_sources; ++i) {
51700107 1383 struct gm_source *source;
d62a17ae 1384 struct in_addr *src_addr;
5421bf8f 1385 bool new;
d62a17ae 1386
1387 src_addr = sources + i;
1388
1389 /* lookup reported source (A) in known sources (X,Y) */
5421bf8f
DL
1390 source = igmp_get_source_by_addr(group, *src_addr, &new);
1391 if (!source)
1392 continue;
1393
1394 if (new) {
d62a17ae 1395 /* 3: if not found, create source with Group Timer:
1396 * (A-X-Y)=Group Timer */
1397 long group_timer_msec;
d62a17ae 1398
df5dfb77 1399 assert(!source->t_source_timer); /* timer == 0 */
d62a17ae 1400 group_timer_msec = igmp_group_timer_remain_msec(group);
1401 igmp_source_timer_on(group, source, group_timer_msec);
df5dfb77 1402 assert(source->t_source_timer); /* (A-X-Y) timer > 0 */
d62a17ae 1403 }
1404
1405 if (source->t_source_timer) {
1406 /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
1407 IGMP_SOURCE_DO_SEND(source->source_flags);
1408 ++num_sources_tosend;
1409 }
1410 }
1411
1412 /* 5. send sources marked with SEND flag: Q(G,A-Y) */
1413 if (num_sources_tosend > 0) {
1414 source_query_send_by_flag(group, num_sources_tosend);
1415 }
12e41d03
DL
1416}
1417
a16db099 1418static void block_incl(struct gm_group *group, int num_sources,
d62a17ae 1419 struct in_addr *sources)
12e41d03 1420{
d62a17ae 1421 int num_sources_tosend = 0;
1422 int i;
1423
1424 /* 1. clear off SEND flag from all known sources (B) */
1425 source_clear_send_flag(group->group_source_list);
1426
1427 /* 2. scan received sources (A) */
1428 for (i = 0; i < num_sources; ++i) {
51700107 1429 struct gm_source *source;
d62a17ae 1430 struct in_addr *src_addr;
1431
1432 src_addr = sources + i;
1433
1434 /* lookup reported source (A) in known sources (B) */
1435 source = igmp_find_source_by_addr(group, *src_addr);
1436 if (source) {
1437 /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
1438 IGMP_SOURCE_DO_SEND(source->source_flags);
1439 ++num_sources_tosend;
1440 }
1441 }
1442
1443 /* 4. send sources marked with SEND flag: Q(G,A*B) */
1444 if (num_sources_tosend > 0) {
1445 source_query_send_by_flag(group, num_sources_tosend);
1446 }
12e41d03
DL
1447}
1448
c5f76fad 1449void igmpv3_report_block(struct gm_sock *igmp, struct in_addr from,
d62a17ae 1450 struct in_addr group_addr, int num_sources,
1451 struct in_addr *sources)
12e41d03 1452{
d62a17ae 1453 struct interface *ifp = igmp->interface;
a16db099 1454 struct gm_group *group;
d62a17ae 1455
15569c58 1456 on_trace(__func__, ifp, from, group_addr, num_sources, sources);
d62a17ae 1457
b97047ed 1458 /* non-existent group is created as INCLUDE {empty} */
d62a17ae 1459 group = igmp_add_group_by_addr(igmp, group_addr);
1460 if (!group) {
1461 return;
1462 }
1463
1464 if (group->group_filtermode_isexcl) {
1465 /* EXCLUDE mode */
1466 block_excl(group, num_sources, sources);
1467 } else {
1468 /* INCLUDE mode */
1469 block_incl(group, num_sources, sources);
1470 }
12e41d03
DL
1471}
1472
a16db099 1473void igmp_group_timer_lower_to_lmqt(struct gm_group *group)
12e41d03 1474{
d62a17ae 1475 struct interface *ifp;
1476 struct pim_interface *pim_ifp;
1477 char *ifname;
1478 int lmqi_dsec; /* Last Member Query Interval */
1479 int lmqc; /* Last Member Query Count */
1480 int lmqt_msec; /* Last Member Query Time */
1481
1482 /*
1483 RFC 3376: 6.2.2. Definition of Group Timers
1484
1485 The group timer is only used when a group is in EXCLUDE mode and
1486 it represents the time for the *filter-mode* of the group to
1487 expire and switch to INCLUDE mode.
1488 */
1489 if (!group->group_filtermode_isexcl) {
1490 return;
1491 }
1492
dda4d23c 1493 ifp = group->interface;
d62a17ae 1494 pim_ifp = ifp->info;
1495 ifname = ifp->name;
1496
18adcff1
SG
1497 lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
1498 lmqc = pim_ifp->gm_last_member_query_count;
d62a17ae 1499 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1500 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1501
a96d64b0 1502 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1503 char group_str[INET_ADDRSTRLEN];
1504 pim_inet4_dump("<group?>", group->group_addr, group_str,
1505 sizeof(group_str));
1506 zlog_debug(
1507 "%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
15569c58 1508 __func__, group_str, ifname, lmqc, lmqi_dsec,
d62a17ae 1509 lmqt_msec);
1510 }
1511
df5dfb77 1512 assert(group->group_filtermode_isexcl);
d62a17ae 1513
1514 igmp_group_timer_on(group, lmqt_msec, ifname);
12e41d03
DL
1515}
1516
51700107 1517void igmp_source_timer_lower_to_lmqt(struct gm_source *source)
12e41d03 1518{
a16db099 1519 struct gm_group *group;
d62a17ae 1520 struct interface *ifp;
1521 struct pim_interface *pim_ifp;
1522 char *ifname;
1523 int lmqi_dsec; /* Last Member Query Interval */
1524 int lmqc; /* Last Member Query Count */
1525 int lmqt_msec; /* Last Member Query Time */
1526
1527 group = source->source_group;
dda4d23c 1528 ifp = group->interface;
d62a17ae 1529 pim_ifp = ifp->info;
1530 ifname = ifp->name;
1531
18adcff1
SG
1532 lmqi_dsec = pim_ifp->gm_specific_query_max_response_time_dsec;
1533 lmqc = pim_ifp->gm_last_member_query_count;
d62a17ae 1534 lmqt_msec = PIM_IGMP_LMQT_MSEC(
1535 lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
1536
a96d64b0 1537 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1538 char group_str[INET_ADDRSTRLEN];
1539 char source_str[INET_ADDRSTRLEN];
1540 pim_inet4_dump("<group?>", group->group_addr, group_str,
1541 sizeof(group_str));
1542 pim_inet4_dump("<source?>", source->source_addr, source_str,
1543 sizeof(source_str));
1544 zlog_debug(
1545 "%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
15569c58
DA
1546 __func__, group_str, source_str, ifname, lmqc,
1547 lmqi_dsec, lmqt_msec);
d62a17ae 1548 }
1549
1550 igmp_source_timer_on(group, source, lmqt_msec);
12e41d03
DL
1551}
1552
a16db099 1553void igmp_v3_send_query(struct gm_group *group, int fd, const char *ifname,
d62a17ae 1554 char *query_buf, int query_buf_size, int num_sources,
1555 struct in_addr dst_addr, struct in_addr group_addr,
1556 int query_max_response_time_dsec, uint8_t s_flag,
1557 uint8_t querier_robustness_variable,
1558 uint16_t querier_query_interval)
12e41d03 1559{
d62a17ae 1560 ssize_t msg_size;
1561 uint8_t max_resp_code;
1562 uint8_t qqic;
1563 ssize_t sent;
1564 struct sockaddr_in to;
1565 socklen_t tolen;
1566 uint16_t checksum;
1567
df5dfb77 1568 assert(num_sources >= 0);
d62a17ae 1569
1570 msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
1571 if (msg_size > query_buf_size) {
af4c2728 1572 flog_err(
450971aa 1573 EC_LIB_DEVELOPMENT,
d62a17ae 1574 "%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
15569c58 1575 __FILE__, __func__, msg_size, query_buf_size);
d62a17ae 1576 return;
1577 }
1578
1579 s_flag = PIM_FORCE_BOOLEAN(s_flag);
df5dfb77 1580 assert((s_flag == 0) || (s_flag == 1));
d62a17ae 1581
1582 max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
1583 qqic = igmp_msg_encode16to8(querier_query_interval);
1584
1585 /*
1586 RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1587
1588 If non-zero, the QRV field contains the [Robustness Variable]
1589 value used by the querier, i.e., the sender of the Query. If the
1590 querier's [Robustness Variable] exceeds 7, the maximum value of
1591 the QRV field, the QRV is set to zero.
1592 */
1593 if (querier_robustness_variable > 7) {
1594 querier_robustness_variable = 0;
1595 }
1596
1597 query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
1598 query_buf[1] = max_resp_code;
1599 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) =
1600 0; /* for computing checksum */
1601 memcpy(query_buf + 4, &group_addr, sizeof(struct in_addr));
1602
1603 query_buf[8] = (s_flag << 3) | querier_robustness_variable;
1604 query_buf[9] = qqic;
1605 *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) =
1606 htons(num_sources);
1607
1608 checksum = in_cksum(query_buf, msg_size);
1609 *(uint16_t *)(query_buf + IGMP_CHECKSUM_OFFSET) = checksum;
1610
55eb347d 1611 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 1612 char dst_str[INET_ADDRSTRLEN];
1613 char group_str[INET_ADDRSTRLEN];
1614 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1615 pim_inet4_dump("<group?>", group_addr, group_str,
1616 sizeof(group_str));
1617 zlog_debug(
1618 "Send IGMPv3 query to %s on %s for group %s, sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x",
1619 dst_str, ifname, group_str, num_sources, msg_size,
1620 s_flag, querier_robustness_variable,
1621 querier_query_interval, qqic);
1622 }
1623
1624 memset(&to, 0, sizeof(to));
1625 to.sin_family = AF_INET;
1626 to.sin_addr = dst_addr;
1627 tolen = sizeof(to);
1628
1629 sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
1630 (struct sockaddr *)&to, tolen);
1631 if (sent != (ssize_t)msg_size) {
1632 char dst_str[INET_ADDRSTRLEN];
1633 char group_str[INET_ADDRSTRLEN];
1634 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
1635 pim_inet4_dump("<group?>", group_addr, group_str,
1636 sizeof(group_str));
1637 if (sent < 0) {
1638 zlog_warn(
1639 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
1640 dst_str, ifname, group_str, msg_size, errno,
1641 safe_strerror(errno));
1642 } else {
1643 zlog_warn(
1644 "Send IGMPv3 query failed due to %s on %s: group=%s msg_size=%zd: sent=%zd",
1645 dst_str, ifname, group_str, msg_size, sent);
1646 }
1647 return;
1648 }
1649
1650 /*
1651 s_flag sanity test: s_flag must be set for general queries
1652
1653 RFC 3376: 6.6.1. Timer Updates
1654
1655 When a router sends or receives a query with a clear Suppress
1656 Router-Side Processing flag, it must update its timers to reflect
1657 the correct timeout values for the group or sources being queried.
1658
1659 General queries don't trigger timer update.
1660 */
1661 if (!s_flag) {
1662 /* general query? */
3ca68c9c 1663 if (group_addr.s_addr == INADDR_ANY) {
d62a17ae 1664 char dst_str[INET_ADDRSTRLEN];
1665 char group_str[INET_ADDRSTRLEN];
1666 pim_inet4_dump("<dst?>", dst_addr, dst_str,
1667 sizeof(dst_str));
1668 pim_inet4_dump("<group?>", group_addr, group_str,
1669 sizeof(group_str));
1670 zlog_warn(
1671 "%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
15569c58 1672 __func__, dst_str, ifname, group_str,
d62a17ae 1673 num_sources);
1674 }
1675 }
b05b72e8
DW
1676}
1677
c5f76fad 1678void igmp_v3_recv_query(struct gm_sock *igmp, const char *from_str,
d62a17ae 1679 char *igmp_msg)
b05b72e8 1680{
d62a17ae 1681 struct interface *ifp;
1682 struct pim_interface *pim_ifp;
1683 struct in_addr group_addr;
1684 uint8_t resv_s_qrv = 0;
1685 uint8_t s_flag = 0;
1686 uint8_t qrv = 0;
1687 int i;
1688
1689 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
1690 ifp = igmp->interface;
1691 pim_ifp = ifp->info;
1692
1693 /*
1694 * RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
1695 *
1696 * Routers adopt the QRV value from the most recently received Query
1697 * as their own [Robustness Variable] value, unless that most
1698 * recently received QRV was zero, in which case the receivers use
1699 * the default [Robustness Variable] value specified in section 8.1
1700 * or a statically configured value.
1701 */
1702 resv_s_qrv = igmp_msg[8];
1703 qrv = 7 & resv_s_qrv;
1704 igmp->querier_robustness_variable =
18adcff1 1705 qrv ? qrv : pim_ifp->gm_default_robustness_variable;
d62a17ae 1706
1707 /*
1708 * RFC 3376: 4.1.7. QQIC (Querier's Query Interval Code)
1709 *
1710 * Multicast routers that are not the current querier adopt the QQI
1711 * value from the most recently received Query as their own [Query
1712 * Interval] value, unless that most recently received QQI was zero,
1713 * in which case the receiving routers use the default.
1714 */
1715 if (igmp->t_other_querier_timer) {
1716 /* other querier present */
1717 uint8_t qqic;
1718 uint16_t qqi;
1719 qqic = igmp_msg[9];
1720 qqi = igmp_msg_decode8to16(qqic);
1721 igmp->querier_query_interval =
18adcff1 1722 qqi ? qqi : pim_ifp->gm_default_query_interval;
d62a17ae 1723
a96d64b0 1724 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1725 char ifaddr_str[INET_ADDRSTRLEN];
1726 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
1727 sizeof(ifaddr_str));
1728 zlog_debug(
1729 "Querier %s new query interval is %s QQI=%u sec (recv QQIC=%02x from %s)",
1730 ifaddr_str,
1731 qqi ? "recv-non-default" : "default",
1732 igmp->querier_query_interval, qqic, from_str);
1733 }
1734 }
1735
1736 /*
1737 * RFC 3376: 6.6.1. Timer Updates
1738 *
1739 * When a router sends or receives a query with a clear Suppress
1740 * Router-Side Processing flag, it must update its timers to reflect
1741 * the correct timeout values for the group or sources being queried.
1742 *
1743 * General queries don't trigger timer update.
1744 */
1745 s_flag = (1 << 3) & resv_s_qrv;
1746
1747 if (!s_flag) {
1748 /* s_flag is clear */
1749
3ca68c9c 1750 if (group_addr.s_addr == INADDR_ANY) {
d62a17ae 1751 /* this is a general query */
1752 /* log that general query should have the s_flag set */
1753 zlog_warn(
1754 "General IGMP query v3 from %s on %s: Suppress Router-Side Processing flag is clear",
1755 from_str, ifp->name);
1756 } else {
a16db099 1757 struct gm_group *group;
d62a17ae 1758
1759 /* this is a non-general query: perform timer updates */
1760
1761 group = find_group_by_addr(igmp, group_addr);
1762 if (group) {
1763 int recv_num_sources = ntohs(*(
1764 uint16_t
1765 *)(igmp_msg
1766 + IGMP_V3_NUMSOURCES_OFFSET));
1767
1768 /*
1769 * RFC 3376: 6.6.1. Timer Updates
1770 * Query Q(G,A): Source Timer for sources in A
1771 * are lowered to LMQT
1772 * Query Q(G): Group Timer is lowered to LMQT
1773 */
1774 if (recv_num_sources < 1) {
1775 /* Query Q(G): Group Timer is lowered to
1776 * LMQT */
1777
1778 igmp_group_timer_lower_to_lmqt(group);
1779 } else {
1780 /* Query Q(G,A): Source Timer for
1781 * sources in A are lowered to LMQT */
1782
1783 /* Scan sources in query and lower their
1784 * timers to LMQT */
1785 struct in_addr *sources =
1786 (struct in_addr
1787 *)(igmp_msg
1788 + IGMP_V3_SOURCES_OFFSET);
1789 for (i = 0; i < recv_num_sources; ++i) {
1790 struct in_addr src_addr;
51700107 1791 struct gm_source *src;
d62a17ae 1792 memcpy(&src_addr, sources + i,
1793 sizeof(struct in_addr));
1794 src = igmp_find_source_by_addr(
1795 group, src_addr);
1796 if (src) {
1797 igmp_source_timer_lower_to_lmqt(
1798 src);
1799 }
1800 }
1801 }
1802 } else {
1803 char group_str[INET_ADDRSTRLEN];
1804 pim_inet4_dump("<group?>", group_addr,
1805 group_str, sizeof(group_str));
1806 zlog_warn(
1807 "IGMP query v3 from %s on %s: could not find group %s for timer update",
1808 from_str, ifp->name, group_str);
1809 }
1810 }
1811 } /* s_flag is clear: timer updates */
b05b72e8
DW
1812}
1813
f6053ff2
A
1814static bool igmp_pkt_grp_addr_ok(struct interface *ifp, const char *from_str,
1815 struct in_addr grp, int rec_type)
1816{
1817 struct pim_interface *pim_ifp;
1818 struct in_addr grp_addr;
1819
1820 pim_ifp = ifp->info;
1821
1822 /* determine filtering status for group */
1823 if (pim_is_group_filtered(pim_ifp, &grp)) {
55eb347d 1824 if (PIM_DEBUG_GM_PACKETS) {
f6053ff2
A
1825 zlog_debug(
1826 "Filtering IGMPv3 group record %pI4 from %s on %s per prefix-list %s",
1827 &grp.s_addr, from_str, ifp->name,
1828 pim_ifp->boundary_oil_plist);
1829 }
1830 return false;
1831 }
1832
1833 /*
1834 * If we receive a igmp report with the group in 224.0.0.0/24
1835 * then we should ignore it
1836 */
1837
1838 grp_addr.s_addr = ntohl(grp.s_addr);
1839
1840 if (pim_is_group_224_0_0_0_24(grp_addr)) {
55eb347d 1841 if (PIM_DEBUG_GM_PACKETS) {
f6053ff2
A
1842 zlog_debug(
1843 "Ignoring IGMPv3 group record %pI4 from %s on %s group range falls in 224.0.0.0/24",
1844 &grp.s_addr, from_str, ifp->name);
1845 }
1846 return false;
1847 }
1848
1849 /*
1850 * RFC 4604
1851 * section 2.2.1
1852 * EXCLUDE mode does not apply to SSM addresses, and an SSM-aware router
1853 * will ignore MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE requests in
1854 * the SSM range.
1855 */
1856 if (pim_is_grp_ssm(pim_ifp->pim, grp)) {
1857 switch (rec_type) {
1858 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
1859 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
55eb347d 1860 if (PIM_DEBUG_GM_PACKETS) {
f6053ff2
A
1861 zlog_debug(
1862 "Ignoring IGMPv3 group record %pI4 from %s on %s exclude mode in SSM range",
1863 &grp.s_addr, from_str, ifp->name);
1864 }
1865 return false;
1866 }
1867 }
1868
1869 return true;
1870}
1871
c5f76fad 1872int igmp_v3_recv_report(struct gm_sock *igmp, struct in_addr from,
d62a17ae 1873 const char *from_str, char *igmp_msg, int igmp_msg_len)
b05b72e8 1874{
d62a17ae 1875 int num_groups;
1876 uint8_t *group_record;
1877 uint8_t *report_pastend = (uint8_t *)igmp_msg + igmp_msg_len;
1878 struct interface *ifp = igmp->interface;
c7c5d685 1879 struct pim_interface *pim_ifp = ifp->info;
d62a17ae 1880 int i;
b0f525a8 1881
f83f3966
MS
1882 if (igmp->mtrace_only)
1883 return 0;
1884
d62a17ae 1885 if (igmp_msg_len < IGMP_V3_MSG_MIN_SIZE) {
1886 zlog_warn(
1887 "Recv IGMP report v3 from %s on %s: size=%d shorter than minimum=%d",
1888 from_str, ifp->name, igmp_msg_len,
1889 IGMP_V3_MSG_MIN_SIZE);
1890 return -1;
1891 }
1892
9041c30a 1893 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
d62a17ae 1894 zlog_warn(
9041c30a
MR
1895 "Recv IGMPv3 report from %s on %s with invalid checksum",
1896 from_str, ifp->name);
d62a17ae 1897 return -1;
1898 }
1899
21313cbf 1900 /* Collecting IGMP Rx stats */
f2058cb4 1901 igmp->igmp_stats.report_v3++;
21313cbf 1902
c7c5d685
DS
1903 if (pim_ifp->igmp_version == 2) {
1904 zlog_warn(
1905 "Received Version 3 packet but interface: %s is configured for version 2",
1906 ifp->name);
1907 return -1;
1908 }
1909
d62a17ae 1910 num_groups = ntohs(
1911 *(uint16_t *)(igmp_msg + IGMP_V3_REPORT_NUMGROUPS_OFFSET));
1912 if (num_groups < 1) {
1913 zlog_warn(
1914 "Recv IGMP report v3 from %s on %s: missing group records",
1915 from_str, ifp->name);
1916 return -1;
1917 }
1918
55eb347d 1919 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 1920 zlog_debug(
9041c30a
MR
1921 "Recv IGMP report v3 from %s on %s: size=%d groups=%d",
1922 from_str, ifp->name, igmp_msg_len, num_groups);
d62a17ae 1923 }
1924
1925 group_record = (uint8_t *)igmp_msg + IGMP_V3_REPORT_GROUPPRECORD_OFFSET;
1926
1927 /* Scan groups */
1928 for (i = 0; i < num_groups; ++i) {
1929 struct in_addr rec_group;
1930 uint8_t *sources;
1931 uint8_t *src;
1932 int rec_type;
1933 int rec_auxdatalen;
1934 int rec_num_sources;
1935 int j;
d62a17ae 1936
1937 if ((group_record + IGMP_V3_GROUP_RECORD_MIN_SIZE)
1938 > report_pastend) {
1939 zlog_warn(
1940 "Recv IGMP report v3 from %s on %s: group record beyond report end",
1941 from_str, ifp->name);
1942 return -1;
1943 }
1944
1945 rec_type = group_record[IGMP_V3_GROUP_RECORD_TYPE_OFFSET];
1946 rec_auxdatalen =
1947 group_record[IGMP_V3_GROUP_RECORD_AUXDATALEN_OFFSET];
1948 rec_num_sources = ntohs(*(
1949 uint16_t *)(group_record
1950 + IGMP_V3_GROUP_RECORD_NUMSOURCES_OFFSET));
1951
1952 memcpy(&rec_group,
1953 group_record + IGMP_V3_GROUP_RECORD_GROUP_OFFSET,
1954 sizeof(struct in_addr));
1955
55eb347d 1956 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 1957 zlog_debug(
ee2bbf7c 1958 " Recv IGMP report v3 from %s on %s: record=%d type=%d auxdatalen=%d sources=%d group=%pI4",
d62a17ae 1959 from_str, ifp->name, i, rec_type,
1960 rec_auxdatalen, rec_num_sources,
ee2bbf7c 1961 &rec_group);
d62a17ae 1962 }
1963
1964 /* Scan sources */
1965
1966 sources = group_record + IGMP_V3_GROUP_RECORD_SOURCE_OFFSET;
1967
1968 for (j = 0, src = sources; j < rec_num_sources; ++j, src += 4) {
1969
1970 if ((src + 4) > report_pastend) {
1971 zlog_warn(
1972 "Recv IGMP report v3 from %s on %s: group source beyond report end",
1973 from_str, ifp->name);
1974 return -1;
1975 }
1976
55eb347d 1977 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 1978 char src_str[200];
1979
1980 if (!inet_ntop(AF_INET, src, src_str,
1981 sizeof(src_str)))
772270f3
QY
1982 snprintf(src_str, sizeof(src_str),
1983 "<source?>");
d62a17ae 1984
1985 zlog_debug(
ee2bbf7c 1986 " Recv IGMP report v3 from %s on %s: record=%d group=%pI4 source=%s",
d62a17ae 1987 from_str, ifp->name, i,
ee2bbf7c 1988 &rec_group, src_str);
d62a17ae 1989 }
1990 } /* for (sources) */
1991
1992
f6053ff2 1993 if (igmp_pkt_grp_addr_ok(ifp, from_str, rec_group, rec_type))
d62a17ae 1994 switch (rec_type) {
1995 case IGMP_GRP_REC_TYPE_MODE_IS_INCLUDE:
1996 igmpv3_report_isin(igmp, from, rec_group,
1997 rec_num_sources,
1998 (struct in_addr *)sources);
1999 break;
2000 case IGMP_GRP_REC_TYPE_MODE_IS_EXCLUDE:
2001 igmpv3_report_isex(
2002 igmp, from, rec_group, rec_num_sources,
2003 (struct in_addr *)sources, 0);
2004 break;
2005 case IGMP_GRP_REC_TYPE_CHANGE_TO_INCLUDE_MODE:
2006 igmpv3_report_toin(igmp, from, rec_group,
2007 rec_num_sources,
2008 (struct in_addr *)sources);
2009 break;
2010 case IGMP_GRP_REC_TYPE_CHANGE_TO_EXCLUDE_MODE:
2011 igmpv3_report_toex(igmp, from, rec_group,
2012 rec_num_sources,
2013 (struct in_addr *)sources);
2014 break;
2015 case IGMP_GRP_REC_TYPE_ALLOW_NEW_SOURCES:
2016 igmpv3_report_allow(igmp, from, rec_group,
2017 rec_num_sources,
2018 (struct in_addr *)sources);
2019 break;
2020 case IGMP_GRP_REC_TYPE_BLOCK_OLD_SOURCES:
2021 igmpv3_report_block(igmp, from, rec_group,
2022 rec_num_sources,
2023 (struct in_addr *)sources);
2024 break;
2025 default:
2026 zlog_warn(
2027 "Recv IGMP report v3 from %s on %s: unknown record type: type=%d",
2028 from_str, ifp->name, rec_type);
2029 }
2030
2031 group_record +=
2032 8 + (rec_num_sources << 2) + (rec_auxdatalen << 2);
d62a17ae 2033
2034 } /* for (group records) */
2035
2036 return 0;
12e41d03 2037}