]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmp.c
Merge pull request #10979 from opensourcerouting/pim-ssm-clean
[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"
30#include "pim_igmp.h"
b05b72e8 31#include "pim_igmpv2.h"
12e41d03 32#include "pim_igmpv3.h"
4d9ad5dc 33#include "pim_igmp_mtrace.h"
12e41d03
DL
34#include "pim_iface.h"
35#include "pim_sock.h"
36#include "pim_mroute.h"
37#include "pim_str.h"
38#include "pim_util.h"
39#include "pim_time.h"
cd6d2858
DL
40#include "pim_ssm.h"
41#include "pim_tib.h"
12e41d03 42
a16db099 43static void group_timer_off(struct gm_group *group);
cc9f21da 44static void pim_igmp_general_query(struct thread *t);
12e41d03 45
cd6d2858
DL
46void igmp_anysource_forward_start(struct pim_instance *pim,
47 struct gm_group *group)
48{
49 struct gm_source *source;
50 struct in_addr src_addr = {.s_addr = 0};
51 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
52 assert(group->group_filtermode_isexcl);
53 assert(listcount(group->group_source_list) < 1);
54
55 source = igmp_get_source_by_addr(group, src_addr, NULL);
56 if (!source) {
57 zlog_warn("%s: Failure to create * source", __func__);
58 return;
59 }
60
61 igmp_source_forward_start(pim, source);
62}
63
64void igmp_anysource_forward_stop(struct gm_group *group)
65{
66 struct gm_source *source;
67 struct in_addr star = {.s_addr = 0};
68
69 source = igmp_find_source_by_addr(group, star);
70 if (source)
71 igmp_source_forward_stop(source);
72}
73
74static void igmp_source_forward_reevaluate_one(struct pim_instance *pim,
261b019f
MR
75 struct gm_source *source,
76 int is_grp_ssm)
cd6d2858
DL
77{
78 pim_sgaddr sg;
79 struct gm_group *group = source->source_group;
cd6d2858
DL
80
81 memset(&sg, 0, sizeof(sg));
82 sg.src = source->source_addr;
83 sg.grp = group->group_addr;
84
261b019f
MR
85 /** if there is no PIM state **/
86 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
87 if (pim_addr_is_any(source->source_addr)) {
88 if (is_grp_ssm) {
89 if (PIM_DEBUG_PIM_EVENTS)
90 zlog_debug(
91 "local membership del for %pSG as G is now SSM",
92 &sg);
93 igmp_source_forward_stop(source);
94 }
95 } else {
96 if (!is_grp_ssm) {
97 if (PIM_DEBUG_PIM_EVENTS)
98 zlog_debug(
99 "local membership del for %pSG as G is now ASM",
100 &sg);
101 igmp_source_forward_stop(source);
102 }
cd6d2858
DL
103 }
104 } else {
261b019f 105 if (!pim_addr_is_any(source->source_addr) && (is_grp_ssm)) {
cd6d2858
DL
106 if (PIM_DEBUG_PIM_EVENTS)
107 zlog_debug(
261b019f 108 "local membership add for %pSG as G is now SSM",
cd6d2858 109 &sg);
261b019f 110 igmp_source_forward_start(pim, source);
cd6d2858
DL
111 }
112 }
113}
114
115void igmp_source_forward_reevaluate_all(struct pim_instance *pim)
116{
117 struct interface *ifp;
118
119 FOR_ALL_INTERFACES (pim->vrf, ifp) {
120 struct pim_interface *pim_ifp = ifp->info;
5c13cb9d 121 struct listnode *grpnode, *grp_nextnode;
cd6d2858
DL
122 struct gm_group *grp;
123 struct pim_ifchannel *ch, *ch_temp;
124
125 if (!pim_ifp)
126 continue;
127
128 /* scan igmp groups */
5c13cb9d
DA
129 for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grpnode,
130 grp_nextnode, grp)) {
cd6d2858
DL
131 struct listnode *srcnode;
132 struct gm_source *src;
261b019f 133 int is_grp_ssm;
cd6d2858 134
6d0a6ede
MR
135 /*
136 * RFC 4604
137 * section 2.2.1
138 * EXCLUDE mode does not apply to SSM addresses,
139 * and an SSM-aware router will ignore
140 * MODE_IS_EXCLUDE and CHANGE_TO_EXCLUDE_MODE
141 * requests in the SSM range.
142 */
261b019f
MR
143 is_grp_ssm = pim_is_grp_ssm(pim, grp->group_addr);
144 if (is_grp_ssm && grp->group_filtermode_isexcl) {
6d0a6ede
MR
145 igmp_group_delete(grp);
146 } else {
147 /* scan group sources */
148 for (ALL_LIST_ELEMENTS_RO(
149 grp->group_source_list, srcnode,
150 src)) {
261b019f
MR
151 igmp_source_forward_reevaluate_one(
152 pim, src, is_grp_ssm);
6d0a6ede
MR
153 } /* scan group sources */
154 }
155 } /* scan igmp groups */
cd6d2858
DL
156
157 RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb,
158 ch_temp) {
159 if (pim_is_grp_ssm(pim, ch->sg.grp)) {
160 if (pim_addr_is_any(ch->sg.src))
161 pim_ifchannel_delete(ch);
162 }
163 }
164 } /* scan interfaces */
165}
166
167void igmp_source_forward_start(struct pim_instance *pim,
168 struct gm_source *source)
169{
170 struct gm_group *group;
171 pim_sgaddr sg;
172
173 memset(&sg, 0, sizeof(sg));
174 sg.src = source->source_addr;
175 sg.grp = source->source_group->group_addr;
176
177 if (PIM_DEBUG_IGMP_TRACE) {
178 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
179 source->source_group->interface->name,
180 IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
181 }
182
037a2c2e
MR
183 /*
184 * PIM state should not be allowed for ASM group with valid source
185 * address.
186 */
187 if ((!pim_is_grp_ssm(pim, source->source_group->group_addr)) &&
188 !pim_addr_is_any(source->source_addr)) {
189 zlog_warn(
190 "%s: (S,G)=%pSG ASM range having source address, not allowed to create PIM state",
191 __func__, &sg);
192 return;
193 }
194
cd6d2858
DL
195 /* Prevent IGMP interface from installing multicast route multiple
196 times */
197 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
198 return;
199 }
200
201 group = source->source_group;
202
203 if (tib_sg_gm_join(pim, sg, group->interface,
204 &source->source_channel_oil))
205 IGMP_SOURCE_DO_FORWARDING(source->source_flags);
206}
207
208/*
07b12758
DS
209 igmp_source_forward_stop: stop forwarding, but keep the source
210 igmp_source_delete: stop forwarding, and delete the source
cd6d2858
DL
211 */
212void igmp_source_forward_stop(struct gm_source *source)
213{
214 struct pim_interface *pim_oif;
215 struct gm_group *group;
216 pim_sgaddr sg;
217
218 memset(&sg, 0, sizeof(sg));
219 sg.src = source->source_addr;
220 sg.grp = source->source_group->group_addr;
221
222 if (PIM_DEBUG_IGMP_TRACE) {
223 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
224 source->source_group->interface->name,
225 IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
226 }
227
228 /* Prevent IGMP interface from removing multicast route multiple
229 times */
230 if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
231 return;
232 }
233
234 group = source->source_group;
235 pim_oif = group->interface->info;
236
237 tib_sg_gm_prune(pim_oif->pim, sg, group->interface,
238 &source->source_channel_oil);
239 IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
240}
241
b05b72e8
DW
242/* This socket is used for TXing IGMP packets only, IGMP RX happens
243 * in pim_mroute_msg()
244 */
d62a17ae 245static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
246 uint32_t pim_options)
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
349 if (PIM_DEBUG_IGMP_TRACE) {
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
385 if (PIM_DEBUG_IGMP_TRACE) {
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
426 if (PIM_DEBUG_IGMP_TRACE) {
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
445 if (PIM_DEBUG_IGMP_TRACE) {
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
DS
503 if (!pim_if_connected_to_source(ifp, from)) {
504 if (PIM_DEBUG_IGMP_PACKETS)
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)) {
26a0f1e2
DS
511 if (PIM_DEBUG_IGMP_PACKETS)
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
557 if (PIM_DEBUG_IGMP_PACKETS) {
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 */
588 struct gm_group *group;
589
590 group = find_group_by_addr(igmp, group_addr);
591 if (group && group->t_group_query_retransmit_timer) {
592 if (PIM_DEBUG_IGMP_TRACE)
593 zlog_debug(
594 "%s: lower address query packet from %s is ignored when last member query interval timer is running",
595 ifp->name, from_str);
596 return 0;
597 }
598
d62a17ae 599 if (PIM_DEBUG_IGMP_TRACE) {
600 char ifaddr_str[INET_ADDRSTRLEN];
601 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
602 sizeof(ifaddr_str));
603 zlog_debug(
604 "%s: local address %s (%u) lost querier election to %s (%u)",
605 ifp->name, ifaddr_str,
606 ntohl(igmp->ifaddr.s_addr), from_str,
607 ntohl(from.s_addr));
608 }
9a7cee26 609 if (ntohl(from.s_addr) < ntohl(igmp->querier_addr.s_addr))
610 igmp->querier_addr.s_addr = from.s_addr;
d62a17ae 611
612 pim_igmp_other_querier_timer_on(igmp);
613 }
614
615 /* IGMP version 3 is the only one where we process the RXed query */
616 if (query_version == 3) {
617 igmp_v3_recv_query(igmp, from_str, igmp_msg);
618 }
619
620 return 0;
12e41d03
DL
621}
622
d62a17ae 623static void on_trace(const char *label, struct interface *ifp,
624 struct in_addr from)
12e41d03 625{
d62a17ae 626 if (PIM_DEBUG_IGMP_TRACE) {
627 char from_str[INET_ADDRSTRLEN];
628 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
629 zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
630 }
12e41d03
DL
631}
632
c5f76fad 633static int igmp_v1_recv_report(struct gm_sock *igmp, struct in_addr from,
d62a17ae 634 const char *from_str, char *igmp_msg,
635 int igmp_msg_len)
12e41d03 636{
d62a17ae 637 struct interface *ifp = igmp->interface;
a16db099 638 struct gm_group *group;
d62a17ae 639 struct in_addr group_addr;
12e41d03 640
15569c58 641 on_trace(__func__, igmp->interface, from);
12e41d03 642
f83f3966
MS
643 if (igmp->mtrace_only)
644 return 0;
645
d62a17ae 646 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
647 zlog_warn(
648 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
649 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
650 return -1;
651 }
12e41d03 652
9041c30a
MR
653 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
654 zlog_warn(
655 "Recv IGMP report v1 from %s on %s with invalid checksum",
656 from_str, ifp->name);
657 return -1;
658 }
659
21313cbf 660 /* Collecting IGMP Rx stats */
f2058cb4 661 igmp->igmp_stats.report_v1++;
21313cbf 662
d62a17ae 663 if (PIM_DEBUG_IGMP_TRACE) {
15569c58 664 zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
d62a17ae 665 }
12e41d03 666
d62a17ae 667 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
12e41d03 668
b0f525a8
QY
669 if (pim_is_group_filtered(ifp->info, &group_addr))
670 return -1;
671
d62a17ae 672 /* non-existant group is created as INCLUDE {empty} */
673 group = igmp_add_group_by_addr(igmp, group_addr);
674 if (!group) {
675 return -1;
676 }
12e41d03 677
d62a17ae 678 group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
12e41d03 679
d62a17ae 680 return 0;
12e41d03
DL
681}
682
88ea79ad 683bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
ff4ad870 684{
88ea79ad
MR
685 char *igmp_msg;
686 int igmp_msg_len;
687 int msg_type;
688 size_t ip_hlen; /* ip header length in bytes */
689
ff4ad870
MR
690 if (len < sizeof(*ip_hdr)) {
691 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
692 sizeof(*ip_hdr));
693 return false;
694 }
695
88ea79ad
MR
696 ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
697 *hlen = ip_hlen;
698
699 if (ip_hlen > len) {
700 zlog_warn(
701 "IGMP packet header claims size %zu, but we only have %zu bytes",
702 ip_hlen, len);
703 return false;
704 }
705
706 igmp_msg = (char *)ip_hdr + ip_hlen;
707 igmp_msg_len = len - ip_hlen;
708 msg_type = *igmp_msg;
709
ff4ad870
MR
710 if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
711 zlog_warn("IGMP message size=%d shorter than minimum=%d",
712 igmp_msg_len, PIM_IGMP_MIN_LEN);
713 return false;
714 }
715
54d7bf0c
MR
716 if ((msg_type != PIM_IGMP_MTRACE_RESPONSE)
717 && (msg_type != PIM_IGMP_MTRACE_QUERY_REQUEST)) {
718 if (ip_hdr->ip_ttl != 1) {
719 zlog_warn(
720 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
721 ip_hdr->ip_ttl);
e748f180 722 return false;
54d7bf0c
MR
723 }
724 }
725
ff4ad870
MR
726 return true;
727}
728
c5f76fad 729int pim_igmp_packet(struct gm_sock *igmp, char *buf, size_t len)
12e41d03 730{
88ea79ad 731 struct ip *ip_hdr = (struct ip *)buf;
d62a17ae 732 size_t ip_hlen; /* ip header length in bytes */
733 char *igmp_msg;
734 int igmp_msg_len;
735 int msg_type;
736 char from_str[INET_ADDRSTRLEN];
737 char to_str[INET_ADDRSTRLEN];
738
88ea79ad 739 if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
f08e6750 740 return -1;
f08e6750 741
d62a17ae 742 igmp_msg = buf + ip_hlen;
d62a17ae 743 igmp_msg_len = len - ip_hlen;
f08e6750
QY
744 msg_type = *igmp_msg;
745
ff4ad870
MR
746 pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
747 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
748
d62a17ae 749 if (PIM_DEBUG_IGMP_PACKETS) {
750 zlog_debug(
c7e663d6
DS
751 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
752 from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl,
d62a17ae 753 msg_type, igmp_msg_len);
754 }
12e41d03 755
d62a17ae 756 switch (msg_type) {
757 case PIM_IGMP_MEMBERSHIP_QUERY: {
758 int max_resp_code = igmp_msg[1];
759 int query_version;
760
761 /*
762 RFC 3376: 7.1. Query Version Distinctions
763 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
764 zero
765 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
766 non-zero
767 IGMPv3 Query: length >= 12 octets
768 */
769
770 if (igmp_msg_len == 8) {
771 query_version = max_resp_code ? 2 : 1;
772 } else if (igmp_msg_len >= 12) {
773 query_version = 3;
774 } else {
775 zlog_warn("Unknown IGMP query version");
776 return -1;
777 }
778
779 return igmp_recv_query(igmp, query_version, max_resp_code,
780 ip_hdr->ip_src, from_str, igmp_msg,
781 igmp_msg_len);
782 }
12e41d03 783
d62a17ae 784 case PIM_IGMP_V3_MEMBERSHIP_REPORT:
785 return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
786 igmp_msg, igmp_msg_len);
12e41d03 787
d62a17ae 788 case PIM_IGMP_V2_MEMBERSHIP_REPORT:
789 return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
790 igmp_msg, igmp_msg_len);
12e41d03 791
d62a17ae 792 case PIM_IGMP_V1_MEMBERSHIP_REPORT:
793 return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
794 igmp_msg, igmp_msg_len);
12e41d03 795
d62a17ae 796 case PIM_IGMP_V2_LEAVE_GROUP:
d1b61cb9
MR
797 return igmp_v2_recv_leave(igmp, ip_hdr, from_str, igmp_msg,
798 igmp_msg_len);
4d9ad5dc
MS
799
800 case PIM_IGMP_MTRACE_RESPONSE:
801 return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
802 from_str, igmp_msg,
803 igmp_msg_len);
4d9ad5dc
MS
804 case PIM_IGMP_MTRACE_QUERY_REQUEST:
805 return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
806 from_str, igmp_msg,
807 igmp_msg_len);
d62a17ae 808 }
12e41d03 809
d62a17ae 810 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
811
21313cbf 812 /* Collecting IGMP Rx stats */
f2058cb4 813 igmp->igmp_stats.unsupported++;
21313cbf 814
d62a17ae 815 return -1;
12e41d03
DL
816}
817
c5f76fad 818void pim_igmp_general_query_on(struct gm_sock *igmp)
12e41d03 819{
d62a17ae 820 struct pim_interface *pim_ifp;
821 int startup_mode;
822 int query_interval;
823
824 /*
825 Since this socket is starting as querier,
826 there should not exist a timer for other-querier-present.
827 */
df5dfb77 828 assert(!igmp->t_other_querier_timer);
d62a17ae 829 pim_ifp = igmp->interface->info;
df5dfb77 830 assert(pim_ifp);
d62a17ae 831
832 /*
833 RFC 3376: 8.6. Startup Query Interval
834
835 The Startup Query Interval is the interval between General Queries
836 sent by a Querier on startup. Default: 1/4 the Query Interval.
837 The first one should be sent out immediately instead of 125/4
838 seconds from now.
839 */
840 startup_mode = igmp->startup_query_count > 0;
841 if (startup_mode) {
842 /*
843 * If this is the first time we are sending a query on a
844 * newly configured igmp interface send it out in 1 second
845 * just to give the entire world a tiny bit of time to settle
846 * else the query interval is:
18adcff1 847 * query_interval = pim_ifp->gm_default_query_interval >> 2;
d62a17ae 848 */
18adcff1
SG
849 if (igmp->startup_query_count ==
850 igmp->querier_robustness_variable)
d62a17ae 851 query_interval = 1;
852 else
29e58225 853 query_interval = PIM_IGMP_SQI(
18adcff1 854 pim_ifp->gm_default_query_interval);
d62a17ae 855
856 --igmp->startup_query_count;
857 } else {
858 query_interval = igmp->querier_query_interval;
859 }
860
861 if (PIM_DEBUG_IGMP_TRACE) {
862 char ifaddr_str[INET_ADDRSTRLEN];
863 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
864 sizeof(ifaddr_str));
865 zlog_debug(
866 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
867 ifaddr_str, query_interval,
868 startup_mode ? "startup" : "non-startup", igmp->fd);
869 }
36417fcc
DS
870 thread_add_timer(router->master, pim_igmp_general_query, igmp,
871 query_interval, &igmp->t_igmp_query_timer);
12e41d03
DL
872}
873
c5f76fad 874void pim_igmp_general_query_off(struct gm_sock *igmp)
12e41d03 875{
df5dfb77 876 assert(igmp);
d62a17ae 877
878 if (PIM_DEBUG_IGMP_TRACE) {
879 if (igmp->t_igmp_query_timer) {
880 char ifaddr_str[INET_ADDRSTRLEN];
881 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
882 sizeof(ifaddr_str));
883 zlog_debug(
884 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
885 ifaddr_str, igmp->fd, igmp->interface->name);
886 }
887 }
888 THREAD_OFF(igmp->t_igmp_query_timer);
12e41d03
DL
889}
890
891/* Issue IGMP general query */
cc9f21da 892static void pim_igmp_general_query(struct thread *t)
12e41d03 893{
c5f76fad 894 struct gm_sock *igmp;
d62a17ae 895 struct in_addr dst_addr;
896 struct in_addr group_addr;
897 struct pim_interface *pim_ifp;
898 int query_buf_size;
899
900 igmp = THREAD_ARG(t);
901
df5dfb77
DL
902 assert(igmp->interface);
903 assert(igmp->interface->info);
d62a17ae 904
905 pim_ifp = igmp->interface->info;
906
29e58225 907 if (pim_ifp->igmp_version == 3) {
d62a17ae 908 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
909 } else {
910 query_buf_size = IGMP_V12_MSG_SIZE;
911 }
912
913 char query_buf[query_buf_size];
914
915 /*
916 RFC3376: 4.1.12. IP Destination Addresses for Queries
917
918 In IGMPv3, General Queries are sent with an IP destination address
919 of 224.0.0.1, the all-systems multicast address. Group-Specific
920 and Group-and-Source-Specific Queries are sent with an IP
921 destination address equal to the multicast address of interest.
922 */
923
924 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
925 group_addr.s_addr = PIM_NET_INADDR_ANY;
926
927 if (PIM_DEBUG_IGMP_TRACE) {
928 char querier_str[INET_ADDRSTRLEN];
929 char dst_str[INET_ADDRSTRLEN];
930 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
931 sizeof(querier_str));
932 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
933 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
934 querier_str, dst_str, igmp->interface->name);
935 }
936
278912ea
DA
937 igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, query_buf,
938 sizeof(query_buf), 0 /* num_sources */, dst_addr,
939 group_addr, pim_ifp->gm_query_max_response_time_dsec,
940 1 /* s_flag: always set for general queries */, igmp);
d62a17ae 941
942 pim_igmp_general_query_on(igmp);
12e41d03
DL
943}
944
c5f76fad 945static void sock_close(struct gm_sock *igmp)
12e41d03 946{
d62a17ae 947 pim_igmp_other_querier_timer_off(igmp);
948 pim_igmp_general_query_off(igmp);
949
950 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
951 if (igmp->t_igmp_read) {
952 zlog_debug(
ee2bbf7c
MS
953 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
954 &igmp->ifaddr, igmp->fd,
d62a17ae 955 igmp->interface->name);
956 }
957 }
958 THREAD_OFF(igmp->t_igmp_read);
959
960 if (close(igmp->fd)) {
af4c2728 961 flog_err(
450971aa 962 EC_LIB_SOCKET,
ee2bbf7c
MS
963 "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
964 &igmp->ifaddr, igmp->fd,
d62a17ae 965 igmp->interface->name, errno, safe_strerror(errno));
966 }
967
968 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
ee2bbf7c
MS
969 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
970 &igmp->ifaddr, igmp->fd,
d62a17ae 971 igmp->interface->name);
972 }
12e41d03
DL
973}
974
c5f76fad 975void igmp_startup_mode_on(struct gm_sock *igmp)
12e41d03 976{
d62a17ae 977 struct pim_interface *pim_ifp;
12e41d03 978
d62a17ae 979 pim_ifp = igmp->interface->info;
12e41d03 980
d62a17ae 981 /*
982 RFC 3376: 8.7. Startup Query Count
12e41d03 983
d62a17ae 984 The Startup Query Count is the number of Queries sent out on
985 startup, separated by the Startup Query Interval. Default: the
986 Robustness Variable.
987 */
988 igmp->startup_query_count = igmp->querier_robustness_variable;
12e41d03 989
d62a17ae 990 /*
991 Since we're (re)starting, reset QQI to default Query Interval
992 */
18adcff1 993 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
12e41d03
DL
994}
995
a16db099 996static void igmp_group_free(struct gm_group *group)
12e41d03 997{
6a154c88 998 list_delete(&group->group_source_list);
12e41d03 999
d62a17ae 1000 XFREE(MTYPE_PIM_IGMP_GROUP, group);
12e41d03
DL
1001}
1002
dda4d23c 1003static void igmp_group_count_incr(struct pim_interface *pim_ifp)
339f7695 1004{
3e5d8665
DA
1005 uint32_t group_count = listcount(pim_ifp->gm_group_list);
1006
339f7695 1007 ++pim_ifp->pim->igmp_group_count;
1008 if (pim_ifp->pim->igmp_group_count
1009 == pim_ifp->pim->igmp_watermark_limit) {
1010 zlog_warn(
1011 "IGMP group count reached watermark limit: %u(vrf: %s)",
1012 pim_ifp->pim->igmp_group_count,
1013 VRF_LOGNAME(pim_ifp->pim->vrf));
1014 }
3e5d8665
DA
1015
1016 if (pim_ifp->igmp_peak_group_count < group_count)
1017 pim_ifp->igmp_peak_group_count = group_count;
339f7695 1018}
1019
dda4d23c 1020static void igmp_group_count_decr(struct pim_interface *pim_ifp)
339f7695 1021{
339f7695 1022 if (pim_ifp->pim->igmp_group_count == 0) {
1023 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
1024 VRF_LOGNAME(pim_ifp->pim->vrf));
1025 return;
1026 }
1027
1028 --pim_ifp->pim->igmp_group_count;
1029}
1030
a16db099 1031void igmp_group_delete(struct gm_group *group)
12e41d03 1032{
d62a17ae 1033 struct listnode *src_node;
1034 struct listnode *src_nextnode;
51700107 1035 struct gm_source *src;
dda4d23c 1036 struct pim_interface *pim_ifp = group->interface->info;
d62a17ae 1037
1038 if (PIM_DEBUG_IGMP_TRACE) {
1039 char group_str[INET_ADDRSTRLEN];
1040 pim_inet4_dump("<group?>", group->group_addr, group_str,
1041 sizeof(group_str));
dda4d23c
DL
1042 zlog_debug("Deleting IGMP group %s from interface %s",
1043 group_str, group->interface->name);
d62a17ae 1044 }
1045
1046 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
1047 src)) {
1048 igmp_source_delete(src);
1049 }
1050
28ef0ee1 1051 THREAD_OFF(group->t_group_query_retransmit_timer);
d62a17ae 1052
1053 group_timer_off(group);
dda4d23c 1054 igmp_group_count_decr(pim_ifp);
18adcff1
SG
1055 listnode_delete(pim_ifp->gm_group_list, group);
1056 hash_release(pim_ifp->gm_group_hash, group);
d62a17ae 1057
1058 igmp_group_free(group);
12e41d03
DL
1059}
1060
a16db099 1061void igmp_group_delete_empty_include(struct gm_group *group)
12e41d03 1062{
df5dfb77
DL
1063 assert(!group->group_filtermode_isexcl);
1064 assert(!listcount(group->group_source_list));
12e41d03 1065
d62a17ae 1066 igmp_group_delete(group);
12e41d03
DL
1067}
1068
c5f76fad 1069void igmp_sock_free(struct gm_sock *igmp)
12e41d03 1070{
df5dfb77
DL
1071 assert(!igmp->t_igmp_read);
1072 assert(!igmp->t_igmp_query_timer);
1073 assert(!igmp->t_other_querier_timer);
12e41d03 1074
d62a17ae 1075 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
12e41d03
DL
1076}
1077
c5f76fad 1078void igmp_sock_delete(struct gm_sock *igmp)
12e41d03 1079{
d62a17ae 1080 struct pim_interface *pim_ifp;
12e41d03 1081
d62a17ae 1082 sock_close(igmp);
12e41d03 1083
d62a17ae 1084 pim_ifp = igmp->interface->info;
12e41d03 1085
18adcff1 1086 listnode_delete(pim_ifp->gm_socket_list, igmp);
12e41d03 1087
d62a17ae 1088 igmp_sock_free(igmp);
dda4d23c 1089
18adcff1 1090 if (!listcount(pim_ifp->gm_socket_list))
dda4d23c 1091 pim_igmp_if_reset(pim_ifp);
12e41d03
DL
1092}
1093
d62a17ae 1094void igmp_sock_delete_all(struct interface *ifp)
cb24fec4 1095{
d62a17ae 1096 struct pim_interface *pim_ifp;
1097 struct listnode *igmp_node, *igmp_nextnode;
c5f76fad 1098 struct gm_sock *igmp;
cb24fec4 1099
d62a17ae 1100 pim_ifp = ifp->info;
cb24fec4 1101
18adcff1 1102 for (ALL_LIST_ELEMENTS(pim_ifp->gm_socket_list, igmp_node,
29e58225 1103 igmp_nextnode, igmp)) {
d62a17ae 1104 igmp_sock_delete(igmp);
1105 }
cb24fec4
DS
1106}
1107
d8b87afe 1108static unsigned int igmp_group_hash_key(const void *arg)
6345a326 1109{
a16db099 1110 const struct gm_group *group = arg;
6345a326 1111
d62a17ae 1112 return jhash_1word(group->group_addr.s_addr, 0);
6345a326
DS
1113}
1114
74df8d6d 1115static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
6345a326 1116{
a16db099
SG
1117 const struct gm_group *g1 = (const struct gm_group *)arg1;
1118 const struct gm_group *g2 = (const struct gm_group *)arg2;
6345a326 1119
d62a17ae 1120 if (g1->group_addr.s_addr == g2->group_addr.s_addr)
74df8d6d 1121 return true;
6345a326 1122
74df8d6d 1123 return false;
6345a326
DS
1124}
1125
dda4d23c
DL
1126void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
1127{
1128 char hash_name[64];
1129
18adcff1
SG
1130 pim_ifp->gm_socket_list = list_new();
1131 pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free;
dda4d23c 1132
18adcff1
SG
1133 pim_ifp->gm_group_list = list_new();
1134 pim_ifp->gm_group_list->del = (void (*)(void *))igmp_group_free;
dda4d23c 1135
a1a4295a 1136 snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
18adcff1
SG
1137 pim_ifp->gm_group_hash = hash_create(igmp_group_hash_key,
1138 igmp_group_hash_equal, hash_name);
dda4d23c
DL
1139}
1140
1141void pim_igmp_if_reset(struct pim_interface *pim_ifp)
1142{
1143 struct listnode *grp_node, *grp_nextnode;
a16db099 1144 struct gm_group *grp;
dda4d23c 1145
18adcff1 1146 for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grp_node, grp_nextnode,
dda4d23c
DL
1147 grp)) {
1148 igmp_group_delete(grp);
1149 }
1150}
1151
1152void pim_igmp_if_fini(struct pim_interface *pim_ifp)
1153{
1154 pim_igmp_if_reset(pim_ifp);
1155
18adcff1
SG
1156 assert(pim_ifp->gm_group_list);
1157 assert(!listcount(pim_ifp->gm_group_list));
dda4d23c 1158
18adcff1
SG
1159 list_delete(&pim_ifp->gm_group_list);
1160 hash_free(pim_ifp->gm_group_hash);
dda4d23c 1161
18adcff1 1162 list_delete(&pim_ifp->gm_socket_list);
dda4d23c
DL
1163}
1164
c5f76fad
SG
1165static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
1166 struct interface *ifp, int mtrace_only)
12e41d03 1167{
d62a17ae 1168 struct pim_interface *pim_ifp;
c5f76fad 1169 struct gm_sock *igmp;
d62a17ae 1170
1171 pim_ifp = ifp->info;
1172
1173 if (PIM_DEBUG_IGMP_TRACE) {
1174 zlog_debug(
ee2bbf7c
MS
1175 "Creating IGMP socket fd=%d for address %pI4 on interface %s",
1176 fd, &ifaddr, ifp->name);
d62a17ae 1177 }
1178
1179 igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
d62a17ae 1180
d62a17ae 1181 igmp->fd = fd;
1182 igmp->interface = ifp;
1183 igmp->ifaddr = ifaddr;
9a7cee26 1184 igmp->querier_addr = ifaddr;
d62a17ae 1185 igmp->t_igmp_read = NULL;
1186 igmp->t_igmp_query_timer = NULL;
1187 igmp->t_other_querier_timer = NULL; /* no other querier present */
1188 igmp->querier_robustness_variable =
18adcff1 1189 pim_ifp->gm_default_robustness_variable;
d62a17ae 1190 igmp->sock_creation = pim_time_monotonic_sec();
1191
f2058cb4 1192 igmp_stats_init(&igmp->igmp_stats);
21313cbf 1193
f83f3966
MS
1194 if (mtrace_only) {
1195 igmp->mtrace_only = mtrace_only;
1196 return igmp;
1197 }
1198
1199 igmp->mtrace_only = false;
1200
d62a17ae 1201 /*
1202 igmp_startup_mode_on() will reset QQI:
1203
18adcff1 1204 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
d62a17ae 1205 */
1206 igmp_startup_mode_on(igmp);
1207 pim_igmp_general_query_on(igmp);
1208
1209 return igmp;
12e41d03
DL
1210}
1211
c5f76fad 1212static void igmp_read_on(struct gm_sock *igmp);
7923d317 1213
cc9f21da 1214static void pim_igmp_read(struct thread *t)
7923d317 1215{
d62a17ae 1216 uint8_t buf[10000];
c5f76fad 1217 struct gm_sock *igmp = (struct gm_sock *)THREAD_ARG(t);
023d3e4a
BG
1218 struct sockaddr_storage from;
1219 struct sockaddr_storage to;
d62a17ae 1220 socklen_t fromlen = sizeof(from);
1221 socklen_t tolen = sizeof(to);
1222 ifindex_t ifindex = -1;
d62a17ae 1223 int len;
1224
2e1cc436 1225 while (1) {
d62a17ae 1226 len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from,
1227 &fromlen, &to, &tolen, &ifindex);
1228 if (len < 0) {
1229 if (errno == EINTR)
1230 continue;
1231 if (errno == EWOULDBLOCK || errno == EAGAIN)
1232 break;
1233
1234 goto done;
1235 }
7923d317 1236 }
7923d317 1237
d62a17ae 1238done:
1239 igmp_read_on(igmp);
7923d317
DS
1240}
1241
c5f76fad 1242static void igmp_read_on(struct gm_sock *igmp)
7923d317
DS
1243{
1244
d62a17ae 1245 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
1246 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1247 igmp->fd);
1248 }
36417fcc 1249 thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
d62a17ae 1250 &igmp->t_igmp_read);
7923d317
DS
1251}
1252
c5f76fad
SG
1253struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
1254 struct in_addr ifaddr, struct interface *ifp,
1255 bool mtrace_only)
12e41d03 1256{
d62a17ae 1257 struct pim_interface *pim_ifp;
c5f76fad 1258 struct gm_sock *igmp;
d05d3f7a 1259 struct sockaddr_in sin;
d62a17ae 1260 int fd;
12e41d03 1261
d62a17ae 1262 pim_ifp = ifp->info;
12e41d03 1263
d62a17ae 1264 fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options);
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
d62a17ae 1315 if (PIM_DEBUG_IGMP_TRACE) {
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
1350 if (PIM_DEBUG_IGMP_TRACE) {
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
1365 if (PIM_DEBUG_IGMP_EVENTS) {
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)) {
6b5122a0
DS
1417 if (PIM_DEBUG_IGMP_TRACE)
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
1457 if (PIM_DEBUG_IGMP_TRACE) {
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
1530 if (PIM_DEBUG_IGMP_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}