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