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