]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmp.c
Merge pull request #11918 from patrasar/show_ipv6_mld_interface_fix
[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 }
9a7cee26 613 if (ntohl(from.s_addr) < ntohl(igmp->querier_addr.s_addr))
614 igmp->querier_addr.s_addr = from.s_addr;
d62a17ae 615
616 pim_igmp_other_querier_timer_on(igmp);
617 }
618
619 /* IGMP version 3 is the only one where we process the RXed query */
620 if (query_version == 3) {
621 igmp_v3_recv_query(igmp, from_str, igmp_msg);
622 }
623
624 return 0;
12e41d03
DL
625}
626
d62a17ae 627static void on_trace(const char *label, struct interface *ifp,
628 struct in_addr from)
12e41d03 629{
a96d64b0 630 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 631 char from_str[INET_ADDRSTRLEN];
632 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
633 zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
634 }
12e41d03
DL
635}
636
c5f76fad 637static int igmp_v1_recv_report(struct gm_sock *igmp, struct in_addr from,
d62a17ae 638 const char *from_str, char *igmp_msg,
639 int igmp_msg_len)
12e41d03 640{
d62a17ae 641 struct interface *ifp = igmp->interface;
a16db099 642 struct gm_group *group;
d62a17ae 643 struct in_addr group_addr;
12e41d03 644
15569c58 645 on_trace(__func__, igmp->interface, from);
12e41d03 646
f83f3966
MS
647 if (igmp->mtrace_only)
648 return 0;
649
d62a17ae 650 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
651 zlog_warn(
652 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
653 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
654 return -1;
655 }
12e41d03 656
9041c30a
MR
657 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
658 zlog_warn(
659 "Recv IGMP report v1 from %s on %s with invalid checksum",
660 from_str, ifp->name);
661 return -1;
662 }
663
21313cbf 664 /* Collecting IGMP Rx stats */
f2058cb4 665 igmp->igmp_stats.report_v1++;
21313cbf 666
a96d64b0 667 if (PIM_DEBUG_GM_TRACE) {
15569c58 668 zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
d62a17ae 669 }
12e41d03 670
d62a17ae 671 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
12e41d03 672
b0f525a8
QY
673 if (pim_is_group_filtered(ifp->info, &group_addr))
674 return -1;
675
b97047ed 676 /* non-existent group is created as INCLUDE {empty} */
d62a17ae 677 group = igmp_add_group_by_addr(igmp, group_addr);
678 if (!group) {
679 return -1;
680 }
12e41d03 681
d62a17ae 682 group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
12e41d03 683
d62a17ae 684 return 0;
12e41d03
DL
685}
686
88ea79ad 687bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
ff4ad870 688{
88ea79ad
MR
689 char *igmp_msg;
690 int igmp_msg_len;
691 int msg_type;
692 size_t ip_hlen; /* ip header length in bytes */
693
ff4ad870
MR
694 if (len < sizeof(*ip_hdr)) {
695 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
696 sizeof(*ip_hdr));
697 return false;
698 }
699
88ea79ad
MR
700 ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
701 *hlen = ip_hlen;
702
703 if (ip_hlen > len) {
704 zlog_warn(
705 "IGMP packet header claims size %zu, but we only have %zu bytes",
706 ip_hlen, len);
707 return false;
708 }
709
710 igmp_msg = (char *)ip_hdr + ip_hlen;
711 igmp_msg_len = len - ip_hlen;
712 msg_type = *igmp_msg;
713
ff4ad870
MR
714 if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
715 zlog_warn("IGMP message size=%d shorter than minimum=%d",
716 igmp_msg_len, PIM_IGMP_MIN_LEN);
717 return false;
718 }
719
54d7bf0c
MR
720 if ((msg_type != PIM_IGMP_MTRACE_RESPONSE)
721 && (msg_type != PIM_IGMP_MTRACE_QUERY_REQUEST)) {
722 if (ip_hdr->ip_ttl != 1) {
723 zlog_warn(
724 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
725 ip_hdr->ip_ttl);
e748f180 726 return false;
54d7bf0c
MR
727 }
728 }
729
ff4ad870
MR
730 return true;
731}
732
c5f76fad 733int pim_igmp_packet(struct gm_sock *igmp, char *buf, size_t len)
12e41d03 734{
88ea79ad 735 struct ip *ip_hdr = (struct ip *)buf;
d62a17ae 736 size_t ip_hlen; /* ip header length in bytes */
737 char *igmp_msg;
738 int igmp_msg_len;
739 int msg_type;
740 char from_str[INET_ADDRSTRLEN];
741 char to_str[INET_ADDRSTRLEN];
742
88ea79ad 743 if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
f08e6750 744 return -1;
f08e6750 745
d62a17ae 746 igmp_msg = buf + ip_hlen;
d62a17ae 747 igmp_msg_len = len - ip_hlen;
f08e6750
QY
748 msg_type = *igmp_msg;
749
ff4ad870
MR
750 pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
751 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
752
55eb347d 753 if (PIM_DEBUG_GM_PACKETS) {
d62a17ae 754 zlog_debug(
c7e663d6
DS
755 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
756 from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl,
d62a17ae 757 msg_type, igmp_msg_len);
758 }
12e41d03 759
d62a17ae 760 switch (msg_type) {
761 case PIM_IGMP_MEMBERSHIP_QUERY: {
762 int max_resp_code = igmp_msg[1];
763 int query_version;
764
765 /*
766 RFC 3376: 7.1. Query Version Distinctions
767 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
768 zero
769 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
770 non-zero
771 IGMPv3 Query: length >= 12 octets
772 */
773
774 if (igmp_msg_len == 8) {
775 query_version = max_resp_code ? 2 : 1;
776 } else if (igmp_msg_len >= 12) {
777 query_version = 3;
778 } else {
779 zlog_warn("Unknown IGMP query version");
780 return -1;
781 }
782
783 return igmp_recv_query(igmp, query_version, max_resp_code,
784 ip_hdr->ip_src, from_str, igmp_msg,
785 igmp_msg_len);
786 }
12e41d03 787
d62a17ae 788 case PIM_IGMP_V3_MEMBERSHIP_REPORT:
789 return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
790 igmp_msg, igmp_msg_len);
12e41d03 791
d62a17ae 792 case PIM_IGMP_V2_MEMBERSHIP_REPORT:
793 return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
794 igmp_msg, igmp_msg_len);
12e41d03 795
d62a17ae 796 case PIM_IGMP_V1_MEMBERSHIP_REPORT:
797 return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
798 igmp_msg, igmp_msg_len);
12e41d03 799
d62a17ae 800 case PIM_IGMP_V2_LEAVE_GROUP:
d1b61cb9
MR
801 return igmp_v2_recv_leave(igmp, ip_hdr, from_str, igmp_msg,
802 igmp_msg_len);
4d9ad5dc
MS
803
804 case PIM_IGMP_MTRACE_RESPONSE:
805 return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
806 from_str, igmp_msg,
807 igmp_msg_len);
4d9ad5dc
MS
808 case PIM_IGMP_MTRACE_QUERY_REQUEST:
809 return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
810 from_str, igmp_msg,
811 igmp_msg_len);
d62a17ae 812 }
12e41d03 813
d62a17ae 814 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
815
21313cbf 816 /* Collecting IGMP Rx stats */
f2058cb4 817 igmp->igmp_stats.unsupported++;
21313cbf 818
d62a17ae 819 return -1;
12e41d03
DL
820}
821
c5f76fad 822void pim_igmp_general_query_on(struct gm_sock *igmp)
12e41d03 823{
d62a17ae 824 struct pim_interface *pim_ifp;
825 int startup_mode;
826 int query_interval;
827
828 /*
829 Since this socket is starting as querier,
830 there should not exist a timer for other-querier-present.
831 */
df5dfb77 832 assert(!igmp->t_other_querier_timer);
d62a17ae 833 pim_ifp = igmp->interface->info;
df5dfb77 834 assert(pim_ifp);
d62a17ae 835
836 /*
837 RFC 3376: 8.6. Startup Query Interval
838
839 The Startup Query Interval is the interval between General Queries
840 sent by a Querier on startup. Default: 1/4 the Query Interval.
841 The first one should be sent out immediately instead of 125/4
842 seconds from now.
843 */
844 startup_mode = igmp->startup_query_count > 0;
845 if (startup_mode) {
846 /*
847 * If this is the first time we are sending a query on a
848 * newly configured igmp interface send it out in 1 second
849 * just to give the entire world a tiny bit of time to settle
850 * else the query interval is:
18adcff1 851 * query_interval = pim_ifp->gm_default_query_interval >> 2;
d62a17ae 852 */
18adcff1
SG
853 if (igmp->startup_query_count ==
854 igmp->querier_robustness_variable)
d62a17ae 855 query_interval = 1;
856 else
29e58225 857 query_interval = PIM_IGMP_SQI(
18adcff1 858 pim_ifp->gm_default_query_interval);
d62a17ae 859
860 --igmp->startup_query_count;
861 } else {
862 query_interval = igmp->querier_query_interval;
863 }
864
a96d64b0 865 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 866 char ifaddr_str[INET_ADDRSTRLEN];
867 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
868 sizeof(ifaddr_str));
869 zlog_debug(
870 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
871 ifaddr_str, query_interval,
872 startup_mode ? "startup" : "non-startup", igmp->fd);
873 }
36417fcc
DS
874 thread_add_timer(router->master, pim_igmp_general_query, igmp,
875 query_interval, &igmp->t_igmp_query_timer);
12e41d03
DL
876}
877
c5f76fad 878void pim_igmp_general_query_off(struct gm_sock *igmp)
12e41d03 879{
df5dfb77 880 assert(igmp);
d62a17ae 881
a96d64b0 882 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 883 if (igmp->t_igmp_query_timer) {
884 char ifaddr_str[INET_ADDRSTRLEN];
885 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
886 sizeof(ifaddr_str));
887 zlog_debug(
888 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
889 ifaddr_str, igmp->fd, igmp->interface->name);
890 }
891 }
892 THREAD_OFF(igmp->t_igmp_query_timer);
12e41d03
DL
893}
894
895/* Issue IGMP general query */
cc9f21da 896static void pim_igmp_general_query(struct thread *t)
12e41d03 897{
c5f76fad 898 struct gm_sock *igmp;
d62a17ae 899 struct in_addr dst_addr;
900 struct in_addr group_addr;
901 struct pim_interface *pim_ifp;
902 int query_buf_size;
903
904 igmp = THREAD_ARG(t);
905
df5dfb77
DL
906 assert(igmp->interface);
907 assert(igmp->interface->info);
d62a17ae 908
909 pim_ifp = igmp->interface->info;
910
29e58225 911 if (pim_ifp->igmp_version == 3) {
d62a17ae 912 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
913 } else {
914 query_buf_size = IGMP_V12_MSG_SIZE;
915 }
916
917 char query_buf[query_buf_size];
918
919 /*
920 RFC3376: 4.1.12. IP Destination Addresses for Queries
921
922 In IGMPv3, General Queries are sent with an IP destination address
923 of 224.0.0.1, the all-systems multicast address. Group-Specific
924 and Group-and-Source-Specific Queries are sent with an IP
925 destination address equal to the multicast address of interest.
926 */
927
928 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
929 group_addr.s_addr = PIM_NET_INADDR_ANY;
930
a96d64b0 931 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 932 char querier_str[INET_ADDRSTRLEN];
933 char dst_str[INET_ADDRSTRLEN];
934 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
935 sizeof(querier_str));
936 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
937 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
938 querier_str, dst_str, igmp->interface->name);
939 }
940
278912ea
DA
941 igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, query_buf,
942 sizeof(query_buf), 0 /* num_sources */, dst_addr,
943 group_addr, pim_ifp->gm_query_max_response_time_dsec,
944 1 /* s_flag: always set for general queries */, igmp);
d62a17ae 945
946 pim_igmp_general_query_on(igmp);
12e41d03
DL
947}
948
c5f76fad 949static void sock_close(struct gm_sock *igmp)
12e41d03 950{
d62a17ae 951 pim_igmp_other_querier_timer_off(igmp);
952 pim_igmp_general_query_off(igmp);
953
a96d64b0 954 if (PIM_DEBUG_GM_TRACE_DETAIL) {
d62a17ae 955 if (igmp->t_igmp_read) {
956 zlog_debug(
ee2bbf7c
MS
957 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
958 &igmp->ifaddr, igmp->fd,
d62a17ae 959 igmp->interface->name);
960 }
961 }
962 THREAD_OFF(igmp->t_igmp_read);
963
964 if (close(igmp->fd)) {
af4c2728 965 flog_err(
450971aa 966 EC_LIB_SOCKET,
ee2bbf7c
MS
967 "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
968 &igmp->ifaddr, igmp->fd,
d62a17ae 969 igmp->interface->name, errno, safe_strerror(errno));
970 }
971
a96d64b0 972 if (PIM_DEBUG_GM_TRACE_DETAIL) {
ee2bbf7c
MS
973 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
974 &igmp->ifaddr, igmp->fd,
d62a17ae 975 igmp->interface->name);
976 }
12e41d03
DL
977}
978
c5f76fad 979void igmp_startup_mode_on(struct gm_sock *igmp)
12e41d03 980{
d62a17ae 981 struct pim_interface *pim_ifp;
12e41d03 982
d62a17ae 983 pim_ifp = igmp->interface->info;
12e41d03 984
d62a17ae 985 /*
986 RFC 3376: 8.7. Startup Query Count
12e41d03 987
d62a17ae 988 The Startup Query Count is the number of Queries sent out on
989 startup, separated by the Startup Query Interval. Default: the
990 Robustness Variable.
991 */
992 igmp->startup_query_count = igmp->querier_robustness_variable;
12e41d03 993
d62a17ae 994 /*
995 Since we're (re)starting, reset QQI to default Query Interval
996 */
18adcff1 997 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
12e41d03
DL
998}
999
a16db099 1000static void igmp_group_free(struct gm_group *group)
12e41d03 1001{
6a154c88 1002 list_delete(&group->group_source_list);
12e41d03 1003
d62a17ae 1004 XFREE(MTYPE_PIM_IGMP_GROUP, group);
12e41d03
DL
1005}
1006
dda4d23c 1007static void igmp_group_count_incr(struct pim_interface *pim_ifp)
339f7695 1008{
3e5d8665
DA
1009 uint32_t group_count = listcount(pim_ifp->gm_group_list);
1010
8934215f
SG
1011 ++pim_ifp->pim->gm_group_count;
1012 if (pim_ifp->pim->gm_group_count == pim_ifp->pim->gm_watermark_limit) {
339f7695 1013 zlog_warn(
1014 "IGMP group count reached watermark limit: %u(vrf: %s)",
8934215f 1015 pim_ifp->pim->gm_group_count,
339f7695 1016 VRF_LOGNAME(pim_ifp->pim->vrf));
1017 }
3e5d8665
DA
1018
1019 if (pim_ifp->igmp_peak_group_count < group_count)
1020 pim_ifp->igmp_peak_group_count = group_count;
339f7695 1021}
1022
dda4d23c 1023static void igmp_group_count_decr(struct pim_interface *pim_ifp)
339f7695 1024{
8934215f 1025 if (pim_ifp->pim->gm_group_count == 0) {
339f7695 1026 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
1027 VRF_LOGNAME(pim_ifp->pim->vrf));
1028 return;
1029 }
1030
8934215f 1031 --pim_ifp->pim->gm_group_count;
339f7695 1032}
1033
a16db099 1034void igmp_group_delete(struct gm_group *group)
12e41d03 1035{
d62a17ae 1036 struct listnode *src_node;
1037 struct listnode *src_nextnode;
51700107 1038 struct gm_source *src;
dda4d23c 1039 struct pim_interface *pim_ifp = group->interface->info;
d62a17ae 1040
a96d64b0 1041 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1042 char group_str[INET_ADDRSTRLEN];
1043 pim_inet4_dump("<group?>", group->group_addr, group_str,
1044 sizeof(group_str));
dda4d23c
DL
1045 zlog_debug("Deleting IGMP group %s from interface %s",
1046 group_str, group->interface->name);
d62a17ae 1047 }
1048
1049 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
1050 src)) {
1051 igmp_source_delete(src);
1052 }
1053
28ef0ee1 1054 THREAD_OFF(group->t_group_query_retransmit_timer);
d62a17ae 1055
1056 group_timer_off(group);
dda4d23c 1057 igmp_group_count_decr(pim_ifp);
18adcff1
SG
1058 listnode_delete(pim_ifp->gm_group_list, group);
1059 hash_release(pim_ifp->gm_group_hash, group);
d62a17ae 1060
1061 igmp_group_free(group);
12e41d03
DL
1062}
1063
a16db099 1064void igmp_group_delete_empty_include(struct gm_group *group)
12e41d03 1065{
df5dfb77
DL
1066 assert(!group->group_filtermode_isexcl);
1067 assert(!listcount(group->group_source_list));
12e41d03 1068
d62a17ae 1069 igmp_group_delete(group);
12e41d03
DL
1070}
1071
c5f76fad 1072void igmp_sock_free(struct gm_sock *igmp)
12e41d03 1073{
df5dfb77
DL
1074 assert(!igmp->t_igmp_read);
1075 assert(!igmp->t_igmp_query_timer);
1076 assert(!igmp->t_other_querier_timer);
12e41d03 1077
d62a17ae 1078 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
12e41d03
DL
1079}
1080
c5f76fad 1081void igmp_sock_delete(struct gm_sock *igmp)
12e41d03 1082{
d62a17ae 1083 struct pim_interface *pim_ifp;
12e41d03 1084
d62a17ae 1085 sock_close(igmp);
12e41d03 1086
d62a17ae 1087 pim_ifp = igmp->interface->info;
12e41d03 1088
18adcff1 1089 listnode_delete(pim_ifp->gm_socket_list, igmp);
12e41d03 1090
d62a17ae 1091 igmp_sock_free(igmp);
dda4d23c 1092
18adcff1 1093 if (!listcount(pim_ifp->gm_socket_list))
dda4d23c 1094 pim_igmp_if_reset(pim_ifp);
12e41d03
DL
1095}
1096
d62a17ae 1097void igmp_sock_delete_all(struct interface *ifp)
cb24fec4 1098{
d62a17ae 1099 struct pim_interface *pim_ifp;
1100 struct listnode *igmp_node, *igmp_nextnode;
c5f76fad 1101 struct gm_sock *igmp;
cb24fec4 1102
d62a17ae 1103 pim_ifp = ifp->info;
cb24fec4 1104
18adcff1 1105 for (ALL_LIST_ELEMENTS(pim_ifp->gm_socket_list, igmp_node,
29e58225 1106 igmp_nextnode, igmp)) {
d62a17ae 1107 igmp_sock_delete(igmp);
1108 }
cb24fec4
DS
1109}
1110
d8b87afe 1111static unsigned int igmp_group_hash_key(const void *arg)
6345a326 1112{
a16db099 1113 const struct gm_group *group = arg;
6345a326 1114
d62a17ae 1115 return jhash_1word(group->group_addr.s_addr, 0);
6345a326
DS
1116}
1117
74df8d6d 1118static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
6345a326 1119{
a16db099
SG
1120 const struct gm_group *g1 = (const struct gm_group *)arg1;
1121 const struct gm_group *g2 = (const struct gm_group *)arg2;
6345a326 1122
d62a17ae 1123 if (g1->group_addr.s_addr == g2->group_addr.s_addr)
74df8d6d 1124 return true;
6345a326 1125
74df8d6d 1126 return false;
6345a326
DS
1127}
1128
dda4d23c
DL
1129void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
1130{
1131 char hash_name[64];
1132
18adcff1
SG
1133 pim_ifp->gm_socket_list = list_new();
1134 pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free;
dda4d23c 1135
18adcff1
SG
1136 pim_ifp->gm_group_list = list_new();
1137 pim_ifp->gm_group_list->del = (void (*)(void *))igmp_group_free;
dda4d23c 1138
a1a4295a 1139 snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
18adcff1
SG
1140 pim_ifp->gm_group_hash = hash_create(igmp_group_hash_key,
1141 igmp_group_hash_equal, hash_name);
dda4d23c
DL
1142}
1143
1144void pim_igmp_if_reset(struct pim_interface *pim_ifp)
1145{
1146 struct listnode *grp_node, *grp_nextnode;
a16db099 1147 struct gm_group *grp;
dda4d23c 1148
18adcff1 1149 for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grp_node, grp_nextnode,
dda4d23c
DL
1150 grp)) {
1151 igmp_group_delete(grp);
1152 }
1153}
1154
1155void pim_igmp_if_fini(struct pim_interface *pim_ifp)
1156{
1157 pim_igmp_if_reset(pim_ifp);
1158
18adcff1
SG
1159 assert(pim_ifp->gm_group_list);
1160 assert(!listcount(pim_ifp->gm_group_list));
dda4d23c 1161
18adcff1
SG
1162 list_delete(&pim_ifp->gm_group_list);
1163 hash_free(pim_ifp->gm_group_hash);
dda4d23c 1164
18adcff1 1165 list_delete(&pim_ifp->gm_socket_list);
dda4d23c
DL
1166}
1167
c5f76fad
SG
1168static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
1169 struct interface *ifp, int mtrace_only)
12e41d03 1170{
d62a17ae 1171 struct pim_interface *pim_ifp;
c5f76fad 1172 struct gm_sock *igmp;
d62a17ae 1173
1174 pim_ifp = ifp->info;
1175
a96d64b0 1176 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1177 zlog_debug(
ee2bbf7c
MS
1178 "Creating IGMP socket fd=%d for address %pI4 on interface %s",
1179 fd, &ifaddr, ifp->name);
d62a17ae 1180 }
1181
1182 igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
d62a17ae 1183
d62a17ae 1184 igmp->fd = fd;
1185 igmp->interface = ifp;
1186 igmp->ifaddr = ifaddr;
9a7cee26 1187 igmp->querier_addr = ifaddr;
d62a17ae 1188 igmp->t_igmp_read = NULL;
1189 igmp->t_igmp_query_timer = NULL;
1190 igmp->t_other_querier_timer = NULL; /* no other querier present */
1191 igmp->querier_robustness_variable =
18adcff1 1192 pim_ifp->gm_default_robustness_variable;
d62a17ae 1193 igmp->sock_creation = pim_time_monotonic_sec();
1194
f2058cb4 1195 igmp_stats_init(&igmp->igmp_stats);
21313cbf 1196
f83f3966
MS
1197 if (mtrace_only) {
1198 igmp->mtrace_only = mtrace_only;
1199 return igmp;
1200 }
1201
1202 igmp->mtrace_only = false;
1203
d62a17ae 1204 /*
1205 igmp_startup_mode_on() will reset QQI:
1206
18adcff1 1207 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
d62a17ae 1208 */
1209 igmp_startup_mode_on(igmp);
1210 pim_igmp_general_query_on(igmp);
1211
1212 return igmp;
12e41d03
DL
1213}
1214
c5f76fad 1215static void igmp_read_on(struct gm_sock *igmp);
7923d317 1216
cc9f21da 1217static void pim_igmp_read(struct thread *t)
7923d317 1218{
d62a17ae 1219 uint8_t buf[10000];
c5f76fad 1220 struct gm_sock *igmp = (struct gm_sock *)THREAD_ARG(t);
023d3e4a
BG
1221 struct sockaddr_storage from;
1222 struct sockaddr_storage to;
d62a17ae 1223 socklen_t fromlen = sizeof(from);
1224 socklen_t tolen = sizeof(to);
1225 ifindex_t ifindex = -1;
d62a17ae 1226 int len;
1227
2e1cc436 1228 while (1) {
d62a17ae 1229 len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from,
1230 &fromlen, &to, &tolen, &ifindex);
1231 if (len < 0) {
1232 if (errno == EINTR)
1233 continue;
1234 if (errno == EWOULDBLOCK || errno == EAGAIN)
1235 break;
1236
1237 goto done;
1238 }
7923d317 1239 }
7923d317 1240
d62a17ae 1241done:
1242 igmp_read_on(igmp);
7923d317
DS
1243}
1244
c5f76fad 1245static void igmp_read_on(struct gm_sock *igmp)
7923d317
DS
1246{
1247
a96d64b0 1248 if (PIM_DEBUG_GM_TRACE_DETAIL) {
d62a17ae 1249 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1250 igmp->fd);
1251 }
36417fcc 1252 thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
d62a17ae 1253 &igmp->t_igmp_read);
7923d317
DS
1254}
1255
c5f76fad
SG
1256struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
1257 struct in_addr ifaddr, struct interface *ifp,
1258 bool mtrace_only)
12e41d03 1259{
c5f76fad 1260 struct gm_sock *igmp;
d05d3f7a 1261 struct sockaddr_in sin;
d62a17ae 1262 int fd;
12e41d03 1263
4f977c8e 1264 fd = igmp_sock_open(ifaddr, ifp);
d62a17ae 1265 if (fd < 0) {
ee2bbf7c
MS
1266 zlog_warn("Could not open IGMP socket for %pI4 on %s",
1267 &ifaddr, ifp->name);
d12f46fa 1268 return NULL;
d62a17ae 1269 }
12e41d03 1270
d05d3f7a
NB
1271 sin.sin_family = AF_INET;
1272 sin.sin_addr = ifaddr;
1273 sin.sin_port = 0;
1274 if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
262649bd
DS
1275 zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
1276 &ifaddr, ifp->name, strerror(errno), errno);
2c85fdd4
DS
1277 close(fd);
1278
d12f46fa 1279 return NULL;
d05d3f7a
NB
1280 }
1281
f83f3966 1282 igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only);
12e41d03 1283
d62a17ae 1284 igmp_read_on(igmp);
7923d317 1285
d62a17ae 1286 listnode_add(igmp_sock_list, igmp);
12e41d03
DL
1287
1288#ifdef IGMP_SOCK_DUMP
d62a17ae 1289 igmp_sock_dump(igmp_sock_array);
12e41d03
DL
1290#endif
1291
d62a17ae 1292 return igmp;
12e41d03
DL
1293}
1294
1295/*
1296 RFC 3376: 6.5. Switching Router Filter-Modes
1297
1298 When a router's filter-mode for a group is EXCLUDE and the group
1299 timer expires, the router filter-mode for the group transitions to
1300 INCLUDE.
1301
1302 A router uses source records with running source timers as its state
1303 for the switch to a filter-mode of INCLUDE. If there are any source
1304 records with source timers greater than zero (i.e., requested to be
1305 forwarded), a router switches to filter-mode of INCLUDE using those
1306 source records. Source records whose timers are zero (from the
1307 previous EXCLUDE mode) are deleted.
1308 */
cc9f21da 1309static void igmp_group_timer(struct thread *t)
12e41d03 1310{
a16db099 1311 struct gm_group *group;
12e41d03 1312
d62a17ae 1313 group = THREAD_ARG(t);
12e41d03 1314
a96d64b0 1315 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1316 char group_str[INET_ADDRSTRLEN];
1317 pim_inet4_dump("<group?>", group->group_addr, group_str,
1318 sizeof(group_str));
15569c58 1319 zlog_debug("%s: Timer for group %s on interface %s", __func__,
dda4d23c 1320 group_str, group->interface->name);
d62a17ae 1321 }
12e41d03 1322
df5dfb77 1323 assert(group->group_filtermode_isexcl);
12e41d03 1324
d62a17ae 1325 group->group_filtermode_isexcl = 0;
12e41d03 1326
d62a17ae 1327 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1328 igmp_anysource_forward_stop(group);
12e41d03 1329
d62a17ae 1330 igmp_source_delete_expired(group->group_source_list);
12e41d03 1331
df5dfb77 1332 assert(!group->group_filtermode_isexcl);
12e41d03 1333
d62a17ae 1334 /*
1335 RFC 3376: 6.2.2. Definition of Group Timers
12e41d03 1336
d62a17ae 1337 If there are no more source records for the group, delete group
1338 record.
1339 */
1340 if (listcount(group->group_source_list) < 1) {
1341 igmp_group_delete_empty_include(group);
1342 }
12e41d03
DL
1343}
1344
a16db099 1345static void group_timer_off(struct gm_group *group)
12e41d03 1346{
d62a17ae 1347 if (!group->t_group_timer)
1348 return;
1349
a96d64b0 1350 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1351 char group_str[INET_ADDRSTRLEN];
1352 pim_inet4_dump("<group?>", group->group_addr, group_str,
1353 sizeof(group_str));
1354 zlog_debug("Cancelling TIMER event for group %s on %s",
dda4d23c 1355 group_str, group->interface->name);
d62a17ae 1356 }
1357 THREAD_OFF(group->t_group_timer);
12e41d03
DL
1358}
1359
a16db099 1360void igmp_group_timer_on(struct gm_group *group, long interval_msec,
d62a17ae 1361 const char *ifname)
12e41d03 1362{
d62a17ae 1363 group_timer_off(group);
1364
95b13dc5 1365 if (PIM_DEBUG_GM_EVENTS) {
d62a17ae 1366 char group_str[INET_ADDRSTRLEN];
1367 pim_inet4_dump("<group?>", group->group_addr, group_str,
1368 sizeof(group_str));
1369 zlog_debug(
1370 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1371 interval_msec / 1000, interval_msec % 1000, group_str,
1372 ifname);
1373 }
1374
1375 /*
1376 RFC 3376: 6.2.2. Definition of Group Timers
1377
1378 The group timer is only used when a group is in EXCLUDE mode and
1379 it represents the time for the *filter-mode* of the group to
1380 expire and switch to INCLUDE mode.
1381 */
df5dfb77 1382 assert(group->group_filtermode_isexcl);
d62a17ae 1383
36417fcc
DS
1384 thread_add_timer_msec(router->master, igmp_group_timer, group,
1385 interval_msec, &group->t_group_timer);
12e41d03
DL
1386}
1387
c5f76fad 1388struct gm_group *find_group_by_addr(struct gm_sock *igmp,
a16db099 1389 struct in_addr group_addr)
12e41d03 1390{
a16db099 1391 struct gm_group lookup;
dda4d23c 1392 struct pim_interface *pim_ifp = igmp->interface->info;
12e41d03 1393
d62a17ae 1394 lookup.group_addr.s_addr = group_addr.s_addr;
12e41d03 1395
18adcff1 1396 return hash_lookup(pim_ifp->gm_group_hash, &lookup);
12e41d03
DL
1397}
1398
c5f76fad 1399struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
a16db099 1400 struct in_addr group_addr)
12e41d03 1401{
a16db099 1402 struct gm_group *group;
dda4d23c 1403 struct pim_interface *pim_ifp = igmp->interface->info;
d62a17ae 1404
1405 group = find_group_by_addr(igmp, group_addr);
1406 if (group) {
1407 return group;
1408 }
1409
1410 if (!pim_is_group_224_4(group_addr)) {
1411 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
15569c58 1412 __func__);
d62a17ae 1413 return NULL;
1414 }
1415
1416 if (pim_is_group_224_0_0_0_24(group_addr)) {
a96d64b0 1417 if (PIM_DEBUG_GM_TRACE)
6b5122a0 1418 zlog_debug(
ee2bbf7c
MS
1419 "%s: Group specified %pI4 is part of 224.0.0.0/24",
1420 __func__, &group_addr);
d62a17ae 1421 return NULL;
1422 }
1423 /*
1424 Non-existant group is created as INCLUDE {empty}:
1425
1426 RFC 3376 - 5.1. Action on Change of Interface State
1427
1428 If no interface state existed for that multicast address before
1429 the change (i.e., the change consisted of creating a new
1430 per-interface record), or if no state exists after the change
1431 (i.e., the change consisted of deleting a per-interface record),
1432 then the "non-existent" state is considered to have a filter mode
1433 of INCLUDE and an empty source list.
1434 */
1435
1436 group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
d62a17ae 1437
1438 group->group_source_list = list_new();
d62a17ae 1439 group->group_source_list->del = (void (*)(void *))igmp_source_free;
1440
1441 group->t_group_timer = NULL;
1442 group->t_group_query_retransmit_timer = NULL;
1443 group->group_specific_query_retransmit_count = 0;
1444 group->group_addr = group_addr;
dda4d23c 1445 group->interface = igmp->interface;
d62a17ae 1446 group->last_igmp_v1_report_dsec = -1;
1447 group->last_igmp_v2_report_dsec = -1;
1448 group->group_creation = pim_time_monotonic_sec();
1449 group->igmp_version = IGMP_DEFAULT_VERSION;
1450
1451 /* initialize new group as INCLUDE {empty} */
1452 group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1453
18adcff1
SG
1454 listnode_add(pim_ifp->gm_group_list, group);
1455 group = hash_get(pim_ifp->gm_group_hash, group, hash_alloc_intern);
d62a17ae 1456
a96d64b0 1457 if (PIM_DEBUG_GM_TRACE) {
d62a17ae 1458 char group_str[INET_ADDRSTRLEN];
1459 pim_inet4_dump("<group?>", group->group_addr, group_str,
1460 sizeof(group_str));
1461 zlog_debug(
1462 "Creating new IGMP group %s on socket %d interface %s",
1463 group_str, igmp->fd, igmp->interface->name);
1464 }
1465
dda4d23c 1466 igmp_group_count_incr(pim_ifp);
339f7695 1467
d62a17ae 1468 /*
1469 RFC 3376: 6.2.2. Definition of Group Timers
1470
1471 The group timer is only used when a group is in EXCLUDE mode and
1472 it represents the time for the *filter-mode* of the group to
1473 expire and switch to INCLUDE mode.
1474 */
df5dfb77
DL
1475 assert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1476 assert(!group->t_group_timer); /* group timer == 0 */
d62a17ae 1477
1478 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1479 igmp_anysource_forward_stop(group);
1480
1481 return group;
12e41d03 1482}
b05b72e8 1483
278912ea
DA
1484void igmp_send_query(int igmp_version, struct gm_group *group, char *query_buf,
1485 int query_buf_size, int num_sources,
1486 struct in_addr dst_addr, struct in_addr group_addr,
d62a17ae 1487 int query_max_response_time_dsec, uint8_t s_flag,
278912ea 1488 struct gm_sock *igmp)
b05b72e8 1489{
278912ea
DA
1490 if (pim_addr_is_any(group_addr) &&
1491 ntohl(dst_addr.s_addr) == INADDR_ALLHOSTS_GROUP)
1492 igmp->igmp_stats.general_queries_sent++;
1493 else if (group)
1494 igmp->igmp_stats.group_queries_sent++;
1495
d62a17ae 1496 if (igmp_version == 3) {
278912ea
DA
1497 igmp_v3_send_query(group, igmp->fd, igmp->interface->name,
1498 query_buf, query_buf_size, num_sources,
1499 dst_addr, group_addr,
d62a17ae 1500 query_max_response_time_dsec, s_flag,
278912ea
DA
1501 igmp->querier_robustness_variable,
1502 igmp->querier_query_interval);
d62a17ae 1503 } else if (igmp_version == 2) {
278912ea
DA
1504 igmp_v2_send_query(group, igmp->fd, igmp->interface->name,
1505 query_buf, dst_addr, group_addr,
1506 query_max_response_time_dsec);
d62a17ae 1507 }
b05b72e8 1508}
6741a5bb 1509
1510void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
1511{
1512 struct pim_interface *pim_ifp = ifp->info;
1513 struct listnode *sock_node = NULL;
c5f76fad 1514 struct gm_sock *igmp = NULL;
6741a5bb 1515 struct in_addr dst_addr;
1516 struct in_addr group_addr;
1517 int query_buf_size;
1518
1519 if (!igmp_ver)
1520 igmp_ver = 2;
1521
1522 if (igmp_ver == 3)
1523 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1524 else
1525 query_buf_size = IGMP_V12_MSG_SIZE;
1526
1527 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
1528 group_addr.s_addr = PIM_NET_INADDR_ANY;
1529
a96d64b0 1530 if (PIM_DEBUG_GM_TRACE)
18adcff1 1531 zlog_debug("Issuing general query on request on %s", ifp->name);
6741a5bb 1532
18adcff1 1533 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
6741a5bb 1534
1535 char query_buf[query_buf_size];
1536
278912ea
DA
1537 igmp_send_query(
1538 igmp_ver, 0 /* igmp_group */, query_buf,
1539 sizeof(query_buf), 0 /* num_sources */, dst_addr,
1540 group_addr, pim_ifp->gm_query_max_response_time_dsec,
1541 1 /* s_flag: always set for general queries */, igmp);
6741a5bb 1542 }
1543}