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