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