]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmp.c
Merge pull request #5494 from opensourcerouting/mlag-module
[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
d62a17ae 481 igmp_msg = buf + ip_hlen;
482 msg_type = *igmp_msg;
483 igmp_msg_len = len - ip_hlen;
484
485 if (PIM_DEBUG_IGMP_PACKETS) {
486 zlog_debug(
c7e663d6
DS
487 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
488 from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl,
d62a17ae 489 msg_type, igmp_msg_len);
490 }
12e41d03 491
d62a17ae 492 if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
493 zlog_warn("IGMP message size=%d shorter than minimum=%d",
494 igmp_msg_len, PIM_IGMP_MIN_LEN);
495 return -1;
496 }
497
498 switch (msg_type) {
499 case PIM_IGMP_MEMBERSHIP_QUERY: {
500 int max_resp_code = igmp_msg[1];
501 int query_version;
502
503 /*
504 RFC 3376: 7.1. Query Version Distinctions
505 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
506 zero
507 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
508 non-zero
509 IGMPv3 Query: length >= 12 octets
510 */
511
512 if (igmp_msg_len == 8) {
513 query_version = max_resp_code ? 2 : 1;
514 } else if (igmp_msg_len >= 12) {
515 query_version = 3;
516 } else {
517 zlog_warn("Unknown IGMP query version");
518 return -1;
519 }
520
521 return igmp_recv_query(igmp, query_version, max_resp_code,
522 ip_hdr->ip_src, from_str, igmp_msg,
523 igmp_msg_len);
524 }
12e41d03 525
d62a17ae 526 case PIM_IGMP_V3_MEMBERSHIP_REPORT:
527 return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
528 igmp_msg, igmp_msg_len);
12e41d03 529
d62a17ae 530 case PIM_IGMP_V2_MEMBERSHIP_REPORT:
531 return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
532 igmp_msg, igmp_msg_len);
12e41d03 533
d62a17ae 534 case PIM_IGMP_V1_MEMBERSHIP_REPORT:
535 return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
536 igmp_msg, igmp_msg_len);
12e41d03 537
d62a17ae 538 case PIM_IGMP_V2_LEAVE_GROUP:
539 return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
540 igmp_msg, igmp_msg_len);
4d9ad5dc
MS
541
542 case PIM_IGMP_MTRACE_RESPONSE:
543 return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
544 from_str, igmp_msg,
545 igmp_msg_len);
4d9ad5dc
MS
546 case PIM_IGMP_MTRACE_QUERY_REQUEST:
547 return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
548 from_str, igmp_msg,
549 igmp_msg_len);
d62a17ae 550 }
12e41d03 551
d62a17ae 552 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
553
21313cbf
MS
554 /* Collecting IGMP Rx stats */
555 igmp->rx_stats.unsupported++;
556
d62a17ae 557 return -1;
12e41d03
DL
558}
559
12e41d03
DL
560void pim_igmp_general_query_on(struct igmp_sock *igmp)
561{
d62a17ae 562 struct pim_interface *pim_ifp;
563 int startup_mode;
564 int query_interval;
565
566 /*
567 Since this socket is starting as querier,
568 there should not exist a timer for other-querier-present.
569 */
570 zassert(!igmp->t_other_querier_timer);
571 pim_ifp = igmp->interface->info;
572 zassert(pim_ifp);
573
574 /*
575 RFC 3376: 8.6. Startup Query Interval
576
577 The Startup Query Interval is the interval between General Queries
578 sent by a Querier on startup. Default: 1/4 the Query Interval.
579 The first one should be sent out immediately instead of 125/4
580 seconds from now.
581 */
582 startup_mode = igmp->startup_query_count > 0;
583 if (startup_mode) {
584 /*
585 * If this is the first time we are sending a query on a
586 * newly configured igmp interface send it out in 1 second
587 * just to give the entire world a tiny bit of time to settle
588 * else the query interval is:
589 * query_interval = pim_ifp->igmp_default_query_interval >> 2;
590 */
591 if (igmp->startup_query_count
592 == igmp->querier_robustness_variable)
593 query_interval = 1;
594 else
595 query_interval = PIM_IGMP_SQI(
596 pim_ifp->igmp_default_query_interval);
597
598 --igmp->startup_query_count;
599 } else {
600 query_interval = igmp->querier_query_interval;
601 }
602
603 if (PIM_DEBUG_IGMP_TRACE) {
604 char ifaddr_str[INET_ADDRSTRLEN];
605 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
606 sizeof(ifaddr_str));
607 zlog_debug(
608 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
609 ifaddr_str, query_interval,
610 startup_mode ? "startup" : "non-startup", igmp->fd);
611 }
612 igmp->t_igmp_query_timer = NULL;
36417fcc
DS
613 thread_add_timer(router->master, pim_igmp_general_query, igmp,
614 query_interval, &igmp->t_igmp_query_timer);
12e41d03
DL
615}
616
617void pim_igmp_general_query_off(struct igmp_sock *igmp)
618{
d62a17ae 619 zassert(igmp);
620
621 if (PIM_DEBUG_IGMP_TRACE) {
622 if (igmp->t_igmp_query_timer) {
623 char ifaddr_str[INET_ADDRSTRLEN];
624 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
625 sizeof(ifaddr_str));
626 zlog_debug(
627 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
628 ifaddr_str, igmp->fd, igmp->interface->name);
629 }
630 }
631 THREAD_OFF(igmp->t_igmp_query_timer);
12e41d03
DL
632}
633
634/* Issue IGMP general query */
635static int pim_igmp_general_query(struct thread *t)
636{
d62a17ae 637 struct igmp_sock *igmp;
638 struct in_addr dst_addr;
639 struct in_addr group_addr;
640 struct pim_interface *pim_ifp;
641 int query_buf_size;
642
643 igmp = THREAD_ARG(t);
644
645 zassert(igmp->interface);
646 zassert(igmp->interface->info);
647
648 pim_ifp = igmp->interface->info;
649
650 if (pim_ifp->igmp_version == 3) {
651 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
652 } else {
653 query_buf_size = IGMP_V12_MSG_SIZE;
654 }
655
656 char query_buf[query_buf_size];
657
658 /*
659 RFC3376: 4.1.12. IP Destination Addresses for Queries
660
661 In IGMPv3, General Queries are sent with an IP destination address
662 of 224.0.0.1, the all-systems multicast address. Group-Specific
663 and Group-and-Source-Specific Queries are sent with an IP
664 destination address equal to the multicast address of interest.
665 */
666
667 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
668 group_addr.s_addr = PIM_NET_INADDR_ANY;
669
670 if (PIM_DEBUG_IGMP_TRACE) {
671 char querier_str[INET_ADDRSTRLEN];
672 char dst_str[INET_ADDRSTRLEN];
673 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
674 sizeof(querier_str));
675 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
676 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
677 querier_str, dst_str, igmp->interface->name);
678 }
679
680 igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, igmp->fd,
681 igmp->interface->name, query_buf, sizeof(query_buf),
682 0 /* num_sources */, dst_addr, group_addr,
683 pim_ifp->igmp_query_max_response_time_dsec,
684 1 /* s_flag: always set for general queries */,
685 igmp->querier_robustness_variable,
686 igmp->querier_query_interval);
687
688 pim_igmp_general_query_on(igmp);
689
690 return 0;
12e41d03
DL
691}
692
12e41d03
DL
693static void sock_close(struct igmp_sock *igmp)
694{
d62a17ae 695 pim_igmp_other_querier_timer_off(igmp);
696 pim_igmp_general_query_off(igmp);
697
698 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
699 if (igmp->t_igmp_read) {
700 zlog_debug(
701 "Cancelling READ event on IGMP socket %s fd=%d on interface %s",
702 inet_ntoa(igmp->ifaddr), igmp->fd,
703 igmp->interface->name);
704 }
705 }
706 THREAD_OFF(igmp->t_igmp_read);
707
708 if (close(igmp->fd)) {
af4c2728 709 flog_err(
450971aa 710 EC_LIB_SOCKET,
d62a17ae 711 "Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
712 inet_ntoa(igmp->ifaddr), igmp->fd,
713 igmp->interface->name, errno, safe_strerror(errno));
714 }
715
716 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
717 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
718 inet_ntoa(igmp->ifaddr), igmp->fd,
719 igmp->interface->name);
720 }
12e41d03
DL
721}
722
723void igmp_startup_mode_on(struct igmp_sock *igmp)
724{
d62a17ae 725 struct pim_interface *pim_ifp;
12e41d03 726
d62a17ae 727 pim_ifp = igmp->interface->info;
12e41d03 728
d62a17ae 729 /*
730 RFC 3376: 8.7. Startup Query Count
12e41d03 731
d62a17ae 732 The Startup Query Count is the number of Queries sent out on
733 startup, separated by the Startup Query Interval. Default: the
734 Robustness Variable.
735 */
736 igmp->startup_query_count = igmp->querier_robustness_variable;
12e41d03 737
d62a17ae 738 /*
739 Since we're (re)starting, reset QQI to default Query Interval
740 */
741 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
12e41d03
DL
742}
743
744static void igmp_group_free(struct igmp_group *group)
745{
6a154c88 746 list_delete(&group->group_source_list);
12e41d03 747
d62a17ae 748 XFREE(MTYPE_PIM_IGMP_GROUP, group);
12e41d03
DL
749}
750
4a5e6e74 751void igmp_group_delete(struct igmp_group *group)
12e41d03 752{
d62a17ae 753 struct listnode *src_node;
754 struct listnode *src_nextnode;
755 struct igmp_source *src;
756
757 if (PIM_DEBUG_IGMP_TRACE) {
758 char group_str[INET_ADDRSTRLEN];
759 pim_inet4_dump("<group?>", group->group_addr, group_str,
760 sizeof(group_str));
761 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
762 group_str, group->group_igmp_sock->fd,
763 group->group_igmp_sock->interface->name);
764 }
765
766 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
767 src)) {
768 igmp_source_delete(src);
769 }
770
771 if (group->t_group_query_retransmit_timer) {
772 THREAD_OFF(group->t_group_query_retransmit_timer);
773 }
774
775 group_timer_off(group);
776 listnode_delete(group->group_igmp_sock->igmp_group_list, group);
777 hash_release(group->group_igmp_sock->igmp_group_hash, group);
778
779 igmp_group_free(group);
12e41d03
DL
780}
781
782void igmp_group_delete_empty_include(struct igmp_group *group)
783{
d62a17ae 784 zassert(!group->group_filtermode_isexcl);
785 zassert(!listcount(group->group_source_list));
12e41d03 786
d62a17ae 787 igmp_group_delete(group);
12e41d03
DL
788}
789
790void igmp_sock_free(struct igmp_sock *igmp)
791{
d62a17ae 792 zassert(!igmp->t_igmp_read);
793 zassert(!igmp->t_igmp_query_timer);
794 zassert(!igmp->t_other_querier_timer);
795 zassert(igmp->igmp_group_list);
796 zassert(!listcount(igmp->igmp_group_list));
12e41d03 797
6a154c88 798 list_delete(&igmp->igmp_group_list);
d62a17ae 799 hash_free(igmp->igmp_group_hash);
12e41d03 800
d62a17ae 801 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
12e41d03
DL
802}
803
804void igmp_sock_delete(struct igmp_sock *igmp)
805{
d62a17ae 806 struct pim_interface *pim_ifp;
807 struct listnode *grp_node;
808 struct listnode *grp_nextnode;
809 struct igmp_group *grp;
810
811 for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode,
812 grp)) {
813 igmp_group_delete(grp);
814 }
12e41d03 815
d62a17ae 816 sock_close(igmp);
12e41d03 817
d62a17ae 818 pim_ifp = igmp->interface->info;
12e41d03 819
d62a17ae 820 listnode_delete(pim_ifp->igmp_socket_list, igmp);
12e41d03 821
d62a17ae 822 igmp_sock_free(igmp);
12e41d03
DL
823}
824
d62a17ae 825void igmp_sock_delete_all(struct interface *ifp)
cb24fec4 826{
d62a17ae 827 struct pim_interface *pim_ifp;
828 struct listnode *igmp_node, *igmp_nextnode;
829 struct igmp_sock *igmp;
cb24fec4 830
d62a17ae 831 pim_ifp = ifp->info;
cb24fec4 832
d62a17ae 833 for (ALL_LIST_ELEMENTS(pim_ifp->igmp_socket_list, igmp_node,
834 igmp_nextnode, igmp)) {
835 igmp_sock_delete(igmp);
836 }
cb24fec4
DS
837}
838
d8b87afe 839static unsigned int igmp_group_hash_key(const void *arg)
6345a326 840{
d8b87afe 841 const struct igmp_group *group = arg;
6345a326 842
d62a17ae 843 return jhash_1word(group->group_addr.s_addr, 0);
6345a326
DS
844}
845
74df8d6d 846static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
6345a326 847{
d62a17ae 848 const struct igmp_group *g1 = (const struct igmp_group *)arg1;
849 const struct igmp_group *g2 = (const struct igmp_group *)arg2;
6345a326 850
d62a17ae 851 if (g1->group_addr.s_addr == g2->group_addr.s_addr)
74df8d6d 852 return true;
6345a326 853
74df8d6d 854 return false;
6345a326
DS
855}
856
d62a17ae 857static struct igmp_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
f83f3966 858 struct interface *ifp, int mtrace_only)
12e41d03 859{
d62a17ae 860 struct pim_interface *pim_ifp;
861 struct igmp_sock *igmp;
9fb302f4 862 char hash_name[64];
d62a17ae 863
864 pim_ifp = ifp->info;
865
866 if (PIM_DEBUG_IGMP_TRACE) {
867 zlog_debug(
868 "Creating IGMP socket fd=%d for address %s on interface %s",
869 fd, inet_ntoa(ifaddr), ifp->name);
870 }
871
872 igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
d62a17ae 873
874 igmp->igmp_group_list = list_new();
d62a17ae 875 igmp->igmp_group_list->del = (void (*)(void *))igmp_group_free;
876
9fb302f4
DS
877 snprintf(hash_name, 64, "IGMP %s hash", ifp->name);
878 igmp->igmp_group_hash = hash_create(igmp_group_hash_key,
996c9314 879 igmp_group_hash_equal, hash_name);
d62a17ae 880
881 igmp->fd = fd;
882 igmp->interface = ifp;
883 igmp->ifaddr = ifaddr;
884 igmp->t_igmp_read = NULL;
885 igmp->t_igmp_query_timer = NULL;
886 igmp->t_other_querier_timer = NULL; /* no other querier present */
887 igmp->querier_robustness_variable =
888 pim_ifp->igmp_default_robustness_variable;
889 igmp->sock_creation = pim_time_monotonic_sec();
890
21313cbf
MS
891 igmp_stats_init(&igmp->rx_stats);
892
f83f3966
MS
893 if (mtrace_only) {
894 igmp->mtrace_only = mtrace_only;
895 return igmp;
896 }
897
898 igmp->mtrace_only = false;
899
d62a17ae 900 /*
901 igmp_startup_mode_on() will reset QQI:
902
903 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
904 */
905 igmp_startup_mode_on(igmp);
906 pim_igmp_general_query_on(igmp);
907
908 return igmp;
12e41d03
DL
909}
910
d62a17ae 911static void igmp_read_on(struct igmp_sock *igmp);
7923d317 912
d62a17ae 913static int pim_igmp_read(struct thread *t)
7923d317 914{
d62a17ae 915 uint8_t buf[10000];
916 struct igmp_sock *igmp = (struct igmp_sock *)THREAD_ARG(t);
917 struct sockaddr_in from;
918 struct sockaddr_in to;
919 socklen_t fromlen = sizeof(from);
920 socklen_t tolen = sizeof(to);
921 ifindex_t ifindex = -1;
d62a17ae 922 int len;
923
2e1cc436 924 while (1) {
d62a17ae 925 len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from,
926 &fromlen, &to, &tolen, &ifindex);
927 if (len < 0) {
928 if (errno == EINTR)
929 continue;
930 if (errno == EWOULDBLOCK || errno == EAGAIN)
931 break;
932
933 goto done;
934 }
7923d317 935 }
7923d317 936
d62a17ae 937done:
938 igmp_read_on(igmp);
939 return 0;
7923d317
DS
940}
941
d62a17ae 942static void igmp_read_on(struct igmp_sock *igmp)
7923d317
DS
943{
944
d62a17ae 945 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
946 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
947 igmp->fd);
948 }
949 igmp->t_igmp_read = NULL;
36417fcc 950 thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
d62a17ae 951 &igmp->t_igmp_read);
7923d317
DS
952}
953
12e41d03
DL
954struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
955 struct in_addr ifaddr,
f83f3966
MS
956 struct interface *ifp,
957 bool mtrace_only)
12e41d03 958{
d62a17ae 959 struct pim_interface *pim_ifp;
960 struct igmp_sock *igmp;
961 int fd;
12e41d03 962
d62a17ae 963 pim_ifp = ifp->info;
12e41d03 964
d62a17ae 965 fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options);
966 if (fd < 0) {
967 zlog_warn("Could not open IGMP socket for %s on %s",
968 inet_ntoa(ifaddr), ifp->name);
969 return 0;
970 }
12e41d03 971
f83f3966 972 igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only);
12e41d03 973
d62a17ae 974 igmp_read_on(igmp);
7923d317 975
d62a17ae 976 listnode_add(igmp_sock_list, igmp);
12e41d03
DL
977
978#ifdef IGMP_SOCK_DUMP
d62a17ae 979 igmp_sock_dump(igmp_sock_array);
12e41d03
DL
980#endif
981
d62a17ae 982 return igmp;
12e41d03
DL
983}
984
985/*
986 RFC 3376: 6.5. Switching Router Filter-Modes
987
988 When a router's filter-mode for a group is EXCLUDE and the group
989 timer expires, the router filter-mode for the group transitions to
990 INCLUDE.
991
992 A router uses source records with running source timers as its state
993 for the switch to a filter-mode of INCLUDE. If there are any source
994 records with source timers greater than zero (i.e., requested to be
995 forwarded), a router switches to filter-mode of INCLUDE using those
996 source records. Source records whose timers are zero (from the
997 previous EXCLUDE mode) are deleted.
998 */
999static int igmp_group_timer(struct thread *t)
1000{
d62a17ae 1001 struct igmp_group *group;
12e41d03 1002
d62a17ae 1003 group = THREAD_ARG(t);
12e41d03 1004
d62a17ae 1005 if (PIM_DEBUG_IGMP_TRACE) {
1006 char group_str[INET_ADDRSTRLEN];
1007 pim_inet4_dump("<group?>", group->group_addr, group_str,
1008 sizeof(group_str));
1009 zlog_debug("%s: Timer for group %s on interface %s",
1010 __PRETTY_FUNCTION__, group_str,
1011 group->group_igmp_sock->interface->name);
1012 }
12e41d03 1013
d62a17ae 1014 zassert(group->group_filtermode_isexcl);
12e41d03 1015
d62a17ae 1016 group->group_filtermode_isexcl = 0;
12e41d03 1017
d62a17ae 1018 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1019 igmp_anysource_forward_stop(group);
12e41d03 1020
d62a17ae 1021 igmp_source_delete_expired(group->group_source_list);
12e41d03 1022
d62a17ae 1023 zassert(!group->group_filtermode_isexcl);
12e41d03 1024
d62a17ae 1025 /*
1026 RFC 3376: 6.2.2. Definition of Group Timers
12e41d03 1027
d62a17ae 1028 If there are no more source records for the group, delete group
1029 record.
1030 */
1031 if (listcount(group->group_source_list) < 1) {
1032 igmp_group_delete_empty_include(group);
1033 }
12e41d03 1034
d62a17ae 1035 return 0;
12e41d03
DL
1036}
1037
1038static void group_timer_off(struct igmp_group *group)
1039{
d62a17ae 1040 if (!group->t_group_timer)
1041 return;
1042
1043 if (PIM_DEBUG_IGMP_TRACE) {
1044 char group_str[INET_ADDRSTRLEN];
1045 pim_inet4_dump("<group?>", group->group_addr, group_str,
1046 sizeof(group_str));
1047 zlog_debug("Cancelling TIMER event for group %s on %s",
1048 group_str, group->group_igmp_sock->interface->name);
1049 }
1050 THREAD_OFF(group->t_group_timer);
12e41d03
DL
1051}
1052
d62a17ae 1053void igmp_group_timer_on(struct igmp_group *group, long interval_msec,
1054 const char *ifname)
12e41d03 1055{
d62a17ae 1056 group_timer_off(group);
1057
1058 if (PIM_DEBUG_IGMP_EVENTS) {
1059 char group_str[INET_ADDRSTRLEN];
1060 pim_inet4_dump("<group?>", group->group_addr, group_str,
1061 sizeof(group_str));
1062 zlog_debug(
1063 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1064 interval_msec / 1000, interval_msec % 1000, group_str,
1065 ifname);
1066 }
1067
1068 /*
1069 RFC 3376: 6.2.2. Definition of Group Timers
1070
1071 The group timer is only used when a group is in EXCLUDE mode and
1072 it represents the time for the *filter-mode* of the group to
1073 expire and switch to INCLUDE mode.
1074 */
1075 zassert(group->group_filtermode_isexcl);
1076
36417fcc
DS
1077 thread_add_timer_msec(router->master, igmp_group_timer, group,
1078 interval_msec, &group->t_group_timer);
12e41d03
DL
1079}
1080
d62a17ae 1081struct igmp_group *find_group_by_addr(struct igmp_sock *igmp,
1082 struct in_addr group_addr)
12e41d03 1083{
d62a17ae 1084 struct igmp_group lookup;
12e41d03 1085
d62a17ae 1086 lookup.group_addr.s_addr = group_addr.s_addr;
12e41d03 1087
d62a17ae 1088 return hash_lookup(igmp->igmp_group_hash, &lookup);
12e41d03
DL
1089}
1090
1091struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
915c13b3 1092 struct in_addr group_addr)
12e41d03 1093{
d62a17ae 1094 struct igmp_group *group;
1095
1096 group = find_group_by_addr(igmp, group_addr);
1097 if (group) {
1098 return group;
1099 }
1100
1101 if (!pim_is_group_224_4(group_addr)) {
1102 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1103 __PRETTY_FUNCTION__);
1104 return NULL;
1105 }
1106
1107 if (pim_is_group_224_0_0_0_24(group_addr)) {
1108 zlog_warn("%s: Group specified is part of 224.0.0.0/24",
1109 __PRETTY_FUNCTION__);
1110 return NULL;
1111 }
1112 /*
1113 Non-existant group is created as INCLUDE {empty}:
1114
1115 RFC 3376 - 5.1. Action on Change of Interface State
1116
1117 If no interface state existed for that multicast address before
1118 the change (i.e., the change consisted of creating a new
1119 per-interface record), or if no state exists after the change
1120 (i.e., the change consisted of deleting a per-interface record),
1121 then the "non-existent" state is considered to have a filter mode
1122 of INCLUDE and an empty source list.
1123 */
1124
1125 group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
d62a17ae 1126
1127 group->group_source_list = list_new();
d62a17ae 1128 group->group_source_list->del = (void (*)(void *))igmp_source_free;
1129
1130 group->t_group_timer = NULL;
1131 group->t_group_query_retransmit_timer = NULL;
1132 group->group_specific_query_retransmit_count = 0;
1133 group->group_addr = group_addr;
1134 group->group_igmp_sock = igmp;
1135 group->last_igmp_v1_report_dsec = -1;
1136 group->last_igmp_v2_report_dsec = -1;
1137 group->group_creation = pim_time_monotonic_sec();
1138 group->igmp_version = IGMP_DEFAULT_VERSION;
1139
1140 /* initialize new group as INCLUDE {empty} */
1141 group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1142
1143 listnode_add(igmp->igmp_group_list, group);
1144 group = hash_get(igmp->igmp_group_hash, group, hash_alloc_intern);
1145
1146 if (PIM_DEBUG_IGMP_TRACE) {
1147 char group_str[INET_ADDRSTRLEN];
1148 pim_inet4_dump("<group?>", group->group_addr, group_str,
1149 sizeof(group_str));
1150 zlog_debug(
1151 "Creating new IGMP group %s on socket %d interface %s",
1152 group_str, igmp->fd, igmp->interface->name);
1153 }
1154
1155 /*
1156 RFC 3376: 6.2.2. Definition of Group Timers
1157
1158 The group timer is only used when a group is in EXCLUDE mode and
1159 it represents the time for the *filter-mode* of the group to
1160 expire and switch to INCLUDE mode.
1161 */
1162 zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1163 zassert(!group->t_group_timer); /* group timer == 0 */
1164
1165 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1166 igmp_anysource_forward_stop(group);
1167
1168 return group;
12e41d03 1169}
b05b72e8 1170
d62a17ae 1171void igmp_send_query(int igmp_version, struct igmp_group *group, int fd,
1172 const char *ifname, char *query_buf, int query_buf_size,
1173 int num_sources, struct in_addr dst_addr,
1174 struct in_addr group_addr,
1175 int query_max_response_time_dsec, uint8_t s_flag,
1176 uint8_t querier_robustness_variable,
1177 uint16_t querier_query_interval)
b05b72e8 1178{
d62a17ae 1179 if (igmp_version == 3) {
1180 igmp_v3_send_query(group, fd, ifname, query_buf, query_buf_size,
1181 num_sources, dst_addr, group_addr,
1182 query_max_response_time_dsec, s_flag,
1183 querier_robustness_variable,
1184 querier_query_interval);
1185 } else if (igmp_version == 2) {
1186 igmp_v2_send_query(group, fd, ifname, query_buf, dst_addr,
1187 group_addr, query_max_response_time_dsec);
1188 }
b05b72e8 1189}
6741a5bb 1190
1191void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
1192{
1193 struct pim_interface *pim_ifp = ifp->info;
1194 struct listnode *sock_node = NULL;
1195 struct igmp_sock *igmp = NULL;
1196 struct in_addr dst_addr;
1197 struct in_addr group_addr;
1198 int query_buf_size;
1199
1200 if (!igmp_ver)
1201 igmp_ver = 2;
1202
1203 if (igmp_ver == 3)
1204 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1205 else
1206 query_buf_size = IGMP_V12_MSG_SIZE;
1207
1208 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
1209 group_addr.s_addr = PIM_NET_INADDR_ANY;
1210
1211 if (PIM_DEBUG_IGMP_TRACE)
1212 zlog_debug("Issuing general query on request on %s",
1213 ifp->name);
1214
1215 for (ALL_LIST_ELEMENTS_RO(pim_ifp->igmp_socket_list, sock_node, igmp)) {
1216
1217 char query_buf[query_buf_size];
1218
1219 igmp_send_query(igmp_ver, 0 /* igmp_group */, igmp->fd,
1220 igmp->interface->name, query_buf,
1221 sizeof(query_buf), 0 /* num_sources */,
1222 dst_addr, group_addr,
1223 pim_ifp->igmp_query_max_response_time_dsec,
1224 1 /* s_flag: always set for general queries */,
1225 igmp->querier_robustness_variable,
1226 igmp->querier_query_interval);
1227 }
1228}