]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmp.c
Merge pull request #10447 from ton31337/fix/json_with_whitespaces
[mirror_frr.git] / pimd / pim_igmp.c
1 /*
2 * PIM for Quagga
3 * Copyright (C) 2008 Everton da Silva Marques
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 #include <zebra.h>
21
22 #include "memory.h"
23 #include "prefix.h"
24 #include "if.h"
25 #include "hash.h"
26 #include "jhash.h"
27 #include "lib_errors.h"
28
29 #include "pimd.h"
30 #include "pim_igmp.h"
31 #include "pim_igmpv2.h"
32 #include "pim_igmpv3.h"
33 #include "pim_igmp_mtrace.h"
34 #include "pim_iface.h"
35 #include "pim_sock.h"
36 #include "pim_mroute.h"
37 #include "pim_str.h"
38 #include "pim_util.h"
39 #include "pim_time.h"
40 #include "pim_ssm.h"
41 #include "pim_tib.h"
42
43 static void group_timer_off(struct gm_group *group);
44 static void pim_igmp_general_query(struct thread *t);
45
46 void igmp_anysource_forward_start(struct pim_instance *pim,
47 struct gm_group *group)
48 {
49 struct gm_source *source;
50 struct in_addr src_addr = {.s_addr = 0};
51 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
52 assert(group->group_filtermode_isexcl);
53 assert(listcount(group->group_source_list) < 1);
54
55 source = igmp_get_source_by_addr(group, src_addr, NULL);
56 if (!source) {
57 zlog_warn("%s: Failure to create * source", __func__);
58 return;
59 }
60
61 igmp_source_forward_start(pim, source);
62 }
63
64 void igmp_anysource_forward_stop(struct gm_group *group)
65 {
66 struct gm_source *source;
67 struct in_addr star = {.s_addr = 0};
68
69 source = igmp_find_source_by_addr(group, star);
70 if (source)
71 igmp_source_forward_stop(source);
72 }
73
74 static void igmp_source_forward_reevaluate_one(struct pim_instance *pim,
75 struct gm_source *source)
76 {
77 pim_sgaddr sg;
78 struct gm_group *group = source->source_group;
79 struct pim_ifchannel *ch;
80
81 if ((source->source_addr.s_addr != INADDR_ANY) ||
82 !IGMP_SOURCE_TEST_FORWARDING(source->source_flags))
83 return;
84
85 memset(&sg, 0, sizeof(sg));
86 sg.src = source->source_addr;
87 sg.grp = group->group_addr;
88
89 ch = pim_ifchannel_find(group->interface, &sg);
90 if (pim_is_grp_ssm(pim, group->group_addr)) {
91 /* If SSM group withdraw local membership */
92 if (ch &&
93 (ch->local_ifmembership == PIM_IFMEMBERSHIP_INCLUDE)) {
94 if (PIM_DEBUG_PIM_EVENTS)
95 zlog_debug(
96 "local membership del for %pSG as G is now SSM",
97 &sg);
98 pim_ifchannel_local_membership_del(group->interface,
99 &sg);
100 }
101 } else {
102 /* If ASM group add local membership */
103 if (!ch ||
104 (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO)) {
105 if (PIM_DEBUG_PIM_EVENTS)
106 zlog_debug(
107 "local membership add for %pSG as G is now ASM",
108 &sg);
109 pim_ifchannel_local_membership_add(
110 group->interface, &sg, false /*is_vxlan*/);
111 }
112 }
113 }
114
115 void igmp_source_forward_reevaluate_all(struct pim_instance *pim)
116 {
117 struct interface *ifp;
118
119 FOR_ALL_INTERFACES (pim->vrf, ifp) {
120 struct pim_interface *pim_ifp = ifp->info;
121 struct listnode *grpnode;
122 struct gm_group *grp;
123 struct pim_ifchannel *ch, *ch_temp;
124
125 if (!pim_ifp)
126 continue;
127
128 /* scan igmp groups */
129 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
130 grp)) {
131 struct listnode *srcnode;
132 struct gm_source *src;
133
134 /* scan group sources */
135 for (ALL_LIST_ELEMENTS_RO(grp->group_source_list,
136 srcnode, src)) {
137 igmp_source_forward_reevaluate_one(pim, src);
138 } /* scan group sources */
139 } /* scan igmp groups */
140
141 RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb,
142 ch_temp) {
143 if (pim_is_grp_ssm(pim, ch->sg.grp)) {
144 if (pim_addr_is_any(ch->sg.src))
145 pim_ifchannel_delete(ch);
146 }
147 }
148 } /* scan interfaces */
149 }
150
151 void igmp_source_forward_start(struct pim_instance *pim,
152 struct gm_source *source)
153 {
154 struct gm_group *group;
155 pim_sgaddr sg;
156
157 memset(&sg, 0, sizeof(sg));
158 sg.src = source->source_addr;
159 sg.grp = source->source_group->group_addr;
160
161 if (PIM_DEBUG_IGMP_TRACE) {
162 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
163 source->source_group->interface->name,
164 IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
165 }
166
167 /* Prevent IGMP interface from installing multicast route multiple
168 times */
169 if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
170 return;
171 }
172
173 group = source->source_group;
174
175 if (tib_sg_gm_join(pim, sg, group->interface,
176 &source->source_channel_oil))
177 IGMP_SOURCE_DO_FORWARDING(source->source_flags);
178 }
179
180 /*
181 igmp_source_forward_stop: stop fowarding, but keep the source
182 igmp_source_delete: stop fowarding, and delete the source
183 */
184 void igmp_source_forward_stop(struct gm_source *source)
185 {
186 struct pim_interface *pim_oif;
187 struct gm_group *group;
188 pim_sgaddr sg;
189
190 memset(&sg, 0, sizeof(sg));
191 sg.src = source->source_addr;
192 sg.grp = source->source_group->group_addr;
193
194 if (PIM_DEBUG_IGMP_TRACE) {
195 zlog_debug("%s: (S,G)=%pSG oif=%s fwd=%d", __func__, &sg,
196 source->source_group->interface->name,
197 IGMP_SOURCE_TEST_FORWARDING(source->source_flags));
198 }
199
200 /* Prevent IGMP interface from removing multicast route multiple
201 times */
202 if (!IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
203 return;
204 }
205
206 group = source->source_group;
207 pim_oif = group->interface->info;
208
209 tib_sg_gm_prune(pim_oif->pim, sg, group->interface,
210 &source->source_channel_oil);
211 IGMP_SOURCE_DONT_FORWARDING(source->source_flags);
212 }
213
214 /* This socket is used for TXing IGMP packets only, IGMP RX happens
215 * in pim_mroute_msg()
216 */
217 static int igmp_sock_open(struct in_addr ifaddr, struct interface *ifp,
218 uint32_t pim_options)
219 {
220 int fd;
221 int join = 0;
222 struct in_addr group;
223 struct pim_interface *pim_ifp = ifp->info;
224
225 fd = pim_socket_mcast(IPPROTO_IGMP, ifaddr, ifp, 1);
226
227 if (fd < 0)
228 return -1;
229
230 if (PIM_IF_TEST_IGMP_LISTEN_ALLROUTERS(pim_options)) {
231 if (inet_aton(PIM_ALL_ROUTERS, &group)) {
232 if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex,
233 pim_ifp))
234 ++join;
235 } else {
236 zlog_warn(
237 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
238 __FILE__, __func__, fd, &ifaddr,
239 PIM_ALL_ROUTERS, errno, safe_strerror(errno));
240 }
241 }
242
243 /*
244 IGMP routers periodically send IGMP general queries to
245 AllSystems=224.0.0.1
246 IGMP routers must receive general queries for querier election.
247 */
248 if (inet_aton(PIM_ALL_SYSTEMS, &group)) {
249 if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex, pim_ifp))
250 ++join;
251 } else {
252 zlog_warn(
253 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
254 __FILE__, __func__, fd, &ifaddr,
255 PIM_ALL_SYSTEMS, errno, safe_strerror(errno));
256 }
257
258 if (inet_aton(PIM_ALL_IGMP_ROUTERS, &group)) {
259 if (!pim_socket_join(fd, group, ifaddr, ifp->ifindex,
260 pim_ifp)) {
261 ++join;
262 }
263 } else {
264 zlog_warn(
265 "%s %s: IGMP socket fd=%d interface %pI4: could not solve %s to group address: errno=%d: %s",
266 __FILE__, __func__, fd, &ifaddr,
267 PIM_ALL_IGMP_ROUTERS, errno, safe_strerror(errno));
268 }
269
270 if (!join) {
271 flog_err_sys(
272 EC_LIB_SOCKET,
273 "IGMP socket fd=%d could not join any group on interface address %pI4",
274 fd, &ifaddr);
275 close(fd);
276 fd = -1;
277 }
278
279 return fd;
280 }
281
282 #undef IGMP_SOCK_DUMP
283
284 #ifdef IGMP_SOCK_DUMP
285 static void igmp_sock_dump(array_t *igmp_sock_array)
286 {
287 int size = array_size(igmp_sock_array);
288 for (int i = 0; i < size; ++i) {
289
290 struct gm_sock *igmp = array_get(igmp_sock_array, i);
291
292 zlog_debug("%s %s: [%d/%d] igmp_addr=%pI4 fd=%d", __FILE__,
293 __func__, i, size, &igmp->ifaddr,
294 igmp->fd);
295 }
296 }
297 #endif
298
299 struct gm_sock *pim_igmp_sock_lookup_ifaddr(struct list *igmp_sock_list,
300 struct in_addr ifaddr)
301 {
302 struct listnode *sock_node;
303 struct gm_sock *igmp;
304
305 #ifdef IGMP_SOCK_DUMP
306 igmp_sock_dump(igmp_sock_list);
307 #endif
308
309 for (ALL_LIST_ELEMENTS_RO(igmp_sock_list, sock_node, igmp))
310 if (ifaddr.s_addr == igmp->ifaddr.s_addr)
311 return igmp;
312
313 return NULL;
314 }
315
316 static void pim_igmp_other_querier_expire(struct thread *t)
317 {
318 struct gm_sock *igmp;
319
320 igmp = THREAD_ARG(t);
321
322 assert(!igmp->t_igmp_query_timer);
323
324 if (PIM_DEBUG_IGMP_TRACE) {
325 char ifaddr_str[INET_ADDRSTRLEN];
326 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
327 sizeof(ifaddr_str));
328 zlog_debug("%s: Querier %s resuming", __func__, ifaddr_str);
329 }
330 /* Mark the interface address as querier address */
331 igmp->querier_addr = igmp->ifaddr;
332
333 /*
334 We are the current querier, then
335 re-start sending general queries.
336 RFC 2236 - sec 7 Other Querier
337 present timer expired (Send General
338 Query, Set Gen. Query. timer)
339 */
340 pim_igmp_general_query(t);
341 }
342
343 void pim_igmp_other_querier_timer_on(struct gm_sock *igmp)
344 {
345 long other_querier_present_interval_msec;
346 struct pim_interface *pim_ifp;
347
348 assert(igmp);
349 assert(igmp->interface);
350 assert(igmp->interface->info);
351
352 pim_ifp = igmp->interface->info;
353
354 if (igmp->t_other_querier_timer) {
355 /*
356 There is other querier present already,
357 then reset the other-querier-present timer.
358 */
359
360 if (PIM_DEBUG_IGMP_TRACE) {
361 char ifaddr_str[INET_ADDRSTRLEN];
362 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
363 sizeof(ifaddr_str));
364 zlog_debug(
365 "Querier %s resetting TIMER event for Other-Querier-Present",
366 ifaddr_str);
367 }
368 THREAD_OFF(igmp->t_other_querier_timer);
369 } else {
370 /*
371 We are the current querier, then stop sending general queries:
372 igmp->t_igmp_query_timer = NULL;
373 */
374 pim_igmp_general_query_off(igmp);
375 }
376
377 /*
378 Since this socket is starting the other-querier-present timer,
379 there should not be periodic query timer for this socket.
380 */
381 assert(!igmp->t_igmp_query_timer);
382
383 /*
384 RFC 3376: 8.5. Other Querier Present Interval
385
386 The Other Querier Present Interval is the length of time that must
387 pass before a multicast router decides that there is no longer
388 another multicast router which should be the querier. This value
389 MUST be ((the Robustness Variable) times (the Query Interval)) plus
390 (one half of one Query Response Interval).
391
392 other_querier_present_interval_msec = \
393 igmp->querier_robustness_variable * \
394 1000 * igmp->querier_query_interval + \
395 100 * (pim_ifp->query_max_response_time_dsec >> 1);
396 */
397 other_querier_present_interval_msec = PIM_IGMP_OQPI_MSEC(
398 igmp->querier_robustness_variable, igmp->querier_query_interval,
399 pim_ifp->gm_query_max_response_time_dsec);
400
401 if (PIM_DEBUG_IGMP_TRACE) {
402 char ifaddr_str[INET_ADDRSTRLEN];
403 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
404 sizeof(ifaddr_str));
405 zlog_debug(
406 "Querier %s scheduling %ld.%03ld sec TIMER event for Other-Querier-Present",
407 ifaddr_str, other_querier_present_interval_msec / 1000,
408 other_querier_present_interval_msec % 1000);
409 }
410
411 thread_add_timer_msec(router->master, pim_igmp_other_querier_expire,
412 igmp, other_querier_present_interval_msec,
413 &igmp->t_other_querier_timer);
414 }
415
416 void pim_igmp_other_querier_timer_off(struct gm_sock *igmp)
417 {
418 assert(igmp);
419
420 if (PIM_DEBUG_IGMP_TRACE) {
421 if (igmp->t_other_querier_timer) {
422 char ifaddr_str[INET_ADDRSTRLEN];
423 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
424 sizeof(ifaddr_str));
425 zlog_debug(
426 "IGMP querier %s fd=%d cancelling other-querier-present TIMER event on %s",
427 ifaddr_str, igmp->fd, igmp->interface->name);
428 }
429 }
430 THREAD_OFF(igmp->t_other_querier_timer);
431 }
432
433 int igmp_validate_checksum(char *igmp_msg, int igmp_msg_len)
434 {
435 uint16_t recv_checksum;
436 uint16_t checksum;
437
438 IGMP_GET_INT16((unsigned char *)(igmp_msg + IGMP_CHECKSUM_OFFSET),
439 recv_checksum);
440
441 /* Clear the checksum field */
442 memset(igmp_msg + IGMP_CHECKSUM_OFFSET, 0, 2);
443
444 checksum = in_cksum(igmp_msg, igmp_msg_len);
445 if (ntohs(checksum) != recv_checksum) {
446 zlog_warn("Invalid checksum received %x, calculated %x",
447 recv_checksum, ntohs(checksum));
448 return -1;
449 }
450
451 return 0;
452 }
453
454 static int igmp_recv_query(struct gm_sock *igmp, int query_version,
455 int max_resp_code, struct in_addr from,
456 const char *from_str, char *igmp_msg,
457 int igmp_msg_len)
458 {
459 struct interface *ifp;
460 struct pim_interface *pim_ifp;
461 struct in_addr group_addr;
462
463 if (igmp->mtrace_only)
464 return 0;
465
466 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
467
468 ifp = igmp->interface;
469 pim_ifp = ifp->info;
470
471 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
472 zlog_warn(
473 "Recv IGMP query v%d from %s on %s with invalid checksum",
474 query_version, from_str, ifp->name);
475 return -1;
476 }
477
478 if (!pim_if_connected_to_source(ifp, from)) {
479 if (PIM_DEBUG_IGMP_PACKETS)
480 zlog_debug("Recv IGMP query on interface: %s from a non-connected source: %s",
481 ifp->name, from_str);
482 return 0;
483 }
484
485 if (if_address_is_local(&from, AF_INET, ifp->vrf->vrf_id)) {
486 if (PIM_DEBUG_IGMP_PACKETS)
487 zlog_debug("Recv IGMP query on interface: %s from ourself %s",
488 ifp->name, from_str);
489 return 0;
490 }
491
492 /* Collecting IGMP Rx stats */
493 switch (query_version) {
494 case 1:
495 igmp->igmp_stats.query_v1++;
496 break;
497 case 2:
498 igmp->igmp_stats.query_v2++;
499 break;
500 case 3:
501 igmp->igmp_stats.query_v3++;
502 break;
503 default:
504 igmp->igmp_stats.unsupported++;
505 }
506
507 /*
508 * RFC 3376 defines some guidelines on operating in backwards
509 * compatibility with older versions of IGMP but there are some gaps in
510 * the logic:
511 *
512 * - once we drop from say version 3 to version 2 we will never go back
513 * to version 3 even if the node that TXed an IGMP v2 query upgrades
514 * to v3
515 *
516 * - The node with the lowest IP is the querier so we will only know to
517 * drop from v3 to v2 if the node that is the querier is also the one
518 * that is running igmp v2. If a non-querier only supports igmp v2
519 * we will have no way of knowing.
520 *
521 * For now we will simplify things and inform the user that they need to
522 * configure all PIM routers to use the same version of IGMP.
523 */
524 if (query_version != pim_ifp->igmp_version) {
525 zlog_warn(
526 "Recv IGMP query v%d from %s on %s but we are using v%d, please configure all PIM routers on this subnet to use the same IGMP version",
527 query_version, from_str, ifp->name,
528 pim_ifp->igmp_version);
529 return 0;
530 }
531
532 if (PIM_DEBUG_IGMP_PACKETS) {
533 char group_str[INET_ADDRSTRLEN];
534 pim_inet4_dump("<group?>", group_addr, group_str,
535 sizeof(group_str));
536 zlog_debug("Recv IGMP query v%d from %s on %s for group %s",
537 query_version, from_str, ifp->name, group_str);
538 }
539
540 /*
541 RFC 3376: 6.6.2. Querier Election
542
543 When a router receives a query with a lower IP address, it sets
544 the Other-Querier-Present timer to Other Querier Present Interval
545 and ceases to send queries on the network if it was the previously
546 elected querier.
547 */
548 if (ntohl(from.s_addr) < ntohl(igmp->ifaddr.s_addr)) {
549
550 /* As per RFC 2236 section 3:
551 * When a Querier receives a Leave Group message for a group
552 * that has group members on the reception interface, it sends
553 * [Last Member Query Count] Group-Specific Queries every [Last
554 * Member Query Interval] to the group being left. These
555 * Group-Specific Queries have their Max Response time set to
556 * [Last Member Query Interval]. If no Reports are received
557 * after the response time of the last query expires, the
558 * routers assume that the group has no local members, as above.
559 * Any Querier to non-Querier transition is ignored during this
560 * time; the same router keeps sending the Group-Specific
561 * Queries.
562 */
563 struct gm_group *group;
564
565 group = find_group_by_addr(igmp, group_addr);
566 if (group && group->t_group_query_retransmit_timer) {
567 if (PIM_DEBUG_IGMP_TRACE)
568 zlog_debug(
569 "%s: lower address query packet from %s is ignored when last member query interval timer is running",
570 ifp->name, from_str);
571 return 0;
572 }
573
574 if (PIM_DEBUG_IGMP_TRACE) {
575 char ifaddr_str[INET_ADDRSTRLEN];
576 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
577 sizeof(ifaddr_str));
578 zlog_debug(
579 "%s: local address %s (%u) lost querier election to %s (%u)",
580 ifp->name, ifaddr_str,
581 ntohl(igmp->ifaddr.s_addr), from_str,
582 ntohl(from.s_addr));
583 }
584 if (ntohl(from.s_addr) < ntohl(igmp->querier_addr.s_addr))
585 igmp->querier_addr.s_addr = from.s_addr;
586
587 pim_igmp_other_querier_timer_on(igmp);
588 }
589
590 /* IGMP version 3 is the only one where we process the RXed query */
591 if (query_version == 3) {
592 igmp_v3_recv_query(igmp, from_str, igmp_msg);
593 }
594
595 return 0;
596 }
597
598 static void on_trace(const char *label, struct interface *ifp,
599 struct in_addr from)
600 {
601 if (PIM_DEBUG_IGMP_TRACE) {
602 char from_str[INET_ADDRSTRLEN];
603 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
604 zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
605 }
606 }
607
608 static int igmp_v1_recv_report(struct gm_sock *igmp, struct in_addr from,
609 const char *from_str, char *igmp_msg,
610 int igmp_msg_len)
611 {
612 struct interface *ifp = igmp->interface;
613 struct gm_group *group;
614 struct in_addr group_addr;
615
616 on_trace(__func__, igmp->interface, from);
617
618 if (igmp->mtrace_only)
619 return 0;
620
621 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
622 zlog_warn(
623 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
624 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
625 return -1;
626 }
627
628 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
629 zlog_warn(
630 "Recv IGMP report v1 from %s on %s with invalid checksum",
631 from_str, ifp->name);
632 return -1;
633 }
634
635 /* Collecting IGMP Rx stats */
636 igmp->igmp_stats.report_v1++;
637
638 if (PIM_DEBUG_IGMP_TRACE) {
639 zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
640 }
641
642 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
643
644 if (pim_is_group_filtered(ifp->info, &group_addr))
645 return -1;
646
647 /* non-existant group is created as INCLUDE {empty} */
648 group = igmp_add_group_by_addr(igmp, group_addr);
649 if (!group) {
650 return -1;
651 }
652
653 group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
654
655 return 0;
656 }
657
658 bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
659 {
660 char *igmp_msg;
661 int igmp_msg_len;
662 int msg_type;
663 size_t ip_hlen; /* ip header length in bytes */
664
665 if (len < sizeof(*ip_hdr)) {
666 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
667 sizeof(*ip_hdr));
668 return false;
669 }
670
671 ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
672 *hlen = ip_hlen;
673
674 if (ip_hlen > len) {
675 zlog_warn(
676 "IGMP packet header claims size %zu, but we only have %zu bytes",
677 ip_hlen, len);
678 return false;
679 }
680
681 igmp_msg = (char *)ip_hdr + ip_hlen;
682 igmp_msg_len = len - ip_hlen;
683 msg_type = *igmp_msg;
684
685 if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
686 zlog_warn("IGMP message size=%d shorter than minimum=%d",
687 igmp_msg_len, PIM_IGMP_MIN_LEN);
688 return false;
689 }
690
691 if ((msg_type != PIM_IGMP_MTRACE_RESPONSE)
692 && (msg_type != PIM_IGMP_MTRACE_QUERY_REQUEST)) {
693 if (ip_hdr->ip_ttl != 1) {
694 zlog_warn(
695 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
696 ip_hdr->ip_ttl);
697 return false;
698 }
699 }
700
701 return true;
702 }
703
704 int pim_igmp_packet(struct gm_sock *igmp, char *buf, size_t len)
705 {
706 struct ip *ip_hdr = (struct ip *)buf;
707 size_t ip_hlen; /* ip header length in bytes */
708 char *igmp_msg;
709 int igmp_msg_len;
710 int msg_type;
711 char from_str[INET_ADDRSTRLEN];
712 char to_str[INET_ADDRSTRLEN];
713
714 if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
715 return -1;
716
717 igmp_msg = buf + ip_hlen;
718 igmp_msg_len = len - ip_hlen;
719 msg_type = *igmp_msg;
720
721 pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
722 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
723
724 if (PIM_DEBUG_IGMP_PACKETS) {
725 zlog_debug(
726 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
727 from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl,
728 msg_type, igmp_msg_len);
729 }
730
731 switch (msg_type) {
732 case PIM_IGMP_MEMBERSHIP_QUERY: {
733 int max_resp_code = igmp_msg[1];
734 int query_version;
735
736 /*
737 RFC 3376: 7.1. Query Version Distinctions
738 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
739 zero
740 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
741 non-zero
742 IGMPv3 Query: length >= 12 octets
743 */
744
745 if (igmp_msg_len == 8) {
746 query_version = max_resp_code ? 2 : 1;
747 } else if (igmp_msg_len >= 12) {
748 query_version = 3;
749 } else {
750 zlog_warn("Unknown IGMP query version");
751 return -1;
752 }
753
754 return igmp_recv_query(igmp, query_version, max_resp_code,
755 ip_hdr->ip_src, from_str, igmp_msg,
756 igmp_msg_len);
757 }
758
759 case PIM_IGMP_V3_MEMBERSHIP_REPORT:
760 return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
761 igmp_msg, igmp_msg_len);
762
763 case PIM_IGMP_V2_MEMBERSHIP_REPORT:
764 return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
765 igmp_msg, igmp_msg_len);
766
767 case PIM_IGMP_V1_MEMBERSHIP_REPORT:
768 return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
769 igmp_msg, igmp_msg_len);
770
771 case PIM_IGMP_V2_LEAVE_GROUP:
772 return igmp_v2_recv_leave(igmp, ip_hdr, from_str, igmp_msg,
773 igmp_msg_len);
774
775 case PIM_IGMP_MTRACE_RESPONSE:
776 return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
777 from_str, igmp_msg,
778 igmp_msg_len);
779 case PIM_IGMP_MTRACE_QUERY_REQUEST:
780 return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
781 from_str, igmp_msg,
782 igmp_msg_len);
783 }
784
785 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
786
787 /* Collecting IGMP Rx stats */
788 igmp->igmp_stats.unsupported++;
789
790 return -1;
791 }
792
793 void pim_igmp_general_query_on(struct gm_sock *igmp)
794 {
795 struct pim_interface *pim_ifp;
796 int startup_mode;
797 int query_interval;
798
799 /*
800 Since this socket is starting as querier,
801 there should not exist a timer for other-querier-present.
802 */
803 assert(!igmp->t_other_querier_timer);
804 pim_ifp = igmp->interface->info;
805 assert(pim_ifp);
806
807 /*
808 RFC 3376: 8.6. Startup Query Interval
809
810 The Startup Query Interval is the interval between General Queries
811 sent by a Querier on startup. Default: 1/4 the Query Interval.
812 The first one should be sent out immediately instead of 125/4
813 seconds from now.
814 */
815 startup_mode = igmp->startup_query_count > 0;
816 if (startup_mode) {
817 /*
818 * If this is the first time we are sending a query on a
819 * newly configured igmp interface send it out in 1 second
820 * just to give the entire world a tiny bit of time to settle
821 * else the query interval is:
822 * query_interval = pim_ifp->gm_default_query_interval >> 2;
823 */
824 if (igmp->startup_query_count ==
825 igmp->querier_robustness_variable)
826 query_interval = 1;
827 else
828 query_interval = PIM_IGMP_SQI(
829 pim_ifp->gm_default_query_interval);
830
831 --igmp->startup_query_count;
832 } else {
833 query_interval = igmp->querier_query_interval;
834 }
835
836 if (PIM_DEBUG_IGMP_TRACE) {
837 char ifaddr_str[INET_ADDRSTRLEN];
838 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
839 sizeof(ifaddr_str));
840 zlog_debug(
841 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
842 ifaddr_str, query_interval,
843 startup_mode ? "startup" : "non-startup", igmp->fd);
844 }
845 thread_add_timer(router->master, pim_igmp_general_query, igmp,
846 query_interval, &igmp->t_igmp_query_timer);
847 }
848
849 void pim_igmp_general_query_off(struct gm_sock *igmp)
850 {
851 assert(igmp);
852
853 if (PIM_DEBUG_IGMP_TRACE) {
854 if (igmp->t_igmp_query_timer) {
855 char ifaddr_str[INET_ADDRSTRLEN];
856 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
857 sizeof(ifaddr_str));
858 zlog_debug(
859 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
860 ifaddr_str, igmp->fd, igmp->interface->name);
861 }
862 }
863 THREAD_OFF(igmp->t_igmp_query_timer);
864 }
865
866 /* Issue IGMP general query */
867 static void pim_igmp_general_query(struct thread *t)
868 {
869 struct gm_sock *igmp;
870 struct in_addr dst_addr;
871 struct in_addr group_addr;
872 struct pim_interface *pim_ifp;
873 int query_buf_size;
874
875 igmp = THREAD_ARG(t);
876
877 assert(igmp->interface);
878 assert(igmp->interface->info);
879
880 pim_ifp = igmp->interface->info;
881
882 if (pim_ifp->igmp_version == 3) {
883 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
884 } else {
885 query_buf_size = IGMP_V12_MSG_SIZE;
886 }
887
888 char query_buf[query_buf_size];
889
890 /*
891 RFC3376: 4.1.12. IP Destination Addresses for Queries
892
893 In IGMPv3, General Queries are sent with an IP destination address
894 of 224.0.0.1, the all-systems multicast address. Group-Specific
895 and Group-and-Source-Specific Queries are sent with an IP
896 destination address equal to the multicast address of interest.
897 */
898
899 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
900 group_addr.s_addr = PIM_NET_INADDR_ANY;
901
902 if (PIM_DEBUG_IGMP_TRACE) {
903 char querier_str[INET_ADDRSTRLEN];
904 char dst_str[INET_ADDRSTRLEN];
905 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
906 sizeof(querier_str));
907 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
908 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
909 querier_str, dst_str, igmp->interface->name);
910 }
911
912 igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, igmp->fd,
913 igmp->interface->name, query_buf, sizeof(query_buf),
914 0 /* num_sources */, dst_addr, group_addr,
915 pim_ifp->gm_query_max_response_time_dsec,
916 1 /* s_flag: always set for general queries */,
917 igmp->querier_robustness_variable,
918 igmp->querier_query_interval);
919
920 pim_igmp_general_query_on(igmp);
921 }
922
923 static void sock_close(struct gm_sock *igmp)
924 {
925 pim_igmp_other_querier_timer_off(igmp);
926 pim_igmp_general_query_off(igmp);
927
928 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
929 if (igmp->t_igmp_read) {
930 zlog_debug(
931 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
932 &igmp->ifaddr, igmp->fd,
933 igmp->interface->name);
934 }
935 }
936 THREAD_OFF(igmp->t_igmp_read);
937
938 if (close(igmp->fd)) {
939 flog_err(
940 EC_LIB_SOCKET,
941 "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
942 &igmp->ifaddr, igmp->fd,
943 igmp->interface->name, errno, safe_strerror(errno));
944 }
945
946 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
947 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
948 &igmp->ifaddr, igmp->fd,
949 igmp->interface->name);
950 }
951 }
952
953 void igmp_startup_mode_on(struct gm_sock *igmp)
954 {
955 struct pim_interface *pim_ifp;
956
957 pim_ifp = igmp->interface->info;
958
959 /*
960 RFC 3376: 8.7. Startup Query Count
961
962 The Startup Query Count is the number of Queries sent out on
963 startup, separated by the Startup Query Interval. Default: the
964 Robustness Variable.
965 */
966 igmp->startup_query_count = igmp->querier_robustness_variable;
967
968 /*
969 Since we're (re)starting, reset QQI to default Query Interval
970 */
971 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
972 }
973
974 static void igmp_group_free(struct gm_group *group)
975 {
976 list_delete(&group->group_source_list);
977
978 XFREE(MTYPE_PIM_IGMP_GROUP, group);
979 }
980
981 static void igmp_group_count_incr(struct pim_interface *pim_ifp)
982 {
983 ++pim_ifp->pim->igmp_group_count;
984 if (pim_ifp->pim->igmp_group_count
985 == pim_ifp->pim->igmp_watermark_limit) {
986 zlog_warn(
987 "IGMP group count reached watermark limit: %u(vrf: %s)",
988 pim_ifp->pim->igmp_group_count,
989 VRF_LOGNAME(pim_ifp->pim->vrf));
990 }
991 }
992
993 static void igmp_group_count_decr(struct pim_interface *pim_ifp)
994 {
995 if (pim_ifp->pim->igmp_group_count == 0) {
996 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
997 VRF_LOGNAME(pim_ifp->pim->vrf));
998 return;
999 }
1000
1001 --pim_ifp->pim->igmp_group_count;
1002 }
1003
1004 void igmp_group_delete(struct gm_group *group)
1005 {
1006 struct listnode *src_node;
1007 struct listnode *src_nextnode;
1008 struct gm_source *src;
1009 struct pim_interface *pim_ifp = group->interface->info;
1010
1011 if (PIM_DEBUG_IGMP_TRACE) {
1012 char group_str[INET_ADDRSTRLEN];
1013 pim_inet4_dump("<group?>", group->group_addr, group_str,
1014 sizeof(group_str));
1015 zlog_debug("Deleting IGMP group %s from interface %s",
1016 group_str, group->interface->name);
1017 }
1018
1019 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
1020 src)) {
1021 igmp_source_delete(src);
1022 }
1023
1024 THREAD_OFF(group->t_group_query_retransmit_timer);
1025
1026 group_timer_off(group);
1027 igmp_group_count_decr(pim_ifp);
1028 listnode_delete(pim_ifp->gm_group_list, group);
1029 hash_release(pim_ifp->gm_group_hash, group);
1030
1031 igmp_group_free(group);
1032 }
1033
1034 void igmp_group_delete_empty_include(struct gm_group *group)
1035 {
1036 assert(!group->group_filtermode_isexcl);
1037 assert(!listcount(group->group_source_list));
1038
1039 igmp_group_delete(group);
1040 }
1041
1042 void igmp_sock_free(struct gm_sock *igmp)
1043 {
1044 assert(!igmp->t_igmp_read);
1045 assert(!igmp->t_igmp_query_timer);
1046 assert(!igmp->t_other_querier_timer);
1047
1048 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
1049 }
1050
1051 void igmp_sock_delete(struct gm_sock *igmp)
1052 {
1053 struct pim_interface *pim_ifp;
1054
1055 sock_close(igmp);
1056
1057 pim_ifp = igmp->interface->info;
1058
1059 listnode_delete(pim_ifp->gm_socket_list, igmp);
1060
1061 igmp_sock_free(igmp);
1062
1063 if (!listcount(pim_ifp->gm_socket_list))
1064 pim_igmp_if_reset(pim_ifp);
1065 }
1066
1067 void igmp_sock_delete_all(struct interface *ifp)
1068 {
1069 struct pim_interface *pim_ifp;
1070 struct listnode *igmp_node, *igmp_nextnode;
1071 struct gm_sock *igmp;
1072
1073 pim_ifp = ifp->info;
1074
1075 for (ALL_LIST_ELEMENTS(pim_ifp->gm_socket_list, igmp_node,
1076 igmp_nextnode, igmp)) {
1077 igmp_sock_delete(igmp);
1078 }
1079 }
1080
1081 static unsigned int igmp_group_hash_key(const void *arg)
1082 {
1083 const struct gm_group *group = arg;
1084
1085 return jhash_1word(group->group_addr.s_addr, 0);
1086 }
1087
1088 static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
1089 {
1090 const struct gm_group *g1 = (const struct gm_group *)arg1;
1091 const struct gm_group *g2 = (const struct gm_group *)arg2;
1092
1093 if (g1->group_addr.s_addr == g2->group_addr.s_addr)
1094 return true;
1095
1096 return false;
1097 }
1098
1099 void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
1100 {
1101 char hash_name[64];
1102
1103 pim_ifp->gm_socket_list = list_new();
1104 pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free;
1105
1106 pim_ifp->gm_group_list = list_new();
1107 pim_ifp->gm_group_list->del = (void (*)(void *))igmp_group_free;
1108
1109 snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
1110 pim_ifp->gm_group_hash = hash_create(igmp_group_hash_key,
1111 igmp_group_hash_equal, hash_name);
1112 }
1113
1114 void pim_igmp_if_reset(struct pim_interface *pim_ifp)
1115 {
1116 struct listnode *grp_node, *grp_nextnode;
1117 struct gm_group *grp;
1118
1119 for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grp_node, grp_nextnode,
1120 grp)) {
1121 igmp_group_delete(grp);
1122 }
1123 }
1124
1125 void pim_igmp_if_fini(struct pim_interface *pim_ifp)
1126 {
1127 pim_igmp_if_reset(pim_ifp);
1128
1129 assert(pim_ifp->gm_group_list);
1130 assert(!listcount(pim_ifp->gm_group_list));
1131
1132 list_delete(&pim_ifp->gm_group_list);
1133 hash_free(pim_ifp->gm_group_hash);
1134
1135 list_delete(&pim_ifp->gm_socket_list);
1136 }
1137
1138 static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
1139 struct interface *ifp, int mtrace_only)
1140 {
1141 struct pim_interface *pim_ifp;
1142 struct gm_sock *igmp;
1143
1144 pim_ifp = ifp->info;
1145
1146 if (PIM_DEBUG_IGMP_TRACE) {
1147 zlog_debug(
1148 "Creating IGMP socket fd=%d for address %pI4 on interface %s",
1149 fd, &ifaddr, ifp->name);
1150 }
1151
1152 igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
1153
1154 igmp->fd = fd;
1155 igmp->interface = ifp;
1156 igmp->ifaddr = ifaddr;
1157 igmp->querier_addr = ifaddr;
1158 igmp->t_igmp_read = NULL;
1159 igmp->t_igmp_query_timer = NULL;
1160 igmp->t_other_querier_timer = NULL; /* no other querier present */
1161 igmp->querier_robustness_variable =
1162 pim_ifp->gm_default_robustness_variable;
1163 igmp->sock_creation = pim_time_monotonic_sec();
1164
1165 igmp_stats_init(&igmp->igmp_stats);
1166
1167 if (mtrace_only) {
1168 igmp->mtrace_only = mtrace_only;
1169 return igmp;
1170 }
1171
1172 igmp->mtrace_only = false;
1173
1174 /*
1175 igmp_startup_mode_on() will reset QQI:
1176
1177 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
1178 */
1179 igmp_startup_mode_on(igmp);
1180 pim_igmp_general_query_on(igmp);
1181
1182 return igmp;
1183 }
1184
1185 static void igmp_read_on(struct gm_sock *igmp);
1186
1187 static void pim_igmp_read(struct thread *t)
1188 {
1189 uint8_t buf[10000];
1190 struct gm_sock *igmp = (struct gm_sock *)THREAD_ARG(t);
1191 struct sockaddr_storage from;
1192 struct sockaddr_storage to;
1193 socklen_t fromlen = sizeof(from);
1194 socklen_t tolen = sizeof(to);
1195 ifindex_t ifindex = -1;
1196 int len;
1197
1198 while (1) {
1199 len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from,
1200 &fromlen, &to, &tolen, &ifindex);
1201 if (len < 0) {
1202 if (errno == EINTR)
1203 continue;
1204 if (errno == EWOULDBLOCK || errno == EAGAIN)
1205 break;
1206
1207 goto done;
1208 }
1209 }
1210
1211 done:
1212 igmp_read_on(igmp);
1213 }
1214
1215 static void igmp_read_on(struct gm_sock *igmp)
1216 {
1217
1218 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
1219 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1220 igmp->fd);
1221 }
1222 thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
1223 &igmp->t_igmp_read);
1224 }
1225
1226 struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
1227 struct in_addr ifaddr, struct interface *ifp,
1228 bool mtrace_only)
1229 {
1230 struct pim_interface *pim_ifp;
1231 struct gm_sock *igmp;
1232 struct sockaddr_in sin;
1233 int fd;
1234
1235 pim_ifp = ifp->info;
1236
1237 fd = igmp_sock_open(ifaddr, ifp, pim_ifp->options);
1238 if (fd < 0) {
1239 zlog_warn("Could not open IGMP socket for %pI4 on %s",
1240 &ifaddr, ifp->name);
1241 return NULL;
1242 }
1243
1244 sin.sin_family = AF_INET;
1245 sin.sin_addr = ifaddr;
1246 sin.sin_port = 0;
1247 if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
1248 zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
1249 &ifaddr, ifp->name, strerror(errno), errno);
1250 close(fd);
1251
1252 return NULL;
1253 }
1254
1255 igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only);
1256
1257 igmp_read_on(igmp);
1258
1259 listnode_add(igmp_sock_list, igmp);
1260
1261 #ifdef IGMP_SOCK_DUMP
1262 igmp_sock_dump(igmp_sock_array);
1263 #endif
1264
1265 return igmp;
1266 }
1267
1268 /*
1269 RFC 3376: 6.5. Switching Router Filter-Modes
1270
1271 When a router's filter-mode for a group is EXCLUDE and the group
1272 timer expires, the router filter-mode for the group transitions to
1273 INCLUDE.
1274
1275 A router uses source records with running source timers as its state
1276 for the switch to a filter-mode of INCLUDE. If there are any source
1277 records with source timers greater than zero (i.e., requested to be
1278 forwarded), a router switches to filter-mode of INCLUDE using those
1279 source records. Source records whose timers are zero (from the
1280 previous EXCLUDE mode) are deleted.
1281 */
1282 static void igmp_group_timer(struct thread *t)
1283 {
1284 struct gm_group *group;
1285
1286 group = THREAD_ARG(t);
1287
1288 if (PIM_DEBUG_IGMP_TRACE) {
1289 char group_str[INET_ADDRSTRLEN];
1290 pim_inet4_dump("<group?>", group->group_addr, group_str,
1291 sizeof(group_str));
1292 zlog_debug("%s: Timer for group %s on interface %s", __func__,
1293 group_str, group->interface->name);
1294 }
1295
1296 assert(group->group_filtermode_isexcl);
1297
1298 group->group_filtermode_isexcl = 0;
1299
1300 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1301 igmp_anysource_forward_stop(group);
1302
1303 igmp_source_delete_expired(group->group_source_list);
1304
1305 assert(!group->group_filtermode_isexcl);
1306
1307 /*
1308 RFC 3376: 6.2.2. Definition of Group Timers
1309
1310 If there are no more source records for the group, delete group
1311 record.
1312 */
1313 if (listcount(group->group_source_list) < 1) {
1314 igmp_group_delete_empty_include(group);
1315 }
1316 }
1317
1318 static void group_timer_off(struct gm_group *group)
1319 {
1320 if (!group->t_group_timer)
1321 return;
1322
1323 if (PIM_DEBUG_IGMP_TRACE) {
1324 char group_str[INET_ADDRSTRLEN];
1325 pim_inet4_dump("<group?>", group->group_addr, group_str,
1326 sizeof(group_str));
1327 zlog_debug("Cancelling TIMER event for group %s on %s",
1328 group_str, group->interface->name);
1329 }
1330 THREAD_OFF(group->t_group_timer);
1331 }
1332
1333 void igmp_group_timer_on(struct gm_group *group, long interval_msec,
1334 const char *ifname)
1335 {
1336 group_timer_off(group);
1337
1338 if (PIM_DEBUG_IGMP_EVENTS) {
1339 char group_str[INET_ADDRSTRLEN];
1340 pim_inet4_dump("<group?>", group->group_addr, group_str,
1341 sizeof(group_str));
1342 zlog_debug(
1343 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1344 interval_msec / 1000, interval_msec % 1000, group_str,
1345 ifname);
1346 }
1347
1348 /*
1349 RFC 3376: 6.2.2. Definition of Group Timers
1350
1351 The group timer is only used when a group is in EXCLUDE mode and
1352 it represents the time for the *filter-mode* of the group to
1353 expire and switch to INCLUDE mode.
1354 */
1355 assert(group->group_filtermode_isexcl);
1356
1357 thread_add_timer_msec(router->master, igmp_group_timer, group,
1358 interval_msec, &group->t_group_timer);
1359 }
1360
1361 struct gm_group *find_group_by_addr(struct gm_sock *igmp,
1362 struct in_addr group_addr)
1363 {
1364 struct gm_group lookup;
1365 struct pim_interface *pim_ifp = igmp->interface->info;
1366
1367 lookup.group_addr.s_addr = group_addr.s_addr;
1368
1369 return hash_lookup(pim_ifp->gm_group_hash, &lookup);
1370 }
1371
1372 struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
1373 struct in_addr group_addr)
1374 {
1375 struct gm_group *group;
1376 struct pim_interface *pim_ifp = igmp->interface->info;
1377
1378 group = find_group_by_addr(igmp, group_addr);
1379 if (group) {
1380 return group;
1381 }
1382
1383 if (!pim_is_group_224_4(group_addr)) {
1384 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1385 __func__);
1386 return NULL;
1387 }
1388
1389 if (pim_is_group_224_0_0_0_24(group_addr)) {
1390 if (PIM_DEBUG_IGMP_TRACE)
1391 zlog_debug(
1392 "%s: Group specified %pI4 is part of 224.0.0.0/24",
1393 __func__, &group_addr);
1394 return NULL;
1395 }
1396 /*
1397 Non-existant group is created as INCLUDE {empty}:
1398
1399 RFC 3376 - 5.1. Action on Change of Interface State
1400
1401 If no interface state existed for that multicast address before
1402 the change (i.e., the change consisted of creating a new
1403 per-interface record), or if no state exists after the change
1404 (i.e., the change consisted of deleting a per-interface record),
1405 then the "non-existent" state is considered to have a filter mode
1406 of INCLUDE and an empty source list.
1407 */
1408
1409 group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
1410
1411 group->group_source_list = list_new();
1412 group->group_source_list->del = (void (*)(void *))igmp_source_free;
1413
1414 group->t_group_timer = NULL;
1415 group->t_group_query_retransmit_timer = NULL;
1416 group->group_specific_query_retransmit_count = 0;
1417 group->group_addr = group_addr;
1418 group->interface = igmp->interface;
1419 group->last_igmp_v1_report_dsec = -1;
1420 group->last_igmp_v2_report_dsec = -1;
1421 group->group_creation = pim_time_monotonic_sec();
1422 group->igmp_version = IGMP_DEFAULT_VERSION;
1423
1424 /* initialize new group as INCLUDE {empty} */
1425 group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1426
1427 listnode_add(pim_ifp->gm_group_list, group);
1428 group = hash_get(pim_ifp->gm_group_hash, group, hash_alloc_intern);
1429
1430 if (PIM_DEBUG_IGMP_TRACE) {
1431 char group_str[INET_ADDRSTRLEN];
1432 pim_inet4_dump("<group?>", group->group_addr, group_str,
1433 sizeof(group_str));
1434 zlog_debug(
1435 "Creating new IGMP group %s on socket %d interface %s",
1436 group_str, igmp->fd, igmp->interface->name);
1437 }
1438
1439 igmp_group_count_incr(pim_ifp);
1440
1441 /*
1442 RFC 3376: 6.2.2. Definition of Group Timers
1443
1444 The group timer is only used when a group is in EXCLUDE mode and
1445 it represents the time for the *filter-mode* of the group to
1446 expire and switch to INCLUDE mode.
1447 */
1448 assert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1449 assert(!group->t_group_timer); /* group timer == 0 */
1450
1451 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1452 igmp_anysource_forward_stop(group);
1453
1454 return group;
1455 }
1456
1457 void igmp_send_query(int igmp_version, struct gm_group *group, int fd,
1458 const char *ifname, char *query_buf, int query_buf_size,
1459 int num_sources, struct in_addr dst_addr,
1460 struct in_addr group_addr,
1461 int query_max_response_time_dsec, uint8_t s_flag,
1462 uint8_t querier_robustness_variable,
1463 uint16_t querier_query_interval)
1464 {
1465 if (igmp_version == 3) {
1466 igmp_v3_send_query(group, fd, ifname, query_buf, query_buf_size,
1467 num_sources, dst_addr, group_addr,
1468 query_max_response_time_dsec, s_flag,
1469 querier_robustness_variable,
1470 querier_query_interval);
1471 } else if (igmp_version == 2) {
1472 igmp_v2_send_query(group, fd, ifname, query_buf, dst_addr,
1473 group_addr, query_max_response_time_dsec);
1474 }
1475 }
1476
1477 void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
1478 {
1479 struct pim_interface *pim_ifp = ifp->info;
1480 struct listnode *sock_node = NULL;
1481 struct gm_sock *igmp = NULL;
1482 struct in_addr dst_addr;
1483 struct in_addr group_addr;
1484 int query_buf_size;
1485
1486 if (!igmp_ver)
1487 igmp_ver = 2;
1488
1489 if (igmp_ver == 3)
1490 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1491 else
1492 query_buf_size = IGMP_V12_MSG_SIZE;
1493
1494 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
1495 group_addr.s_addr = PIM_NET_INADDR_ANY;
1496
1497 if (PIM_DEBUG_IGMP_TRACE)
1498 zlog_debug("Issuing general query on request on %s", ifp->name);
1499
1500 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
1501
1502 char query_buf[query_buf_size];
1503
1504 igmp_send_query(igmp_ver, 0 /* igmp_group */, igmp->fd,
1505 igmp->interface->name, query_buf,
1506 sizeof(query_buf), 0 /* num_sources */,
1507 dst_addr, group_addr,
1508 pim_ifp->gm_query_max_response_time_dsec,
1509 1 /* s_flag: always set for general queries */,
1510 igmp->querier_robustness_variable,
1511 igmp->querier_query_interval);
1512 }
1513 }