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