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