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