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