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