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