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