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