]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmp.c
*: auto-convert to SPDX License IDs
[mirror_frr.git] / pimd / pim_igmp.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
9#include "memory.h"
74fb6c66 10#include "prefix.h"
9df99407 11#include "if.h"
6345a326
DS
12#include "hash.h"
13#include "jhash.h"
3613d898 14#include "lib_errors.h"
12e41d03
DL
15
16#include "pimd.h"
993e3d8e 17#include "pim_instance.h"
12e41d03 18#include "pim_igmp.h"
b05b72e8 19#include "pim_igmpv2.h"
12e41d03 20#include "pim_igmpv3.h"
4d9ad5dc 21#include "pim_igmp_mtrace.h"
12e41d03
DL
22#include "pim_iface.h"
23#include "pim_sock.h"
24#include "pim_mroute.h"
25#include "pim_str.h"
26#include "pim_util.h"
27#include "pim_time.h"
cd6d2858
DL
28#include "pim_ssm.h"
29#include "pim_tib.h"
12e41d03 30
a16db099 31static void group_timer_off(struct gm_group *group);
cc9f21da 32static void pim_igmp_general_query(struct thread *t);
12e41d03 33
cd6d2858
DL
34void igmp_anysource_forward_start(struct pim_instance *pim,
35 struct gm_group *group)
36{
37 struct gm_source *source;
38 struct in_addr src_addr = {.s_addr = 0};
39 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
40 assert(group->group_filtermode_isexcl);
41 assert(listcount(group->group_source_list) < 1);
42
43 source = igmp_get_source_by_addr(group, src_addr, NULL);
44 if (!source) {
45 zlog_warn("%s: Failure to create * source", __func__);
46 return;
47 }
48
49 igmp_source_forward_start(pim, source);
50}
51
52void igmp_anysource_forward_stop(struct gm_group *group)
53{
54 struct gm_source *source;
55 struct in_addr star = {.s_addr = 0};
56
57 source = igmp_find_source_by_addr(group, star);
58 if (source)
59 igmp_source_forward_stop(source);
60}
61
62static void igmp_source_forward_reevaluate_one(struct pim_instance *pim,
261b019f
MR
63 struct gm_source *source,
64 int is_grp_ssm)
cd6d2858
DL
65{
66 pim_sgaddr sg;
67 struct gm_group *group = source->source_group;
cd6d2858
DL
68
69 memset(&sg, 0, sizeof(sg));
70 sg.src = source->source_addr;
71 sg.grp = group->group_addr;
72
261b019f
MR
73 /** if there is no PIM state **/
74 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
75 if (pim_addr_is_any(source->source_addr)) {
76 if (is_grp_ssm) {
77 if (PIM_DEBUG_PIM_EVENTS)
78 zlog_debug(
79 "local membership del for %pSG as G is now SSM",
80 &sg);
81 igmp_source_forward_stop(source);
82 }
83 } else {
84 if (!is_grp_ssm) {
85 if (PIM_DEBUG_PIM_EVENTS)
86 zlog_debug(
87 "local membership del for %pSG as G is now ASM",
88 &sg);
89 igmp_source_forward_stop(source);
90 }
cd6d2858
DL
91 }
92 } else {
261b019f 93 if (!pim_addr_is_any(source->source_addr) && (is_grp_ssm)) {
cd6d2858
DL
94 if (PIM_DEBUG_PIM_EVENTS)
95 zlog_debug(
261b019f 96 "local membership add for %pSG as G is now SSM",
cd6d2858 97 &sg);
261b019f 98 igmp_source_forward_start(pim, source);
cd6d2858
DL
99 }
100 }
101}
102
103void igmp_source_forward_reevaluate_all(struct pim_instance *pim)
104{
105 struct interface *ifp;
106
107 FOR_ALL_INTERFACES (pim->vrf, ifp) {
108 struct pim_interface *pim_ifp = ifp->info;
5c13cb9d 109 struct listnode *grpnode, *grp_nextnode;
cd6d2858
DL
110 struct gm_group *grp;
111 struct pim_ifchannel *ch, *ch_temp;
112
113 if (!pim_ifp)
114 continue;
115
116 /* scan igmp groups */
5c13cb9d
DA
117 for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grpnode,
118 grp_nextnode, grp)) {
cd6d2858
DL
119 struct listnode *srcnode;
120 struct gm_source *src;
261b019f 121 int is_grp_ssm;
cd6d2858 122
6d0a6ede
MR
123 /*
124 * RFC 4604
125 * section 2.2.1
126 * EXCLUDE mode does not apply to SSM addresses,
127 * and an SSM-aware router will ignore
128 * MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE
129 * requests in the SSM range.
130 */
261b019f
MR
131 is_grp_ssm = pim_is_grp_ssm(pim, grp->group_addr);
132 if (is_grp_ssm && grp->group_filtermode_isexcl) {
6d0a6ede
MR
133 igmp_group_delete(grp);
134 } else {
135 /* scan group sources */
136 for (ALL_LIST_ELEMENTS_RO(
137 grp->group_source_list, srcnode,
138 src)) {
261b019f
MR
139 igmp_source_forward_reevaluate_one(
140 pim, src, is_grp_ssm);
6d0a6ede
MR
141 } /* scan group sources */
142 }
143 } /* scan igmp groups */
cd6d2858
DL
144
145 RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb,
146 ch_temp) {
147 if (pim_is_grp_ssm(pim, ch->sg.grp)) {
148 if (pim_addr_is_any(ch->sg.src))
149 pim_ifchannel_delete(ch);
150 }
151 }
152 } /* scan interfaces */
153}
154
155void igmp_source_forward_start(struct pim_instance *pim,
156 struct gm_source *source)
157{
158 struct gm_group *group;
159 pim_sgaddr sg;
160
161 memset(&sg, 0, sizeof(sg));
162 sg.src = source->source_addr;
163 sg.grp = source->source_group->group_addr;
164
a96d64b0 165 if (PIM_DEBUG_GM_TRACE) {
cd6d2858
DL
166 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
167 source->source_group->interface->name,
168 IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
169 }
170
037a2c2e
MR
171 /*
172 * PIM state should not be allowed for ASM group with valid source
173 * address.
174 */
175 if ((!pim_is_grp_ssm(pim, source->source_group->group_addr)) &&
176 !pim_addr_is_any(source->source_addr)) {
177 zlog_warn(
178 "%s: (S,G)=%pSG ASM range having source address, not allowed to create PIM state",
179 __func__, &sg);
180 return;
181 }
182
cd6d2858
DL
183 /* Prevent IGMP interface from installing multicast route multiple
184 times */
185 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
186 return;
187 }
188
189 group = source->source_group;
190
191 if (tib_sg_gm_join(pim, sg, group->interface,
192 &source->source_channel_oil))
193 IGMP_SOURCE_DO_FORWARDING(source->source_flags);
194}
195
196/*
07b12758
DS
197 igmp_source_forward_stop: stop forwarding, but keep the source
198 igmp_source_delete: stop forwarding, and delete the source
cd6d2858
DL
199 */
200void igmp_source_forward_stop(struct gm_source *source)
201{
202 struct pim_interface *pim_oif;
203 struct gm_group *group;
204 pim_sgaddr sg;
205
206 memset(&sg, 0, sizeof(sg));
207 sg.src = source->source_addr;
208 sg.grp = source->source_group->group_addr;
209
a96d64b0 210 if (PIM_DEBUG_GM_TRACE) {
cd6d2858
DL
211 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
212 source->source_group->interface->name,
213 IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
214 }
215
216 /* Prevent IGMP interface from removing multicast route multiple
217 times */
218 if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
219 return;
220 }
221
222 group = source->source_group;
223 pim_oif = group->interface->info;
224
225 tib_sg_gm_prune(pim_oif->pim, sg, group->interface,
226 &source->source_channel_oil);
227 IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
228}
229
b05b72e8
DW
230/* This socket is used for TXing IGMP packets only, IGMP RX happens
231 * in pim_mroute_msg()
232 */
4f977c8e 233static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp)
12e41d03 234{
d62a17ae 235 int fd;
236 int join = 0;
237 struct in_addr group;
f2058cb4 238 struct pim_interface *pim_ifp = ifp->info;
d62a17ae 239
240 fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 1);
241
242 if (fd < 0)
243 return -1;
244
b6fcc0b7
DL
245 if (inet_aton(PIM_ALL_ROUTERS, &group)) {
246 if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex, pim_ifp))
247 ++join;
248 } else {
249 zlog_warn(
250 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
251 __FILE__, __func__, fd, &ifaddr, PIM_ALL_ROUTERS, errno,
252 safe_strerror(errno));
d62a17ae 253 }
254
255 /*
256 IGMP routers periodically send IGMP general queries to
257 AllSystems=224.0.0.1
258 IGMP routers must receive general queries for querier election.
259 */
260 if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
f2058cb4 261 if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex, pim_ifp))
d62a17ae 262 ++join;
263 } else {
264 zlog_warn(
ee2bbf7c
MS
265 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
266 __FILE__, __func__, fd, &ifaddr,
d62a17ae 267 PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
268 }
269
270 if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
f2058cb4
DA
271 if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex,
272 pim_ifp)) {
d62a17ae 273 ++join;
274 }
275 } else {
276 zlog_warn(
ee2bbf7c
MS
277 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
278 __FILE__, __func__, fd, &ifaddr,
d62a17ae 279 PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
280 }
281
282 if (!join) {
09c866e3 283 flog_err_sys(
450971aa 284 EC_LIB_SOCKET,
ee2bbf7c
MS
285 "IGMP socket fd=%d could not join any group on interface address %pI4",
286 fd, &ifaddr);
d62a17ae 287 close(fd);
288 fd = -1;
289 }
290
291 return fd;
12e41d03
DL
292}
293
294#undef IGMP_SOCK_DUMP
295
296#ifdef IGMP_SOCK_DUMP
297static void igmp_sock_dump(array_t *igmp_sock_array)
298{
d62a17ae 299 int size = array_size(igmp_sock_array);
300 for (int i = 0; i < size; ++i) {
301
c5f76fad 302 struct gm_sock *igmp = array_get(igmp_sock_array, i);
d62a17ae 303
ee2bbf7c
MS
304 zlog_debug("%s %s: [%d/%d] igmp_addr=%pI4 fd=%d", __FILE__,
305 __func__, i, size, &igmp->ifaddr,
15569c58 306 igmp->fd);
d62a17ae 307 }
12e41d03
DL
308}
309#endif
310
c5f76fad
SG
311struct gm_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
312 struct in_addr ifaddr)
12e41d03 313{
d62a17ae 314 struct listnode *sock_node;
c5f76fad 315 struct gm_sock *igmp;
12e41d03
DL
316
317#ifdef IGMP_SOCK_DUMP
d62a17ae 318 igmp_sock_dump(igmp_sock_list);
12e41d03
DL
319#endif
320
d62a17ae 321 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
322 if (ifaddr.s_addr == igmp->ifaddr.s_addr)
323 return igmp;
12e41d03 324
d12f46fa 325 return NULL;
12e41d03
DL
326}
327
cc9f21da 328static void pim_igmp_other_querier_expire(struct thread *t)
12e41d03 329{
c5f76fad 330 struct gm_sock *igmp;
d62a17ae 331
332 igmp = THREAD_ARG(t);
333
df5dfb77 334 assert(!igmp->t_igmp_query_timer);
d62a17ae 335
a96d64b0 336 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 337 char ifaddr_str[INET_ADDRSTRLEN];
338 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
339 sizeof(ifaddr_str));
15569c58 340 zlog_debug("%s: Querier %s resuming", __func__, ifaddr_str);
d62a17ae 341 }
9a7cee26 342 /* Mark the interface address as querier address */
343 igmp->querier_addr = igmp->ifaddr;
d62a17ae 344
345 /*
346 We are the current querier, then
347 re-start sending general queries.
348 RFC 2236 - sec 7 Other Querier
349 present timer expired (Send General
350 Query, Set Gen. Query. timer)
351 */
352 pim_igmp_general_query(t);
12e41d03
DL
353}
354
c5f76fad 355void pim_igmp_other_querier_timer_on(struct gm_sock *igmp)
12e41d03 356{
d62a17ae 357 long other_querier_present_interval_msec;
358 struct pim_interface *pim_ifp;
359
df5dfb77
DL
360 assert(igmp);
361 assert(igmp->interface);
362 assert(igmp->interface->info);
d62a17ae 363
364 pim_ifp = igmp->interface->info;
365
366 if (igmp->t_other_querier_timer) {
367 /*
368 There is other querier present already,
369 then reset the other-querier-present timer.
370 */
371
a96d64b0 372 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 373 char ifaddr_str[INET_ADDRSTRLEN];
374 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
375 sizeof(ifaddr_str));
376 zlog_debug(
377 "Querier %s resetting TIMER event for Other-Querier-Present",
378 ifaddr_str);
379 }
380 THREAD_OFF(igmp->t_other_querier_timer);
381 } else {
382 /*
383 We are the current querier, then stop sending general queries:
384 igmp->t_igmp_query_timer = NULL;
385 */
386 pim_igmp_general_query_off(igmp);
387 }
388
389 /*
390 Since this socket is starting the other-querier-present timer,
391 there should not be periodic query timer for this socket.
392 */
df5dfb77 393 assert(!igmp->t_igmp_query_timer);
d62a17ae 394
395 /*
396 RFC 3376: 8.5. Other Querier Present Interval
397
398 The Other Querier Present Interval is the length of time that must
399 pass before a multicast router decides that there is no longer
400 another multicast router which should be the querier. This value
401 MUST be ((the Robustness Variable) times (the Query Interval)) plus
402 (one half of one Query Response Interval).
403
404 other_querier_present_interval_msec = \
405 igmp->querier_robustness_variable * \
406 1000 * igmp->querier_query_interval + \
407 100 * (pim_ifp->query_max_response_time_dsec >> 1);
408 */
409 other_querier_present_interval_msec = PIM_IGMP_OQPI_MSEC(
410 igmp->querier_robustness_variable, igmp->querier_query_interval,
18adcff1 411 pim_ifp->gm_query_max_response_time_dsec);
d62a17ae 412
a96d64b0 413 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 414 char ifaddr_str[INET_ADDRSTRLEN];
415 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
416 sizeof(ifaddr_str));
417 zlog_debug(
418 "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
419 ifaddr_str, other_querier_present_interval_msec / 1000,
420 other_querier_present_interval_msec % 1000);
421 }
422
36417fcc
DS
423 thread_add_timer_msec(router->master, pim_igmp_other_querier_expire,
424 igmp, other_querier_present_interval_msec,
d62a17ae 425 &igmp->t_other_querier_timer);
12e41d03
DL
426}
427
c5f76fad 428void pim_igmp_other_querier_timer_off(struct gm_sock *igmp)
12e41d03 429{
df5dfb77 430 assert(igmp);
d62a17ae 431
a96d64b0 432 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 433 if (igmp->t_other_querier_timer) {
434 char ifaddr_str[INET_ADDRSTRLEN];
435 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
436 sizeof(ifaddr_str));
437 zlog_debug(
438 "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
439 ifaddr_str, igmp->fd, igmp->interface->name);
440 }
441 }
442 THREAD_OFF(igmp->t_other_querier_timer);
12e41d03
DL
443}
444
9041c30a
MR
445int igmp_validate_checksum(char *igmp_msg, int igmp_msg_len)
446{
447 uint16_t recv_checksum;
448 uint16_t checksum;
449
450 IGMP_GET_INT16((unsigned char *)(igmp_msg + IGMP_CHECKSUM_OFFSET),
451 recv_checksum);
452
453 /* Clear the checksum field */
454 memset(igmp_msg + IGMP_CHECKSUM_OFFSET, 0, 2);
455
456 checksum = in_cksum(igmp_msg, igmp_msg_len);
457 if (ntohs(checksum) != recv_checksum) {
458 zlog_warn("Invalid checksum received %x, calculated %x",
459 recv_checksum, ntohs(checksum));
460 return -1;
461 }
462
463 return 0;
464}
465
c5f76fad 466static int igmp_recv_query(struct gm_sock *igmp, int query_version,
d62a17ae 467 int max_resp_code, struct in_addr from,
468 const char *from_str, char *igmp_msg,
469 int igmp_msg_len)
12e41d03 470{
d62a17ae 471 struct interface *ifp;
472 struct pim_interface *pim_ifp;
473 struct in_addr group_addr;
d62a17ae 474
f83f3966
MS
475 if (igmp->mtrace_only)
476 return 0;
477
d62a17ae 478 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
479
480 ifp = igmp->interface;
481 pim_ifp = ifp->info;
482
9041c30a 483 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
d62a17ae 484 zlog_warn(
9041c30a
MR
485 "Recv IGMP query v%d from %s on %s with invalid checksum",
486 query_version, from_str, ifp->name);
d62a17ae 487 return -1;
488 }
489
8890b440 490 if (!pim_if_connected_to_source(ifp, from)) {
55eb347d 491 if (PIM_DEBUG_GM_PACKETS)
8890b440
DS
492 zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
493 ifp->name, from_str);
494 return 0;
495 }
496
096f7609 497 if (if_address_is_local(&from, AF_INET, ifp->vrf->vrf_id)) {
55eb347d 498 if (PIM_DEBUG_GM_PACKETS)
26a0f1e2
DS
499 zlog_debug("Recv IGMP query on interface: %s from ourself %s",
500 ifp->name, from_str);
501 return 0;
502 }
503
21313cbf
MS
504 /* Collecting IGMP Rx stats */
505 switch (query_version) {
506 case 1:
f2058cb4 507 igmp->igmp_stats.query_v1++;
21313cbf
MS
508 break;
509 case 2:
f2058cb4 510 igmp->igmp_stats.query_v2++;
21313cbf
MS
511 break;
512 case 3:
f2058cb4 513 igmp->igmp_stats.query_v3++;
21313cbf
MS
514 break;
515 default:
f2058cb4 516 igmp->igmp_stats.unsupported++;
21313cbf
MS
517 }
518
b0f525a8
QY
519 /*
520 * RFC 3376 defines some guidelines on operating in backwards
521 * compatibility with older versions of IGMP but there are some gaps in
522 * the logic:
d62a17ae 523 *
524 * - once we drop from say version 3 to version 2 we will never go back
b0f525a8
QY
525 * to version 3 even if the node that TXed an IGMP v2 query upgrades
526 * to v3
d62a17ae 527 *
528 * - The node with the lowest IP is the querier so we will only know to
b0f525a8
QY
529 * drop from v3 to v2 if the node that is the querier is also the one
530 * that is running igmp v2. If a non-querier only supports igmp v2
531 * we will have no way of knowing.
d62a17ae 532 *
533 * For now we will simplify things and inform the user that they need to
534 * configure all PIM routers to use the same version of IGMP.
535 */
29e58225 536 if (query_version != pim_ifp->igmp_version) {
d62a17ae 537 zlog_warn(
3efd0893 538 "Recv IGMP query v%d from %s on %s but we are using v%d, please configure all PIM routers on this subnet to use the same IGMP version",
29e58225
DL
539 query_version, from_str, ifp->name,
540 pim_ifp->igmp_version);
d62a17ae 541 return 0;
542 }
543
55eb347d 544 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 545 char group_str[INET_ADDRSTRLEN];
546 pim_inet4_dump("<group?>", group_addr, group_str,
547 sizeof(group_str));
548 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
549 query_version, from_str, ifp->name, group_str);
550 }
551
552 /*
553 RFC 3376: 6.6.2. Querier Election
554
555 When a router receives a query with a lower IP address, it sets
556 the Other-Querier-Present timer to Other Querier Present Interval
557 and ceases to send queries on the network if it was the previously
558 elected querier.
559 */
560 if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
561
5bc4a46b
MR
562 /* As per RFC 2236 section 3:
563 * When a Querier receives a Leave Group message for a group
564 * that has group members on the reception interface, it sends
565 * [Last Member Query Count] Group-Specific Queries every [Last
566 * Member Query Interval] to the group being left. These
567 * Group-Specific Queries have their Max Response time set to
568 * [Last Member Query Interval]. If no Reports are received
569 * after the response time of the last query expires, the
570 * routers assume that the group has no local members, as above.
571 * Any Querier to non-Querier transition is ignored during this
572 * time; the same router keeps sending the Group-Specific
573 * Queries.
574 */
8d9e46a7
MR
575 const struct gm_group *group;
576 const struct listnode *grpnode;
577
578 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
579 group)) {
580 if (!group->t_group_query_retransmit_timer)
581 continue;
5bc4a46b 582
a96d64b0 583 if (PIM_DEBUG_GM_TRACE)
5bc4a46b
MR
584 zlog_debug(
585 "%s: lower address query packet from %s is ignored when last member query interval timer is running",
586 ifp->name, from_str);
587 return 0;
588 }
589
a96d64b0 590 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 591 char ifaddr_str[INET_ADDRSTRLEN];
592 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
593 sizeof(ifaddr_str));
594 zlog_debug(
595 "%s: local address %s (%u) lost querier election to %s (%u)",
596 ifp->name, ifaddr_str,
597 ntohl(igmp->ifaddr.s_addr), from_str,
598 ntohl(from.s_addr));
599 }
a0625bb3
MR
600 /* Reset the other querier timer only if query is received from
601 * the previously elected querier or a better new querier
602 * This will make sure that non-querier elects the new querier
603 * whose ip address is higher than the old querier
604 * in case the old querier goes down via other querier present
605 * timer expiry
606 */
607 if (ntohl(from.s_addr) <= ntohl(igmp->querier_addr.s_addr)) {
9a7cee26 608 igmp->querier_addr.s_addr = from.s_addr;
a0625bb3
MR
609 pim_igmp_other_querier_timer_on(igmp);
610 }
d62a17ae 611 }
612
613 /* IGMP version 3 is the only one where we process the RXed query */
614 if (query_version == 3) {
615 igmp_v3_recv_query(igmp, from_str, igmp_msg);
616 }
617
618 return 0;
12e41d03
DL
619}
620
d62a17ae 621static void on_trace(const char *label, struct interface *ifp,
622 struct in_addr from)
12e41d03 623{
a96d64b0 624 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 625 char from_str[INET_ADDRSTRLEN];
626 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
627 zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
628 }
12e41d03
DL
629}
630
c5f76fad 631static int igmp_v1_recv_report(struct gm_sock *igmp, struct in_addr from,
d62a17ae 632 const char *from_str, char *igmp_msg,
633 int igmp_msg_len)
12e41d03 634{
d62a17ae 635 struct interface *ifp = igmp->interface;
a16db099 636 struct gm_group *group;
d62a17ae 637 struct in_addr group_addr;
12e41d03 638
15569c58 639 on_trace(__func__, igmp->interface, from);
12e41d03 640
f83f3966
MS
641 if (igmp->mtrace_only)
642 return 0;
643
d62a17ae 644 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
645 zlog_warn(
646 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
647 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
648 return -1;
649 }
12e41d03 650
9041c30a
MR
651 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
652 zlog_warn(
653 "Recv IGMP report v1 from %s on %s with invalid checksum",
654 from_str, ifp->name);
655 return -1;
656 }
657
21313cbf 658 /* Collecting IGMP Rx stats */
f2058cb4 659 igmp->igmp_stats.report_v1++;
21313cbf 660
a96d64b0 661 if (PIM_DEBUG_GM_TRACE) {
15569c58 662 zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
d62a17ae 663 }
12e41d03 664
d62a17ae 665 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
12e41d03 666
b0f525a8
QY
667 if (pim_is_group_filtered(ifp->info, &group_addr))
668 return -1;
669
b97047ed 670 /* non-existent group is created as INCLUDE {empty} */
d62a17ae 671 group = igmp_add_group_by_addr(igmp, group_addr);
672 if (!group) {
673 return -1;
674 }
12e41d03 675
d62a17ae 676 group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
12e41d03 677
d62a17ae 678 return 0;
12e41d03
DL
679}
680
88ea79ad 681bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
ff4ad870 682{
88ea79ad
MR
683 char *igmp_msg;
684 int igmp_msg_len;
685 int msg_type;
686 size_t ip_hlen; /* ip header length in bytes */
687
ff4ad870
MR
688 if (len < sizeof(*ip_hdr)) {
689 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
690 sizeof(*ip_hdr));
691 return false;
692 }
693
88ea79ad
MR
694 ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
695 *hlen = ip_hlen;
696
697 if (ip_hlen > len) {
698 zlog_warn(
699 "IGMP packet header claims size %zu, but we only have %zu bytes",
700 ip_hlen, len);
701 return false;
702 }
703
704 igmp_msg = (char *)ip_hdr + ip_hlen;
705 igmp_msg_len = len - ip_hlen;
706 msg_type = *igmp_msg;
707
ff4ad870
MR
708 if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
709 zlog_warn("IGMP message size=%d shorter than minimum=%d",
710 igmp_msg_len, PIM_IGMP_MIN_LEN);
711 return false;
712 }
713
54d7bf0c
MR
714 if ((msg_type != PIM_IGMP_MTRACE_RESPONSE)
715 && (msg_type != PIM_IGMP_MTRACE_QUERY_REQUEST)) {
716 if (ip_hdr->ip_ttl != 1) {
717 zlog_warn(
718 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
719 ip_hdr->ip_ttl);
e748f180 720 return false;
54d7bf0c
MR
721 }
722 }
723
ff4ad870
MR
724 return true;
725}
726
c5f76fad 727int pim_igmp_packet(struct gm_sock *igmp, char *buf, size_t len)
12e41d03 728{
88ea79ad 729 struct ip *ip_hdr = (struct ip *)buf;
d62a17ae 730 size_t ip_hlen; /* ip header length in bytes */
731 char *igmp_msg;
732 int igmp_msg_len;
733 int msg_type;
734 char from_str[INET_ADDRSTRLEN];
735 char to_str[INET_ADDRSTRLEN];
736
88ea79ad 737 if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
f08e6750 738 return -1;
f08e6750 739
d62a17ae 740 igmp_msg = buf + ip_hlen;
d62a17ae 741 igmp_msg_len = len - ip_hlen;
f08e6750
QY
742 msg_type = *igmp_msg;
743
ff4ad870
MR
744 pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
745 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
746
55eb347d 747 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 748 zlog_debug(
c7e663d6
DS
749 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
750 from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl,
d62a17ae 751 msg_type, igmp_msg_len);
752 }
12e41d03 753
d62a17ae 754 switch (msg_type) {
755 case PIM_IGMP_MEMBERSHIP_QUERY: {
756 int max_resp_code = igmp_msg[1];
757 int query_version;
758
759 /*
760 RFC 3376: 7.1. Query Version Distinctions
761 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
762 zero
763 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
764 non-zero
765 IGMPv3 Query: length >= 12 octets
766 */
767
768 if (igmp_msg_len == 8) {
769 query_version = max_resp_code ? 2 : 1;
770 } else if (igmp_msg_len >= 12) {
771 query_version = 3;
772 } else {
773 zlog_warn("Unknown IGMP query version");
774 return -1;
775 }
776
777 return igmp_recv_query(igmp, query_version, max_resp_code,
778 ip_hdr->ip_src, from_str, igmp_msg,
779 igmp_msg_len);
780 }
12e41d03 781
d62a17ae 782 case PIM_IGMP_V3_MEMBERSHIP_REPORT:
783 return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
784 igmp_msg, igmp_msg_len);
12e41d03 785
d62a17ae 786 case PIM_IGMP_V2_MEMBERSHIP_REPORT:
787 return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
788 igmp_msg, igmp_msg_len);
12e41d03 789
d62a17ae 790 case PIM_IGMP_V1_MEMBERSHIP_REPORT:
791 return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
792 igmp_msg, igmp_msg_len);
12e41d03 793
d62a17ae 794 case PIM_IGMP_V2_LEAVE_GROUP:
d1b61cb9
MR
795 return igmp_v2_recv_leave(igmp, ip_hdr, from_str, igmp_msg,
796 igmp_msg_len);
4d9ad5dc
MS
797
798 case PIM_IGMP_MTRACE_RESPONSE:
799 return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
800 from_str, igmp_msg,
801 igmp_msg_len);
4d9ad5dc
MS
802 case PIM_IGMP_MTRACE_QUERY_REQUEST:
803 return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
804 from_str, igmp_msg,
805 igmp_msg_len);
d62a17ae 806 }
12e41d03 807
d62a17ae 808 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
809
21313cbf 810 /* Collecting IGMP Rx stats */
f2058cb4 811 igmp->igmp_stats.unsupported++;
21313cbf 812
d62a17ae 813 return -1;
12e41d03
DL
814}
815
c5f76fad 816void pim_igmp_general_query_on(struct gm_sock *igmp)
12e41d03 817{
d62a17ae 818 struct pim_interface *pim_ifp;
819 int startup_mode;
820 int query_interval;
821
822 /*
823 Since this socket is starting as querier,
824 there should not exist a timer for other-querier-present.
825 */
df5dfb77 826 assert(!igmp->t_other_querier_timer);
d62a17ae 827 pim_ifp = igmp->interface->info;
df5dfb77 828 assert(pim_ifp);
d62a17ae 829
830 /*
831 RFC 3376: 8.6. Startup Query Interval
832
833 The Startup Query Interval is the interval between General Queries
834 sent by a Querier on startup. Default: 1/4 the Query Interval.
835 The first one should be sent out immediately instead of 125/4
836 seconds from now.
837 */
838 startup_mode = igmp->startup_query_count > 0;
839 if (startup_mode) {
840 /*
841 * If this is the first time we are sending a query on a
842 * newly configured igmp interface send it out in 1 second
843 * just to give the entire world a tiny bit of time to settle
844 * else the query interval is:
18adcff1 845 * query_interval = pim_ifp->gm_default_query_interval >> 2;
d62a17ae 846 */
18adcff1
SG
847 if (igmp->startup_query_count ==
848 igmp->querier_robustness_variable)
d62a17ae 849 query_interval = 1;
850 else
29e58225 851 query_interval = PIM_IGMP_SQI(
18adcff1 852 pim_ifp->gm_default_query_interval);
d62a17ae 853
854 --igmp->startup_query_count;
855 } else {
856 query_interval = igmp->querier_query_interval;
857 }
858
a96d64b0 859 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 860 char ifaddr_str[INET_ADDRSTRLEN];
861 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
862 sizeof(ifaddr_str));
863 zlog_debug(
864 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
865 ifaddr_str, query_interval,
866 startup_mode ? "startup" : "non-startup", igmp->fd);
867 }
36417fcc
DS
868 thread_add_timer(router->master, pim_igmp_general_query, igmp,
869 query_interval, &igmp->t_igmp_query_timer);
12e41d03
DL
870}
871
c5f76fad 872void pim_igmp_general_query_off(struct gm_sock *igmp)
12e41d03 873{
df5dfb77 874 assert(igmp);
d62a17ae 875
a96d64b0 876 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 877 if (igmp->t_igmp_query_timer) {
878 char ifaddr_str[INET_ADDRSTRLEN];
879 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
880 sizeof(ifaddr_str));
881 zlog_debug(
882 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
883 ifaddr_str, igmp->fd, igmp->interface->name);
884 }
885 }
886 THREAD_OFF(igmp->t_igmp_query_timer);
12e41d03
DL
887}
888
889/* Issue IGMP general query */
cc9f21da 890static void pim_igmp_general_query(struct thread *t)
12e41d03 891{
c5f76fad 892 struct gm_sock *igmp;
d62a17ae 893 struct in_addr dst_addr;
894 struct in_addr group_addr;
895 struct pim_interface *pim_ifp;
896 int query_buf_size;
897
898 igmp = THREAD_ARG(t);
899
df5dfb77
DL
900 assert(igmp->interface);
901 assert(igmp->interface->info);
d62a17ae 902
903 pim_ifp = igmp->interface->info;
904
29e58225 905 if (pim_ifp->igmp_version == 3) {
d62a17ae 906 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
907 } else {
908 query_buf_size = IGMP_V12_MSG_SIZE;
909 }
910
911 char query_buf[query_buf_size];
912
913 /*
914 RFC3376: 4.1.12. IP Destination Addresses for Queries
915
916 In IGMPv3, General Queries are sent with an IP destination address
917 of 224.0.0.1, the all-systems multicast address. Group-Specific
918 and Group-and-Source-Specific Queries are sent with an IP
919 destination address equal to the multicast address of interest.
920 */
921
922 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
923 group_addr.s_addr = PIM_NET_INADDR_ANY;
924
a96d64b0 925 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 926 char querier_str[INET_ADDRSTRLEN];
927 char dst_str[INET_ADDRSTRLEN];
928 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
929 sizeof(querier_str));
930 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
931 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
932 querier_str, dst_str, igmp->interface->name);
933 }
934
278912ea
DA
935 igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, query_buf,
936 sizeof(query_buf), 0 /* num_sources */, dst_addr,
937 group_addr, pim_ifp->gm_query_max_response_time_dsec,
938 1 /* s_flag: always set for general queries */, igmp);
d62a17ae 939
940 pim_igmp_general_query_on(igmp);
12e41d03
DL
941}
942
c5f76fad 943static void sock_close(struct gm_sock *igmp)
12e41d03 944{
d62a17ae 945 pim_igmp_other_querier_timer_off(igmp);
946 pim_igmp_general_query_off(igmp);
947
a96d64b0 948 if (PIM_DEBUG_GM_TRACE_DETAIL) {
d62a17ae 949 if (igmp->t_igmp_read) {
950 zlog_debug(
ee2bbf7c
MS
951 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
952 &igmp->ifaddr, igmp->fd,
d62a17ae 953 igmp->interface->name);
954 }
955 }
956 THREAD_OFF(igmp->t_igmp_read);
957
958 if (close(igmp->fd)) {
af4c2728 959 flog_err(
450971aa 960 EC_LIB_SOCKET,
ee2bbf7c
MS
961 "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
962 &igmp->ifaddr, igmp->fd,
d62a17ae 963 igmp->interface->name, errno, safe_strerror(errno));
964 }
965
a96d64b0 966 if (PIM_DEBUG_GM_TRACE_DETAIL) {
ee2bbf7c
MS
967 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
968 &igmp->ifaddr, igmp->fd,
d62a17ae 969 igmp->interface->name);
970 }
12e41d03
DL
971}
972
c5f76fad 973void igmp_startup_mode_on(struct gm_sock *igmp)
12e41d03 974{
d62a17ae 975 struct pim_interface *pim_ifp;
12e41d03 976
d62a17ae 977 pim_ifp = igmp->interface->info;
12e41d03 978
d62a17ae 979 /*
980 RFC 3376: 8.7. Startup Query Count
12e41d03 981
d62a17ae 982 The Startup Query Count is the number of Queries sent out on
983 startup, separated by the Startup Query Interval. Default: the
984 Robustness Variable.
985 */
986 igmp->startup_query_count = igmp->querier_robustness_variable;
12e41d03 987
d62a17ae 988 /*
989 Since we're (re)starting, reset QQI to default Query Interval
990 */
18adcff1 991 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
12e41d03
DL
992}
993
a16db099 994static void igmp_group_free(struct gm_group *group)
12e41d03 995{
6a154c88 996 list_delete(&group->group_source_list);
12e41d03 997
d62a17ae 998 XFREE(MTYPE_PIM_IGMP_GROUP, group);
12e41d03
DL
999}
1000
dda4d23c 1001static void igmp_group_count_incr(struct pim_interface *pim_ifp)
339f7695 1002{
3e5d8665
DA
1003 uint32_t group_count = listcount(pim_ifp->gm_group_list);
1004
8934215f
SG
1005 ++pim_ifp->pim->gm_group_count;
1006 if (pim_ifp->pim->gm_group_count == pim_ifp->pim->gm_watermark_limit) {
339f7695 1007 zlog_warn(
1008 "IGMP group count reached watermark limit: %u(vrf: %s)",
8934215f 1009 pim_ifp->pim->gm_group_count,
339f7695 1010 VRF_LOGNAME(pim_ifp->pim->vrf));
1011 }
3e5d8665
DA
1012
1013 if (pim_ifp->igmp_peak_group_count < group_count)
1014 pim_ifp->igmp_peak_group_count = group_count;
339f7695 1015}
1016
dda4d23c 1017static void igmp_group_count_decr(struct pim_interface *pim_ifp)
339f7695 1018{
8934215f 1019 if (pim_ifp->pim->gm_group_count == 0) {
339f7695 1020 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
1021 VRF_LOGNAME(pim_ifp->pim->vrf));
1022 return;
1023 }
1024
8934215f 1025 --pim_ifp->pim->gm_group_count;
339f7695 1026}
1027
a16db099 1028void igmp_group_delete(struct gm_group *group)
12e41d03 1029{
d62a17ae 1030 struct listnode *src_node;
1031 struct listnode *src_nextnode;
51700107 1032 struct gm_source *src;
dda4d23c 1033 struct pim_interface *pim_ifp = group->interface->info;
d62a17ae 1034
a96d64b0 1035 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1036 char group_str[INET_ADDRSTRLEN];
1037 pim_inet4_dump("<group?>", group->group_addr, group_str,
1038 sizeof(group_str));
dda4d23c
DL
1039 zlog_debug("Deleting IGMP group %s from interface %s",
1040 group_str, group->interface->name);
d62a17ae 1041 }
1042
1043 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
1044 src)) {
1045 igmp_source_delete(src);
1046 }
1047
28ef0ee1 1048 THREAD_OFF(group->t_group_query_retransmit_timer);
d62a17ae 1049
1050 group_timer_off(group);
dda4d23c 1051 igmp_group_count_decr(pim_ifp);
18adcff1
SG
1052 listnode_delete(pim_ifp->gm_group_list, group);
1053 hash_release(pim_ifp->gm_group_hash, group);
d62a17ae 1054
1055 igmp_group_free(group);
12e41d03
DL
1056}
1057
a16db099 1058void igmp_group_delete_empty_include(struct gm_group *group)
12e41d03 1059{
df5dfb77
DL
1060 assert(!group->group_filtermode_isexcl);
1061 assert(!listcount(group->group_source_list));
12e41d03 1062
d62a17ae 1063 igmp_group_delete(group);
12e41d03
DL
1064}
1065
c5f76fad 1066void igmp_sock_free(struct gm_sock *igmp)
12e41d03 1067{
df5dfb77
DL
1068 assert(!igmp->t_igmp_read);
1069 assert(!igmp->t_igmp_query_timer);
1070 assert(!igmp->t_other_querier_timer);
12e41d03 1071
d62a17ae 1072 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
12e41d03
DL
1073}
1074
c5f76fad 1075void igmp_sock_delete(struct gm_sock *igmp)
12e41d03 1076{
d62a17ae 1077 struct pim_interface *pim_ifp;
12e41d03 1078
d62a17ae 1079 sock_close(igmp);
12e41d03 1080
d62a17ae 1081 pim_ifp = igmp->interface->info;
12e41d03 1082
18adcff1 1083 listnode_delete(pim_ifp->gm_socket_list, igmp);
12e41d03 1084
d62a17ae 1085 igmp_sock_free(igmp);
dda4d23c 1086
18adcff1 1087 if (!listcount(pim_ifp->gm_socket_list))
dda4d23c 1088 pim_igmp_if_reset(pim_ifp);
12e41d03
DL
1089}
1090
d62a17ae 1091void igmp_sock_delete_all(struct interface *ifp)
cb24fec4 1092{
d62a17ae 1093 struct pim_interface *pim_ifp;
1094 struct listnode *igmp_node, *igmp_nextnode;
c5f76fad 1095 struct gm_sock *igmp;
cb24fec4 1096
d62a17ae 1097 pim_ifp = ifp->info;
cb24fec4 1098
18adcff1 1099 for (ALL_LIST_ELEMENTS(pim_ifp->gm_socket_list, igmp_node,
29e58225 1100 igmp_nextnode, igmp)) {
d62a17ae 1101 igmp_sock_delete(igmp);
1102 }
cb24fec4
DS
1103}
1104
d8b87afe 1105static unsigned int igmp_group_hash_key(const void *arg)
6345a326 1106{
a16db099 1107 const struct gm_group *group = arg;
6345a326 1108
d62a17ae 1109 return jhash_1word(group->group_addr.s_addr, 0);
6345a326
DS
1110}
1111
74df8d6d 1112static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
6345a326 1113{
a16db099
SG
1114 const struct gm_group *g1 = (const struct gm_group *)arg1;
1115 const struct gm_group *g2 = (const struct gm_group *)arg2;
6345a326 1116
d62a17ae 1117 if (g1->group_addr.s_addr == g2->group_addr.s_addr)
74df8d6d 1118 return true;
6345a326 1119
74df8d6d 1120 return false;
6345a326
DS
1121}
1122
dda4d23c
DL
1123void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
1124{
1125 char hash_name[64];
1126
18adcff1
SG
1127 pim_ifp->gm_socket_list = list_new();
1128 pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free;
dda4d23c 1129
18adcff1
SG
1130 pim_ifp->gm_group_list = list_new();
1131 pim_ifp->gm_group_list->del = (void (*)(void *))igmp_group_free;
dda4d23c 1132
a1a4295a 1133 snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
18adcff1
SG
1134 pim_ifp->gm_group_hash = hash_create(igmp_group_hash_key,
1135 igmp_group_hash_equal, hash_name);
dda4d23c
DL
1136}
1137
1138void pim_igmp_if_reset(struct pim_interface *pim_ifp)
1139{
1140 struct listnode *grp_node, *grp_nextnode;
a16db099 1141 struct gm_group *grp;
dda4d23c 1142
18adcff1 1143 for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grp_node, grp_nextnode,
dda4d23c
DL
1144 grp)) {
1145 igmp_group_delete(grp);
1146 }
1147}
1148
1149void pim_igmp_if_fini(struct pim_interface *pim_ifp)
1150{
1151 pim_igmp_if_reset(pim_ifp);
1152
18adcff1
SG
1153 assert(pim_ifp->gm_group_list);
1154 assert(!listcount(pim_ifp->gm_group_list));
dda4d23c 1155
18adcff1
SG
1156 list_delete(&pim_ifp->gm_group_list);
1157 hash_free(pim_ifp->gm_group_hash);
dda4d23c 1158
18adcff1 1159 list_delete(&pim_ifp->gm_socket_list);
dda4d23c
DL
1160}
1161
c5f76fad
SG
1162static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
1163 struct interface *ifp, int mtrace_only)
12e41d03 1164{
d62a17ae 1165 struct pim_interface *pim_ifp;
c5f76fad 1166 struct gm_sock *igmp;
d62a17ae 1167
1168 pim_ifp = ifp->info;
1169
a96d64b0 1170 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1171 zlog_debug(
ee2bbf7c
MS
1172 "Creating IGMP socket fd=%d for address %pI4 on interface %s",
1173 fd, &ifaddr, ifp->name);
d62a17ae 1174 }
1175
1176 igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
d62a17ae 1177
d62a17ae 1178 igmp->fd = fd;
1179 igmp->interface = ifp;
1180 igmp->ifaddr = ifaddr;
9a7cee26 1181 igmp->querier_addr = ifaddr;
d62a17ae 1182 igmp->t_igmp_read = NULL;
1183 igmp->t_igmp_query_timer = NULL;
1184 igmp->t_other_querier_timer = NULL; /* no other querier present */
1185 igmp->querier_robustness_variable =
18adcff1 1186 pim_ifp->gm_default_robustness_variable;
d62a17ae 1187 igmp->sock_creation = pim_time_monotonic_sec();
1188
f2058cb4 1189 igmp_stats_init(&igmp->igmp_stats);
21313cbf 1190
f83f3966
MS
1191 if (mtrace_only) {
1192 igmp->mtrace_only = mtrace_only;
1193 return igmp;
1194 }
1195
1196 igmp->mtrace_only = false;
1197
d62a17ae 1198 /*
1199 igmp_startup_mode_on() will reset QQI:
1200
18adcff1 1201 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
d62a17ae 1202 */
1203 igmp_startup_mode_on(igmp);
1204 pim_igmp_general_query_on(igmp);
1205
1206 return igmp;
12e41d03
DL
1207}
1208
c5f76fad 1209static void igmp_read_on(struct gm_sock *igmp);
7923d317 1210
cc9f21da 1211static void pim_igmp_read(struct thread *t)
7923d317 1212{
d62a17ae 1213 uint8_t buf[10000];
c5f76fad 1214 struct gm_sock *igmp = (struct gm_sock *)THREAD_ARG(t);
023d3e4a
BG
1215 struct sockaddr_storage from;
1216 struct sockaddr_storage to;
d62a17ae 1217 socklen_t fromlen = sizeof(from);
1218 socklen_t tolen = sizeof(to);
1219 ifindex_t ifindex = -1;
d62a17ae 1220 int len;
1221
2e1cc436 1222 while (1) {
d62a17ae 1223 len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from,
1224 &fromlen, &to, &tolen, &ifindex);
1225 if (len < 0) {
1226 if (errno == EINTR)
1227 continue;
1228 if (errno == EWOULDBLOCK || errno == EAGAIN)
1229 break;
1230
1231 goto done;
1232 }
7923d317 1233 }
7923d317 1234
d62a17ae 1235done:
1236 igmp_read_on(igmp);
7923d317
DS
1237}
1238
c5f76fad 1239static void igmp_read_on(struct gm_sock *igmp)
7923d317
DS
1240{
1241
a96d64b0 1242 if (PIM_DEBUG_GM_TRACE_DETAIL) {
d62a17ae 1243 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1244 igmp->fd);
1245 }
36417fcc 1246 thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
d62a17ae 1247 &igmp->t_igmp_read);
7923d317
DS
1248}
1249
c5f76fad
SG
1250struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
1251 struct in_addr ifaddr, struct interface *ifp,
1252 bool mtrace_only)
12e41d03 1253{
c5f76fad 1254 struct gm_sock *igmp;
d05d3f7a 1255 struct sockaddr_in sin;
d62a17ae 1256 int fd;
12e41d03 1257
4f977c8e 1258 fd = igmp_sock_open(ifaddr, ifp);
d62a17ae 1259 if (fd < 0) {
ee2bbf7c
MS
1260 zlog_warn("Could not open IGMP socket for %pI4 on %s",
1261 &ifaddr, ifp->name);
d12f46fa 1262 return NULL;
d62a17ae 1263 }
12e41d03 1264
d05d3f7a
NB
1265 sin.sin_family = AF_INET;
1266 sin.sin_addr = ifaddr;
1267 sin.sin_port = 0;
1268 if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
262649bd
DS
1269 zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
1270 &ifaddr, ifp->name, strerror(errno), errno);
2c85fdd4
DS
1271 close(fd);
1272
d12f46fa 1273 return NULL;
d05d3f7a
NB
1274 }
1275
f83f3966 1276 igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only);
12e41d03 1277
d62a17ae 1278 igmp_read_on(igmp);
7923d317 1279
d62a17ae 1280 listnode_add(igmp_sock_list, igmp);
12e41d03
DL
1281
1282#ifdef IGMP_SOCK_DUMP
d62a17ae 1283 igmp_sock_dump(igmp_sock_array);
12e41d03
DL
1284#endif
1285
d62a17ae 1286 return igmp;
12e41d03
DL
1287}
1288
1289/*
1290 RFC 3376: 6.5. Switching Router Filter-Modes
1291
1292 When a router's filter-mode for a group is EXCLUDE and the group
1293 timer expires, the router filter-mode for the group transitions to
1294 INCLUDE.
1295
1296 A router uses source records with running source timers as its state
1297 for the switch to a filter-mode of INCLUDE. If there are any source
1298 records with source timers greater than zero (i.e., requested to be
1299 forwarded), a router switches to filter-mode of INCLUDE using those
1300 source records. Source records whose timers are zero (from the
1301 previous EXCLUDE mode) are deleted.
1302 */
cc9f21da 1303static void igmp_group_timer(struct thread *t)
12e41d03 1304{
a16db099 1305 struct gm_group *group;
12e41d03 1306
d62a17ae 1307 group = THREAD_ARG(t);
12e41d03 1308
a96d64b0 1309 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1310 char group_str[INET_ADDRSTRLEN];
1311 pim_inet4_dump("<group?>", group->group_addr, group_str,
1312 sizeof(group_str));
15569c58 1313 zlog_debug("%s: Timer for group %s on interface %s", __func__,
dda4d23c 1314 group_str, group->interface->name);
d62a17ae 1315 }
12e41d03 1316
df5dfb77 1317 assert(group->group_filtermode_isexcl);
12e41d03 1318
d62a17ae 1319 group->group_filtermode_isexcl = 0;
12e41d03 1320
d62a17ae 1321 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1322 igmp_anysource_forward_stop(group);
12e41d03 1323
d62a17ae 1324 igmp_source_delete_expired(group->group_source_list);
12e41d03 1325
df5dfb77 1326 assert(!group->group_filtermode_isexcl);
12e41d03 1327
d62a17ae 1328 /*
1329 RFC 3376: 6.2.2. Definition of Group Timers
12e41d03 1330
d62a17ae 1331 If there are no more source records for the group, delete group
1332 record.
1333 */
1334 if (listcount(group->group_source_list) < 1) {
1335 igmp_group_delete_empty_include(group);
1336 }
12e41d03
DL
1337}
1338
a16db099 1339static void group_timer_off(struct gm_group *group)
12e41d03 1340{
d62a17ae 1341 if (!group->t_group_timer)
1342 return;
1343
a96d64b0 1344 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1345 char group_str[INET_ADDRSTRLEN];
1346 pim_inet4_dump("<group?>", group->group_addr, group_str,
1347 sizeof(group_str));
1348 zlog_debug("Cancelling TIMER event for group %s on %s",
dda4d23c 1349 group_str, group->interface->name);
d62a17ae 1350 }
1351 THREAD_OFF(group->t_group_timer);
12e41d03
DL
1352}
1353
a16db099 1354void igmp_group_timer_on(struct gm_group *group, long interval_msec,
d62a17ae 1355 const char *ifname)
12e41d03 1356{
d62a17ae 1357 group_timer_off(group);
1358
95b13dc5 1359 if (PIM_DEBUG_GM_EVENTS) {
d62a17ae 1360 char group_str[INET_ADDRSTRLEN];
1361 pim_inet4_dump("<group?>", group->group_addr, group_str,
1362 sizeof(group_str));
1363 zlog_debug(
1364 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1365 interval_msec / 1000, interval_msec % 1000, group_str,
1366 ifname);
1367 }
1368
1369 /*
1370 RFC 3376: 6.2.2. Definition of Group Timers
1371
1372 The group timer is only used when a group is in EXCLUDE mode and
1373 it represents the time for the *filter-mode* of the group to
1374 expire and switch to INCLUDE mode.
1375 */
df5dfb77 1376 assert(group->group_filtermode_isexcl);
d62a17ae 1377
36417fcc
DS
1378 thread_add_timer_msec(router->master, igmp_group_timer, group,
1379 interval_msec, &group->t_group_timer);
12e41d03
DL
1380}
1381
c5f76fad 1382struct gm_group *find_group_by_addr(struct gm_sock *igmp,
a16db099 1383 struct in_addr group_addr)
12e41d03 1384{
a16db099 1385 struct gm_group lookup;
dda4d23c 1386 struct pim_interface *pim_ifp = igmp->interface->info;
12e41d03 1387
d62a17ae 1388 lookup.group_addr.s_addr = group_addr.s_addr;
12e41d03 1389
18adcff1 1390 return hash_lookup(pim_ifp->gm_group_hash, &lookup);
12e41d03
DL
1391}
1392
c5f76fad 1393struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
a16db099 1394 struct in_addr group_addr)
12e41d03 1395{
a16db099 1396 struct gm_group *group;
dda4d23c 1397 struct pim_interface *pim_ifp = igmp->interface->info;
d62a17ae 1398
1399 group = find_group_by_addr(igmp, group_addr);
1400 if (group) {
1401 return group;
1402 }
1403
1404 if (!pim_is_group_224_4(group_addr)) {
1405 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
15569c58 1406 __func__);
d62a17ae 1407 return NULL;
1408 }
1409
1410 if (pim_is_group_224_0_0_0_24(group_addr)) {
a96d64b0 1411 if (PIM_DEBUG_GM_TRACE)
6b5122a0 1412 zlog_debug(
ee2bbf7c
MS
1413 "%s: Group specified %pI4 is part of 224.0.0.0/24",
1414 __func__, &group_addr);
d62a17ae 1415 return NULL;
1416 }
1417 /*
1418 Non-existant group is created as INCLUDE {empty}:
1419
1420 RFC 3376 - 5.1. Action on Change of Interface State
1421
1422 If no interface state existed for that multicast address before
1423 the change (i.e., the change consisted of creating a new
1424 per-interface record), or if no state exists after the change
1425 (i.e., the change consisted of deleting a per-interface record),
1426 then the "non-existent" state is considered to have a filter mode
1427 of INCLUDE and an empty source list.
1428 */
1429
1430 group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
d62a17ae 1431
1432 group->group_source_list = list_new();
d62a17ae 1433 group->group_source_list->del = (void (*)(void *))igmp_source_free;
1434
1435 group->t_group_timer = NULL;
1436 group->t_group_query_retransmit_timer = NULL;
1437 group->group_specific_query_retransmit_count = 0;
1438 group->group_addr = group_addr;
dda4d23c 1439 group->interface = igmp->interface;
d62a17ae 1440 group->last_igmp_v1_report_dsec = -1;
1441 group->last_igmp_v2_report_dsec = -1;
1442 group->group_creation = pim_time_monotonic_sec();
1443 group->igmp_version = IGMP_DEFAULT_VERSION;
1444
1445 /* initialize new group as INCLUDE {empty} */
1446 group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1447
18adcff1
SG
1448 listnode_add(pim_ifp->gm_group_list, group);
1449 group = hash_get(pim_ifp->gm_group_hash, group, hash_alloc_intern);
d62a17ae 1450
a96d64b0 1451 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1452 char group_str[INET_ADDRSTRLEN];
1453 pim_inet4_dump("<group?>", group->group_addr, group_str,
1454 sizeof(group_str));
1455 zlog_debug(
1456 "Creating new IGMP group %s on socket %d interface %s",
1457 group_str, igmp->fd, igmp->interface->name);
1458 }
1459
dda4d23c 1460 igmp_group_count_incr(pim_ifp);
339f7695 1461
d62a17ae 1462 /*
1463 RFC 3376: 6.2.2. Definition of Group Timers
1464
1465 The group timer is only used when a group is in EXCLUDE mode and
1466 it represents the time for the *filter-mode* of the group to
1467 expire and switch to INCLUDE mode.
1468 */
df5dfb77
DL
1469 assert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1470 assert(!group->t_group_timer); /* group timer == 0 */
d62a17ae 1471
1472 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1473 igmp_anysource_forward_stop(group);
1474
1475 return group;
12e41d03 1476}
b05b72e8 1477
278912ea
DA
1478void igmp_send_query(int igmp_version, struct gm_group *group, char *query_buf,
1479 int query_buf_size, int num_sources,
1480 struct in_addr dst_addr, struct in_addr group_addr,
d62a17ae 1481 int query_max_response_time_dsec, uint8_t s_flag,
278912ea 1482 struct gm_sock *igmp)
b05b72e8 1483{
278912ea
DA
1484 if (pim_addr_is_any(group_addr) &&
1485 ntohl(dst_addr.s_addr) == INADDR_ALLHOSTS_GROUP)
1486 igmp->igmp_stats.general_queries_sent++;
1487 else if (group)
1488 igmp->igmp_stats.group_queries_sent++;
1489
d62a17ae 1490 if (igmp_version == 3) {
278912ea
DA
1491 igmp_v3_send_query(group, igmp->fd, igmp->interface->name,
1492 query_buf, query_buf_size, num_sources,
1493 dst_addr, group_addr,
d62a17ae 1494 query_max_response_time_dsec, s_flag,
278912ea
DA
1495 igmp->querier_robustness_variable,
1496 igmp->querier_query_interval);
d62a17ae 1497 } else if (igmp_version == 2) {
278912ea
DA
1498 igmp_v2_send_query(group, igmp->fd, igmp->interface->name,
1499 query_buf, dst_addr, group_addr,
1500 query_max_response_time_dsec);
d62a17ae 1501 }
b05b72e8 1502}
6741a5bb 1503
1504void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
1505{
1506 struct pim_interface *pim_ifp = ifp->info;
1507 struct listnode *sock_node = NULL;
c5f76fad 1508 struct gm_sock *igmp = NULL;
6741a5bb 1509 struct in_addr dst_addr;
1510 struct in_addr group_addr;
1511 int query_buf_size;
1512
1513 if (!igmp_ver)
1514 igmp_ver = 2;
1515
1516 if (igmp_ver == 3)
1517 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1518 else
1519 query_buf_size = IGMP_V12_MSG_SIZE;
1520
1521 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
1522 group_addr.s_addr = PIM_NET_INADDR_ANY;
1523
a96d64b0 1524 if (PIM_DEBUG_GM_TRACE)
18adcff1 1525 zlog_debug("Issuing general query on request on %s", ifp->name);
6741a5bb 1526
18adcff1 1527 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
6741a5bb 1528
1529 char query_buf[query_buf_size];
1530
278912ea
DA
1531 igmp_send_query(
1532 igmp_ver, 0 /* igmp_group */, query_buf,
1533 sizeof(query_buf), 0 /* num_sources */, dst_addr,
1534 group_addr, pim_ifp->gm_query_max_response_time_dsec,
1535 1 /* s_flag: always set for general queries */, igmp);
6741a5bb 1536 }
1537}