]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_igmp.c
pimd: Cleanup dest buffer to small
[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
431 if (ip_hdr->ip_p != PIM_IP_PROTO_IGMP) {
432 zlog_warn("IP packet protocol=%d is not IGMP=%d",
433 ip_hdr->ip_p, PIM_IP_PROTO_IGMP);
434 return -1;
435 }
436
437 if (ip_hlen < PIM_IP_HEADER_MIN_LEN) {
438 zlog_warn("IP packet header size=%zu shorter than minimum=%d",
439 ip_hlen, PIM_IP_HEADER_MIN_LEN);
440 return -1;
441 }
442 if (ip_hlen > PIM_IP_HEADER_MAX_LEN) {
443 zlog_warn("IP packet header size=%zu greater than maximum=%d",
444 ip_hlen, PIM_IP_HEADER_MAX_LEN);
445 return -1;
446 }
447
448 igmp_msg = buf + ip_hlen;
449 msg_type = *igmp_msg;
450 igmp_msg_len = len - ip_hlen;
451
452 if (PIM_DEBUG_IGMP_PACKETS) {
453 zlog_debug("Recv IGMP packet from %s to %s on %s: ttl=%d msg_type=%d msg_size=%d",
454 from_str, to_str, igmp->interface->name, ip_hdr->ip_ttl, msg_type,
455 igmp_msg_len);
456 }
457
458 if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
459 zlog_warn("IGMP message size=%d shorter than minimum=%d",
460 igmp_msg_len, PIM_IGMP_MIN_LEN);
461 return -1;
462 }
463
464 switch (msg_type) {
465 case PIM_IGMP_MEMBERSHIP_QUERY:
466 {
467 int max_resp_code = igmp_msg[1];
468 int query_version;
469
470 /*
471 RFC 3376: 7.1. Query Version Distinctions
472 IGMPv1 Query: length = 8 octets AND Max Resp Code field is zero
473 IGMPv2 Query: length = 8 octets AND Max Resp Code field is non-zero
474 IGMPv3 Query: length >= 12 octets
475 */
476
477 if (igmp_msg_len == 8) {
478 query_version = max_resp_code ? 2 : 1;
479 }
480 else if (igmp_msg_len >= 12) {
481 query_version = 3;
482 }
483 else {
484 zlog_warn("Unknown IGMP query version");
485 return -1;
486 }
487
b05b72e8 488 return igmp_recv_query(igmp, query_version, max_resp_code,
12e41d03
DL
489 ip_hdr->ip_src, from_str,
490 igmp_msg, igmp_msg_len);
491 }
492
493 case PIM_IGMP_V3_MEMBERSHIP_REPORT:
b05b72e8
DW
494 return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
495 igmp_msg, igmp_msg_len);
12e41d03
DL
496
497 case PIM_IGMP_V2_MEMBERSHIP_REPORT:
b05b72e8
DW
498 return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
499 igmp_msg, igmp_msg_len);
12e41d03
DL
500
501 case PIM_IGMP_V1_MEMBERSHIP_REPORT:
b05b72e8
DW
502 return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
503 igmp_msg, igmp_msg_len);
12e41d03
DL
504
505 case PIM_IGMP_V2_LEAVE_GROUP:
b05b72e8
DW
506 return igmp_v2_recv_leave(igmp, ip_hdr->ip_src, from_str,
507 igmp_msg, igmp_msg_len);
12e41d03
DL
508 }
509
510 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
511
512 return -1;
513}
514
515static int pim_igmp_general_query(struct thread *t);
516
517void pim_igmp_general_query_on(struct igmp_sock *igmp)
518{
519 struct pim_interface *pim_ifp;
520 int startup_mode;
521 int query_interval;
522
12e41d03
DL
523 /*
524 Since this socket is starting as querier,
525 there should not exist a timer for other-querier-present.
526 */
527 zassert(!igmp->t_other_querier_timer);
528 pim_ifp = igmp->interface->info;
529 zassert(pim_ifp);
530
531 /*
532 RFC 3376: 8.6. Startup Query Interval
533
534 The Startup Query Interval is the interval between General Queries
535 sent by a Querier on startup. Default: 1/4 the Query Interval.
8971ccb2
DS
536 The first one should be sent out immediately instead of 125/4
537 seconds from now.
12e41d03
DL
538 */
539 startup_mode = igmp->startup_query_count > 0;
540 if (startup_mode) {
8971ccb2
DS
541 /*
542 * If this is the first time we are sending a query on a
543 * newly configured igmp interface send it out in 1 second
544 * just to give the entire world a tiny bit of time to settle
545 * else the query interval is:
546 * query_interval = pim_ifp->igmp_default_query_interval >> 2;
547 */
548 if (igmp->startup_query_count == igmp->querier_robustness_variable)
549 query_interval = 1;
550 else
551 query_interval = PIM_IGMP_SQI(pim_ifp->igmp_default_query_interval);
12e41d03 552
8971ccb2 553 --igmp->startup_query_count;
12e41d03
DL
554 }
555 else {
556 query_interval = igmp->querier_query_interval;
557 }
558
559 if (PIM_DEBUG_IGMP_TRACE) {
eaa54bdb 560 char ifaddr_str[INET_ADDRSTRLEN];
12e41d03
DL
561 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
562 zlog_debug("Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
563 ifaddr_str,
564 query_interval,
565 startup_mode ? "startup" : "non-startup",
566 igmp->fd);
567 }
59ba0ac3 568 igmp->t_igmp_query_timer = NULL;
12e41d03
DL
569 THREAD_TIMER_ON(master, igmp->t_igmp_query_timer,
570 pim_igmp_general_query,
571 igmp, query_interval);
572}
573
574void pim_igmp_general_query_off(struct igmp_sock *igmp)
575{
576 zassert(igmp);
577
578 if (PIM_DEBUG_IGMP_TRACE) {
579 if (igmp->t_igmp_query_timer) {
eaa54bdb 580 char ifaddr_str[INET_ADDRSTRLEN];
12e41d03
DL
581 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str, sizeof(ifaddr_str));
582 zlog_debug("IGMP querier %s fd=%d cancelling query TIMER event on %s",
583 ifaddr_str, igmp->fd, igmp->interface->name);
584 }
585 }
586 THREAD_OFF(igmp->t_igmp_query_timer);
12e41d03
DL
587}
588
589/* Issue IGMP general query */
590static int pim_igmp_general_query(struct thread *t)
591{
12e41d03
DL
592 struct igmp_sock *igmp;
593 struct in_addr dst_addr;
594 struct in_addr group_addr;
595 struct pim_interface *pim_ifp;
b05b72e8 596 int query_buf_size;
12e41d03 597
12e41d03
DL
598 igmp = THREAD_ARG(t);
599
12e41d03
DL
600 zassert(igmp->interface);
601 zassert(igmp->interface->info);
602
603 pim_ifp = igmp->interface->info;
604
b05b72e8
DW
605 if (pim_ifp->igmp_version == 3) {
606 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
607 } else {
608 query_buf_size = IGMP_V12_MSG_SIZE;
609 }
610
611 char query_buf[query_buf_size];
612
12e41d03
DL
613 /*
614 RFC3376: 4.1.12. IP Destination Addresses for Queries
615
616 In IGMPv3, General Queries are sent with an IP destination address
617 of 224.0.0.1, the all-systems multicast address. Group-Specific
618 and Group-and-Source-Specific Queries are sent with an IP
619 destination address equal to the multicast address of interest.
620 */
621
622 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
623 group_addr.s_addr = PIM_NET_INADDR_ANY;
624
625 if (PIM_DEBUG_IGMP_TRACE) {
eaa54bdb
DW
626 char querier_str[INET_ADDRSTRLEN];
627 char dst_str[INET_ADDRSTRLEN];
12e41d03
DL
628 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
629 sizeof(querier_str));
630 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
631 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
632 querier_str, dst_str, igmp->interface->name);
633 }
634
b05b72e8
DW
635 igmp_send_query (pim_ifp->igmp_version,
636 0 /* igmp_group */,
637 igmp->fd,
638 igmp->interface->name,
639 query_buf,
640 sizeof(query_buf),
641 0 /* num_sources */,
642 dst_addr,
643 group_addr,
644 pim_ifp->igmp_query_max_response_time_dsec,
645 1 /* s_flag: always set for general queries */,
646 igmp->querier_robustness_variable,
647 igmp->querier_query_interval);
12e41d03
DL
648
649 pim_igmp_general_query_on(igmp);
650
651 return 0;
652}
653
12e41d03
DL
654static void sock_close(struct igmp_sock *igmp)
655{
656 pim_igmp_other_querier_timer_off(igmp);
657 pim_igmp_general_query_off(igmp);
658
d3a8a0f5 659 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
12e41d03
DL
660 if (igmp->t_igmp_read) {
661 zlog_debug("Cancelling READ event on IGMP socket %s fd=%d on interface %s",
662 inet_ntoa(igmp->ifaddr), igmp->fd,
663 igmp->interface->name);
664 }
665 }
666 THREAD_OFF(igmp->t_igmp_read);
12e41d03
DL
667
668 if (close(igmp->fd)) {
669 zlog_err("Failure closing IGMP socket %s fd=%d on interface %s: errno=%d: %s",
670 inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name,
671 errno, safe_strerror(errno));
672 }
673
d3a8a0f5 674 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
12e41d03
DL
675 zlog_debug("Deleted IGMP socket %s fd=%d on interface %s",
676 inet_ntoa(igmp->ifaddr), igmp->fd, igmp->interface->name);
677 }
678}
679
680void igmp_startup_mode_on(struct igmp_sock *igmp)
681{
682 struct pim_interface *pim_ifp;
683
684 pim_ifp = igmp->interface->info;
685
686 /*
687 RFC 3376: 8.7. Startup Query Count
688
689 The Startup Query Count is the number of Queries sent out on
690 startup, separated by the Startup Query Interval. Default: the
691 Robustness Variable.
692 */
693 igmp->startup_query_count = igmp->querier_robustness_variable;
694
695 /*
696 Since we're (re)starting, reset QQI to default Query Interval
697 */
698 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
699}
700
701static void igmp_group_free(struct igmp_group *group)
702{
12e41d03
DL
703 list_free(group->group_source_list);
704
705 XFREE(MTYPE_PIM_IGMP_GROUP, group);
706}
707
708static void igmp_group_delete(struct igmp_group *group)
709{
710 struct listnode *src_node;
711 struct listnode *src_nextnode;
712 struct igmp_source *src;
713
714 if (PIM_DEBUG_IGMP_TRACE) {
eaa54bdb 715 char group_str[INET_ADDRSTRLEN];
12e41d03
DL
716 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
717 zlog_debug("Deleting IGMP group %s from socket %d interface %s",
718 group_str,
719 group->group_igmp_sock->fd,
720 group->group_igmp_sock->interface->name);
721 }
722
723 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode, src)) {
724 igmp_source_delete(src);
725 }
726
727 if (group->t_group_query_retransmit_timer) {
728 THREAD_OFF(group->t_group_query_retransmit_timer);
12e41d03
DL
729 }
730
731 group_timer_off(group);
732 listnode_delete(group->group_igmp_sock->igmp_group_list, group);
733 igmp_group_free(group);
734}
735
736void igmp_group_delete_empty_include(struct igmp_group *group)
737{
738 zassert(!group->group_filtermode_isexcl);
739 zassert(!listcount(group->group_source_list));
740
741 igmp_group_delete(group);
742}
743
744void igmp_sock_free(struct igmp_sock *igmp)
745{
746 zassert(!igmp->t_igmp_read);
747 zassert(!igmp->t_igmp_query_timer);
748 zassert(!igmp->t_other_querier_timer);
749 zassert(igmp->igmp_group_list);
750 zassert(!listcount(igmp->igmp_group_list));
751
752 list_free(igmp->igmp_group_list);
753
754 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
755}
756
757void igmp_sock_delete(struct igmp_sock *igmp)
758{
759 struct pim_interface *pim_ifp;
760 struct listnode *grp_node;
761 struct listnode *grp_nextnode;
762 struct igmp_group *grp;
763
764 for (ALL_LIST_ELEMENTS(igmp->igmp_group_list, grp_node, grp_nextnode, grp)) {
765 igmp_group_delete(grp);
766 }
767
768 sock_close(igmp);
769
770 pim_ifp = igmp->interface->info;
771
772 listnode_delete(pim_ifp->igmp_socket_list, igmp);
773
774 igmp_sock_free(igmp);
775}
776
cb24fec4
DS
777void
778igmp_sock_delete_all (struct interface *ifp)
779{
780 struct pim_interface *pim_ifp;
781 struct listnode *igmp_node, *igmp_nextnode;
782 struct igmp_sock *igmp;
783
784 pim_ifp = ifp->info;
785
786 for (ALL_LIST_ELEMENTS (pim_ifp->igmp_socket_list, igmp_node,
787 igmp_nextnode, igmp))
788 {
789 igmp_sock_delete(igmp);
790 }
791}
792
12e41d03
DL
793static struct igmp_sock *igmp_sock_new(int fd,
794 struct in_addr ifaddr,
795 struct interface *ifp)
796{
797 struct pim_interface *pim_ifp;
798 struct igmp_sock *igmp;
799
800 pim_ifp = ifp->info;
801
802 if (PIM_DEBUG_IGMP_TRACE) {
803 zlog_debug("Creating IGMP socket fd=%d for address %s on interface %s",
804 fd, inet_ntoa(ifaddr), ifp->name);
805 }
806
36d9e7dc 807 igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
12e41d03 808 if (!igmp) {
36d9e7dc 809 zlog_warn("%s %s: XCALLOC() failure",
12e41d03
DL
810 __FILE__, __PRETTY_FUNCTION__);
811 return 0;
812 }
813
814 igmp->igmp_group_list = list_new();
815 if (!igmp->igmp_group_list) {
816 zlog_err("%s %s: failure: igmp_group_list = list_new()",
817 __FILE__, __PRETTY_FUNCTION__);
818 return 0;
819 }
820 igmp->igmp_group_list->del = (void (*)(void *)) igmp_group_free;
821
822 igmp->fd = fd;
823 igmp->interface = ifp;
824 igmp->ifaddr = ifaddr;
59ba0ac3
DS
825 igmp->t_igmp_read = NULL;
826 igmp->t_igmp_query_timer = NULL;
827 igmp->t_other_querier_timer = NULL; /* no other querier present */
12e41d03
DL
828 igmp->querier_robustness_variable = pim_ifp->igmp_default_robustness_variable;
829 igmp->sock_creation = pim_time_monotonic_sec();
830
831 /*
832 igmp_startup_mode_on() will reset QQI:
833
834 igmp->querier_query_interval = pim_ifp->igmp_default_query_interval;
835 */
836 igmp_startup_mode_on(igmp);
12e41d03
DL
837 pim_igmp_general_query_on(igmp);
838
839 return igmp;
840}
841
7923d317
DS
842static void igmp_read_on (struct igmp_sock *igmp);
843
844static int
845pim_igmp_read (struct thread *t)
846{
847 uint8_t buf[10000];
848 struct igmp_sock *igmp = (struct igmp_sock *)THREAD_ARG(t);
849 struct sockaddr_in from;
850 struct sockaddr_in to;
851 socklen_t fromlen = sizeof(from);
852 socklen_t tolen = sizeof(to);
853 ifindex_t ifindex = -1;
854 int cont = 1;
855 int len;
856
857 while (cont)
858 {
859 len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf),
860 &from, &fromlen,
861 &to, &tolen,
862 &ifindex);
863 if (len < 0)
864 {
865 if (errno == EINTR)
866 continue;
867 if (errno == EWOULDBLOCK || errno == EAGAIN)
868 {
869 cont = 0;
870 break;
871 }
872 goto done;
873 }
874 }
875
876 done:
877 igmp_read_on(igmp);
878 return 0;
879}
880
881static void
882igmp_read_on (struct igmp_sock *igmp)
883{
884
885 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
886 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
887 igmp->fd);
888 }
889 igmp->t_igmp_read = NULL;
890 THREAD_READ_ON(master, igmp->t_igmp_read, pim_igmp_read, igmp, igmp->fd);
891
892}
893
12e41d03
DL
894struct igmp_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
895 struct in_addr ifaddr,
896 struct interface *ifp)
897{
898 struct pim_interface *pim_ifp;
899 struct igmp_sock *igmp;
900 int fd;
901
902 pim_ifp = ifp->info;
903
b35702d0 904 fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options);
12e41d03
DL
905 if (fd < 0) {
906 zlog_warn("Could not open IGMP socket for %s on %s",
907 inet_ntoa(ifaddr), ifp->name);
908 return 0;
909 }
910
911 igmp = igmp_sock_new(fd, ifaddr, ifp);
912 if (!igmp) {
913 zlog_err("%s %s: igmp_sock_new() failure",
914 __FILE__, __PRETTY_FUNCTION__);
915 close(fd);
916 return 0;
917 }
918
7923d317
DS
919 igmp_read_on (igmp);
920
12e41d03
DL
921 listnode_add(igmp_sock_list, igmp);
922
923#ifdef IGMP_SOCK_DUMP
924 igmp_sock_dump(igmp_sock_array);
925#endif
926
927 return igmp;
928}
929
930/*
931 RFC 3376: 6.5. Switching Router Filter-Modes
932
933 When a router's filter-mode for a group is EXCLUDE and the group
934 timer expires, the router filter-mode for the group transitions to
935 INCLUDE.
936
937 A router uses source records with running source timers as its state
938 for the switch to a filter-mode of INCLUDE. If there are any source
939 records with source timers greater than zero (i.e., requested to be
940 forwarded), a router switches to filter-mode of INCLUDE using those
941 source records. Source records whose timers are zero (from the
942 previous EXCLUDE mode) are deleted.
943 */
944static int igmp_group_timer(struct thread *t)
945{
946 struct igmp_group *group;
947
12e41d03 948 group = THREAD_ARG(t);
12e41d03
DL
949
950 if (PIM_DEBUG_IGMP_TRACE) {
eaa54bdb 951 char group_str[INET_ADDRSTRLEN];
12e41d03
DL
952 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
953 zlog_debug("%s: Timer for group %s on interface %s",
954 __PRETTY_FUNCTION__,
955 group_str, group->group_igmp_sock->interface->name);
956 }
957
958 zassert(group->group_filtermode_isexcl);
959
09c02cc3 960 group->t_group_timer = NULL;
12e41d03
DL
961 group->group_filtermode_isexcl = 0;
962
963 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
964 igmp_anysource_forward_stop(group);
965
966 igmp_source_delete_expired(group->group_source_list);
967
968 zassert(!group->t_group_timer);
969 zassert(!group->group_filtermode_isexcl);
970
971 /*
972 RFC 3376: 6.2.2. Definition of Group Timers
973
974 If there are no more source records for the group, delete group
975 record.
976 */
977 if (listcount(group->group_source_list) < 1) {
978 igmp_group_delete_empty_include(group);
979 }
980
981 return 0;
982}
983
984static void group_timer_off(struct igmp_group *group)
985{
986 if (!group->t_group_timer)
987 return;
988
989 if (PIM_DEBUG_IGMP_TRACE) {
eaa54bdb 990 char group_str[INET_ADDRSTRLEN];
12e41d03
DL
991 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
992 zlog_debug("Cancelling TIMER event for group %s on %s",
993 group_str, group->group_igmp_sock->interface->name);
994 }
995
996 THREAD_OFF(group->t_group_timer);
997 zassert(!group->t_group_timer);
998}
999
1000void igmp_group_timer_on(struct igmp_group *group,
1001 long interval_msec, const char *ifname)
1002{
1003 group_timer_off(group);
1004
1005 if (PIM_DEBUG_IGMP_EVENTS) {
eaa54bdb 1006 char group_str[INET_ADDRSTRLEN];
12e41d03
DL
1007 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1008 zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1009 interval_msec / 1000,
1010 interval_msec % 1000,
1011 group_str, ifname);
1012 }
1013
1014 /*
1015 RFC 3376: 6.2.2. Definition of Group Timers
1016
1017 The group timer is only used when a group is in EXCLUDE mode and
1018 it represents the time for the *filter-mode* of the group to
1019 expire and switch to INCLUDE mode.
1020 */
1021 zassert(group->group_filtermode_isexcl);
1022
1023 THREAD_TIMER_MSEC_ON(master, group->t_group_timer,
1024 igmp_group_timer,
1025 group, interval_msec);
1026}
1027
5b1207f7
DS
1028struct igmp_group *
1029find_group_by_addr (struct igmp_sock *igmp,
1030 struct in_addr group_addr)
12e41d03
DL
1031{
1032 struct igmp_group *group;
1033 struct listnode *node;
1034
1035 for (ALL_LIST_ELEMENTS_RO(igmp->igmp_group_list, node, group))
1036 if (group_addr.s_addr == group->group_addr.s_addr)
1037 return group;
1038
1039 return 0;
1040}
1041
1042struct igmp_group *igmp_add_group_by_addr(struct igmp_sock *igmp,
915c13b3 1043 struct in_addr group_addr)
12e41d03
DL
1044{
1045 struct igmp_group *group;
1046
1047 group = find_group_by_addr(igmp, group_addr);
1048 if (group) {
1049 return group;
1050 }
1051
b815998a
DS
1052 if (!pim_is_group_224_4 (group_addr))
1053 {
1054 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1055 __PRETTY_FUNCTION__);
1056 return NULL;
1057 }
1058
06d1c450
DS
1059 if (pim_is_group_224_0_0_0_24 (group_addr))
1060 {
1061 zlog_warn("%s: Group specified is part of 224.0.0.0/24",
1062 __PRETTY_FUNCTION__);
1063 return NULL;
1064 }
12e41d03
DL
1065 /*
1066 Non-existant group is created as INCLUDE {empty}:
1067
1068 RFC 3376 - 5.1. Action on Change of Interface State
1069
1070 If no interface state existed for that multicast address before
1071 the change (i.e., the change consisted of creating a new
1072 per-interface record), or if no state exists after the change
1073 (i.e., the change consisted of deleting a per-interface record),
1074 then the "non-existent" state is considered to have a filter mode
1075 of INCLUDE and an empty source list.
1076 */
1077
36d9e7dc 1078 group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
12e41d03 1079 if (!group) {
36d9e7dc 1080 zlog_warn("%s %s: XCALLOC() failure",
12e41d03 1081 __FILE__, __PRETTY_FUNCTION__);
b815998a 1082 return NULL; /* error, not found, could not create */
12e41d03
DL
1083 }
1084
1085 group->group_source_list = list_new();
1086 if (!group->group_source_list) {
1087 zlog_warn("%s %s: list_new() failure",
1088 __FILE__, __PRETTY_FUNCTION__);
1089 XFREE(MTYPE_PIM_IGMP_GROUP, group); /* discard group */
b815998a 1090 return NULL; /* error, not found, could not initialize */
12e41d03
DL
1091 }
1092 group->group_source_list->del = (void (*)(void *)) igmp_source_free;
1093
915c13b3
DS
1094 group->t_group_timer = NULL;
1095 group->t_group_query_retransmit_timer = NULL;
12e41d03
DL
1096 group->group_specific_query_retransmit_count = 0;
1097 group->group_addr = group_addr;
1098 group->group_igmp_sock = igmp;
1099 group->last_igmp_v1_report_dsec = -1;
1100 group->last_igmp_v2_report_dsec = -1;
1101 group->group_creation = pim_time_monotonic_sec();
b05b72e8 1102 group->igmp_version = IGMP_DEFAULT_VERSION;
12e41d03
DL
1103
1104 /* initialize new group as INCLUDE {empty} */
1105 group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1106
1107 listnode_add(igmp->igmp_group_list, group);
1108
1109 if (PIM_DEBUG_IGMP_TRACE) {
eaa54bdb 1110 char group_str[INET_ADDRSTRLEN];
12e41d03
DL
1111 pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
1112 zlog_debug("Creating new IGMP group %s on socket %d interface %s",
915c13b3 1113 group_str, igmp->fd, igmp->interface->name);
12e41d03
DL
1114 }
1115
1116 /*
1117 RFC 3376: 6.2.2. Definition of Group Timers
1118
1119 The group timer is only used when a group is in EXCLUDE mode and
1120 it represents the time for the *filter-mode* of the group to
1121 expire and switch to INCLUDE mode.
1122 */
1123 zassert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1124 zassert(!group->t_group_timer); /* group timer == 0 */
1125
1126 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1127 igmp_anysource_forward_stop(group);
1128
1129 return group;
1130}
b05b72e8
DW
1131
1132void
1133igmp_send_query (int igmp_version,
1134 struct igmp_group *group,
1135 int fd,
1136 const char *ifname,
1137 char *query_buf,
1138 int query_buf_size,
1139 int num_sources,
1140 struct in_addr dst_addr,
1141 struct in_addr group_addr,
1142 int query_max_response_time_dsec,
1143 uint8_t s_flag,
1144 uint8_t querier_robustness_variable,
1145 uint16_t querier_query_interval)
1146{
1147 if (igmp_version == 3) {
1148 igmp_v3_send_query (group, fd, ifname, query_buf,
1149 query_buf_size, num_sources,
1150 dst_addr, group_addr,
1151 query_max_response_time_dsec, s_flag,
1152 querier_robustness_variable,
1153 querier_query_interval);
1154 } else if (igmp_version == 2) {
1155 igmp_v2_send_query (group, fd, ifname, query_buf,
1156 dst_addr, group_addr,
1157 query_max_response_time_dsec);
1158 }
1159}