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