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