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