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