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