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