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