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