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