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