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