]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_igmp.c
pimd: Modifying member of pim_instance to accomodate IPv6 changes
[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 const struct gm_group *group;
589 const struct listnode *grpnode;
590
591 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_group_list, grpnode,
592 group)) {
593 if (!group->t_group_query_retransmit_timer)
594 continue;
595
596 if (PIM_DEBUG_IGMP_TRACE)
597 zlog_debug(
598 "%s: lower address query packet from %s is ignored when last member query interval timer is running",
599 ifp->name, from_str);
600 return 0;
601 }
602
603 if (PIM_DEBUG_IGMP_TRACE) {
604 char ifaddr_str[INET_ADDRSTRLEN];
605 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
606 sizeof(ifaddr_str));
607 zlog_debug(
608 "%s: local address %s (%u) lost querier election to %s (%u)",
609 ifp->name, ifaddr_str,
610 ntohl(igmp->ifaddr.s_addr), from_str,
611 ntohl(from.s_addr));
612 }
613 if (ntohl(from.s_addr) < ntohl(igmp->querier_addr.s_addr))
614 igmp->querier_addr.s_addr = from.s_addr;
615
616 pim_igmp_other_querier_timer_on(igmp);
617 }
618
619 /* IGMP version 3 is the only one where we process the RXed query */
620 if (query_version == 3) {
621 igmp_v3_recv_query(igmp, from_str, igmp_msg);
622 }
623
624 return 0;
625 }
626
627 static void on_trace(const char *label, struct interface *ifp,
628 struct in_addr from)
629 {
630 if (PIM_DEBUG_IGMP_TRACE) {
631 char from_str[INET_ADDRSTRLEN];
632 pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
633 zlog_debug("%s: from %s on %s", label, from_str, ifp->name);
634 }
635 }
636
637 static int igmp_v1_recv_report(struct gm_sock *igmp, struct in_addr from,
638 const char *from_str, char *igmp_msg,
639 int igmp_msg_len)
640 {
641 struct interface *ifp = igmp->interface;
642 struct gm_group *group;
643 struct in_addr group_addr;
644
645 on_trace(__func__, igmp->interface, from);
646
647 if (igmp->mtrace_only)
648 return 0;
649
650 if (igmp_msg_len != IGMP_V12_MSG_SIZE) {
651 zlog_warn(
652 "Recv IGMP report v1 from %s on %s: size=%d other than correct=%d",
653 from_str, ifp->name, igmp_msg_len, IGMP_V12_MSG_SIZE);
654 return -1;
655 }
656
657 if (igmp_validate_checksum(igmp_msg, igmp_msg_len) == -1) {
658 zlog_warn(
659 "Recv IGMP report v1 from %s on %s with invalid checksum",
660 from_str, ifp->name);
661 return -1;
662 }
663
664 /* Collecting IGMP Rx stats */
665 igmp->igmp_stats.report_v1++;
666
667 if (PIM_DEBUG_IGMP_TRACE) {
668 zlog_warn("%s %s: FIXME WRITEME", __FILE__, __func__);
669 }
670
671 memcpy(&group_addr, igmp_msg + 4, sizeof(struct in_addr));
672
673 if (pim_is_group_filtered(ifp->info, &group_addr))
674 return -1;
675
676 /* non-existent group is created as INCLUDE {empty} */
677 group = igmp_add_group_by_addr(igmp, group_addr);
678 if (!group) {
679 return -1;
680 }
681
682 group->last_igmp_v1_report_dsec = pim_time_monotonic_dsec();
683
684 return 0;
685 }
686
687 bool pim_igmp_verify_header(struct ip *ip_hdr, size_t len, size_t *hlen)
688 {
689 char *igmp_msg;
690 int igmp_msg_len;
691 int msg_type;
692 size_t ip_hlen; /* ip header length in bytes */
693
694 if (len < sizeof(*ip_hdr)) {
695 zlog_warn("IGMP packet size=%zu shorter than minimum=%zu", len,
696 sizeof(*ip_hdr));
697 return false;
698 }
699
700 ip_hlen = ip_hdr->ip_hl << 2; /* ip_hl gives length in 4-byte words */
701 *hlen = ip_hlen;
702
703 if (ip_hlen > len) {
704 zlog_warn(
705 "IGMP packet header claims size %zu, but we only have %zu bytes",
706 ip_hlen, len);
707 return false;
708 }
709
710 igmp_msg = (char *)ip_hdr + ip_hlen;
711 igmp_msg_len = len - ip_hlen;
712 msg_type = *igmp_msg;
713
714 if (igmp_msg_len < PIM_IGMP_MIN_LEN) {
715 zlog_warn("IGMP message size=%d shorter than minimum=%d",
716 igmp_msg_len, PIM_IGMP_MIN_LEN);
717 return false;
718 }
719
720 if ((msg_type != PIM_IGMP_MTRACE_RESPONSE)
721 && (msg_type != PIM_IGMP_MTRACE_QUERY_REQUEST)) {
722 if (ip_hdr->ip_ttl != 1) {
723 zlog_warn(
724 "Recv IGMP packet with invalid ttl=%u, discarding the packet",
725 ip_hdr->ip_ttl);
726 return false;
727 }
728 }
729
730 return true;
731 }
732
733 int pim_igmp_packet(struct gm_sock *igmp, char *buf, size_t len)
734 {
735 struct ip *ip_hdr = (struct ip *)buf;
736 size_t ip_hlen; /* ip header length in bytes */
737 char *igmp_msg;
738 int igmp_msg_len;
739 int msg_type;
740 char from_str[INET_ADDRSTRLEN];
741 char to_str[INET_ADDRSTRLEN];
742
743 if (!pim_igmp_verify_header(ip_hdr, len, &ip_hlen))
744 return -1;
745
746 igmp_msg = buf + ip_hlen;
747 igmp_msg_len = len - ip_hlen;
748 msg_type = *igmp_msg;
749
750 pim_inet4_dump("<src?>", ip_hdr->ip_src, from_str, sizeof(from_str));
751 pim_inet4_dump("<dst?>", ip_hdr->ip_dst, to_str, sizeof(to_str));
752
753 if (PIM_DEBUG_IGMP_PACKETS) {
754 zlog_debug(
755 "Recv IGMP packet from %s to %s on %s: size=%zu ttl=%d msg_type=%d msg_size=%d",
756 from_str, to_str, igmp->interface->name, len, ip_hdr->ip_ttl,
757 msg_type, igmp_msg_len);
758 }
759
760 switch (msg_type) {
761 case PIM_IGMP_MEMBERSHIP_QUERY: {
762 int max_resp_code = igmp_msg[1];
763 int query_version;
764
765 /*
766 RFC 3376: 7.1. Query Version Distinctions
767 IGMPv1 Query: length = 8 octets AND Max Resp Code field is
768 zero
769 IGMPv2 Query: length = 8 octets AND Max Resp Code field is
770 non-zero
771 IGMPv3 Query: length >= 12 octets
772 */
773
774 if (igmp_msg_len == 8) {
775 query_version = max_resp_code ? 2 : 1;
776 } else if (igmp_msg_len >= 12) {
777 query_version = 3;
778 } else {
779 zlog_warn("Unknown IGMP query version");
780 return -1;
781 }
782
783 return igmp_recv_query(igmp, query_version, max_resp_code,
784 ip_hdr->ip_src, from_str, igmp_msg,
785 igmp_msg_len);
786 }
787
788 case PIM_IGMP_V3_MEMBERSHIP_REPORT:
789 return igmp_v3_recv_report(igmp, ip_hdr->ip_src, from_str,
790 igmp_msg, igmp_msg_len);
791
792 case PIM_IGMP_V2_MEMBERSHIP_REPORT:
793 return igmp_v2_recv_report(igmp, ip_hdr->ip_src, from_str,
794 igmp_msg, igmp_msg_len);
795
796 case PIM_IGMP_V1_MEMBERSHIP_REPORT:
797 return igmp_v1_recv_report(igmp, ip_hdr->ip_src, from_str,
798 igmp_msg, igmp_msg_len);
799
800 case PIM_IGMP_V2_LEAVE_GROUP:
801 return igmp_v2_recv_leave(igmp, ip_hdr, from_str, igmp_msg,
802 igmp_msg_len);
803
804 case PIM_IGMP_MTRACE_RESPONSE:
805 return igmp_mtrace_recv_response(igmp, ip_hdr, ip_hdr->ip_src,
806 from_str, igmp_msg,
807 igmp_msg_len);
808 case PIM_IGMP_MTRACE_QUERY_REQUEST:
809 return igmp_mtrace_recv_qry_req(igmp, ip_hdr, ip_hdr->ip_src,
810 from_str, igmp_msg,
811 igmp_msg_len);
812 }
813
814 zlog_warn("Ignoring unsupported IGMP message type: %d", msg_type);
815
816 /* Collecting IGMP Rx stats */
817 igmp->igmp_stats.unsupported++;
818
819 return -1;
820 }
821
822 void pim_igmp_general_query_on(struct gm_sock *igmp)
823 {
824 struct pim_interface *pim_ifp;
825 int startup_mode;
826 int query_interval;
827
828 /*
829 Since this socket is starting as querier,
830 there should not exist a timer for other-querier-present.
831 */
832 assert(!igmp->t_other_querier_timer);
833 pim_ifp = igmp->interface->info;
834 assert(pim_ifp);
835
836 /*
837 RFC 3376: 8.6. Startup Query Interval
838
839 The Startup Query Interval is the interval between General Queries
840 sent by a Querier on startup. Default: 1/4 the Query Interval.
841 The first one should be sent out immediately instead of 125/4
842 seconds from now.
843 */
844 startup_mode = igmp->startup_query_count > 0;
845 if (startup_mode) {
846 /*
847 * If this is the first time we are sending a query on a
848 * newly configured igmp interface send it out in 1 second
849 * just to give the entire world a tiny bit of time to settle
850 * else the query interval is:
851 * query_interval = pim_ifp->gm_default_query_interval >> 2;
852 */
853 if (igmp->startup_query_count ==
854 igmp->querier_robustness_variable)
855 query_interval = 1;
856 else
857 query_interval = PIM_IGMP_SQI(
858 pim_ifp->gm_default_query_interval);
859
860 --igmp->startup_query_count;
861 } else {
862 query_interval = igmp->querier_query_interval;
863 }
864
865 if (PIM_DEBUG_IGMP_TRACE) {
866 char ifaddr_str[INET_ADDRSTRLEN];
867 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
868 sizeof(ifaddr_str));
869 zlog_debug(
870 "Querier %s scheduling %d-second (%s) TIMER event for IGMP query on fd=%d",
871 ifaddr_str, query_interval,
872 startup_mode ? "startup" : "non-startup", igmp->fd);
873 }
874 thread_add_timer(router->master, pim_igmp_general_query, igmp,
875 query_interval, &igmp->t_igmp_query_timer);
876 }
877
878 void pim_igmp_general_query_off(struct gm_sock *igmp)
879 {
880 assert(igmp);
881
882 if (PIM_DEBUG_IGMP_TRACE) {
883 if (igmp->t_igmp_query_timer) {
884 char ifaddr_str[INET_ADDRSTRLEN];
885 pim_inet4_dump("<ifaddr?>", igmp->ifaddr, ifaddr_str,
886 sizeof(ifaddr_str));
887 zlog_debug(
888 "IGMP querier %s fd=%d cancelling query TIMER event on %s",
889 ifaddr_str, igmp->fd, igmp->interface->name);
890 }
891 }
892 THREAD_OFF(igmp->t_igmp_query_timer);
893 }
894
895 /* Issue IGMP general query */
896 static void pim_igmp_general_query(struct thread *t)
897 {
898 struct gm_sock *igmp;
899 struct in_addr dst_addr;
900 struct in_addr group_addr;
901 struct pim_interface *pim_ifp;
902 int query_buf_size;
903
904 igmp = THREAD_ARG(t);
905
906 assert(igmp->interface);
907 assert(igmp->interface->info);
908
909 pim_ifp = igmp->interface->info;
910
911 if (pim_ifp->igmp_version == 3) {
912 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
913 } else {
914 query_buf_size = IGMP_V12_MSG_SIZE;
915 }
916
917 char query_buf[query_buf_size];
918
919 /*
920 RFC3376: 4.1.12. IP Destination Addresses for Queries
921
922 In IGMPv3, General Queries are sent with an IP destination address
923 of 224.0.0.1, the all-systems multicast address. Group-Specific
924 and Group-and-Source-Specific Queries are sent with an IP
925 destination address equal to the multicast address of interest.
926 */
927
928 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
929 group_addr.s_addr = PIM_NET_INADDR_ANY;
930
931 if (PIM_DEBUG_IGMP_TRACE) {
932 char querier_str[INET_ADDRSTRLEN];
933 char dst_str[INET_ADDRSTRLEN];
934 pim_inet4_dump("<querier?>", igmp->ifaddr, querier_str,
935 sizeof(querier_str));
936 pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
937 zlog_debug("Querier %s issuing IGMP general query to %s on %s",
938 querier_str, dst_str, igmp->interface->name);
939 }
940
941 igmp_send_query(pim_ifp->igmp_version, 0 /* igmp_group */, query_buf,
942 sizeof(query_buf), 0 /* num_sources */, dst_addr,
943 group_addr, pim_ifp->gm_query_max_response_time_dsec,
944 1 /* s_flag: always set for general queries */, igmp);
945
946 pim_igmp_general_query_on(igmp);
947 }
948
949 static void sock_close(struct gm_sock *igmp)
950 {
951 pim_igmp_other_querier_timer_off(igmp);
952 pim_igmp_general_query_off(igmp);
953
954 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
955 if (igmp->t_igmp_read) {
956 zlog_debug(
957 "Cancelling READ event on IGMP socket %pI4 fd=%d on interface %s",
958 &igmp->ifaddr, igmp->fd,
959 igmp->interface->name);
960 }
961 }
962 THREAD_OFF(igmp->t_igmp_read);
963
964 if (close(igmp->fd)) {
965 flog_err(
966 EC_LIB_SOCKET,
967 "Failure closing IGMP socket %pI4 fd=%d on interface %s: errno=%d: %s",
968 &igmp->ifaddr, igmp->fd,
969 igmp->interface->name, errno, safe_strerror(errno));
970 }
971
972 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
973 zlog_debug("Deleted IGMP socket %pI4 fd=%d on interface %s",
974 &igmp->ifaddr, igmp->fd,
975 igmp->interface->name);
976 }
977 }
978
979 void igmp_startup_mode_on(struct gm_sock *igmp)
980 {
981 struct pim_interface *pim_ifp;
982
983 pim_ifp = igmp->interface->info;
984
985 /*
986 RFC 3376: 8.7. Startup Query Count
987
988 The Startup Query Count is the number of Queries sent out on
989 startup, separated by the Startup Query Interval. Default: the
990 Robustness Variable.
991 */
992 igmp->startup_query_count = igmp->querier_robustness_variable;
993
994 /*
995 Since we're (re)starting, reset QQI to default Query Interval
996 */
997 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
998 }
999
1000 static void igmp_group_free(struct gm_group *group)
1001 {
1002 list_delete(&group->group_source_list);
1003
1004 XFREE(MTYPE_PIM_IGMP_GROUP, group);
1005 }
1006
1007 static void igmp_group_count_incr(struct pim_interface *pim_ifp)
1008 {
1009 uint32_t group_count = listcount(pim_ifp->gm_group_list);
1010
1011 ++pim_ifp->pim->gm_group_count;
1012 if (pim_ifp->pim->gm_group_count == pim_ifp->pim->gm_watermark_limit) {
1013 zlog_warn(
1014 "IGMP group count reached watermark limit: %u(vrf: %s)",
1015 pim_ifp->pim->gm_group_count,
1016 VRF_LOGNAME(pim_ifp->pim->vrf));
1017 }
1018
1019 if (pim_ifp->igmp_peak_group_count < group_count)
1020 pim_ifp->igmp_peak_group_count = group_count;
1021 }
1022
1023 static void igmp_group_count_decr(struct pim_interface *pim_ifp)
1024 {
1025 if (pim_ifp->pim->gm_group_count == 0) {
1026 zlog_warn("Cannot decrement igmp group count below 0(vrf: %s)",
1027 VRF_LOGNAME(pim_ifp->pim->vrf));
1028 return;
1029 }
1030
1031 --pim_ifp->pim->gm_group_count;
1032 }
1033
1034 void igmp_group_delete(struct gm_group *group)
1035 {
1036 struct listnode *src_node;
1037 struct listnode *src_nextnode;
1038 struct gm_source *src;
1039 struct pim_interface *pim_ifp = group->interface->info;
1040
1041 if (PIM_DEBUG_IGMP_TRACE) {
1042 char group_str[INET_ADDRSTRLEN];
1043 pim_inet4_dump("<group?>", group->group_addr, group_str,
1044 sizeof(group_str));
1045 zlog_debug("Deleting IGMP group %s from interface %s",
1046 group_str, group->interface->name);
1047 }
1048
1049 for (ALL_LIST_ELEMENTS(group->group_source_list, src_node, src_nextnode,
1050 src)) {
1051 igmp_source_delete(src);
1052 }
1053
1054 THREAD_OFF(group->t_group_query_retransmit_timer);
1055
1056 group_timer_off(group);
1057 igmp_group_count_decr(pim_ifp);
1058 listnode_delete(pim_ifp->gm_group_list, group);
1059 hash_release(pim_ifp->gm_group_hash, group);
1060
1061 igmp_group_free(group);
1062 }
1063
1064 void igmp_group_delete_empty_include(struct gm_group *group)
1065 {
1066 assert(!group->group_filtermode_isexcl);
1067 assert(!listcount(group->group_source_list));
1068
1069 igmp_group_delete(group);
1070 }
1071
1072 void igmp_sock_free(struct gm_sock *igmp)
1073 {
1074 assert(!igmp->t_igmp_read);
1075 assert(!igmp->t_igmp_query_timer);
1076 assert(!igmp->t_other_querier_timer);
1077
1078 XFREE(MTYPE_PIM_IGMP_SOCKET, igmp);
1079 }
1080
1081 void igmp_sock_delete(struct gm_sock *igmp)
1082 {
1083 struct pim_interface *pim_ifp;
1084
1085 sock_close(igmp);
1086
1087 pim_ifp = igmp->interface->info;
1088
1089 listnode_delete(pim_ifp->gm_socket_list, igmp);
1090
1091 igmp_sock_free(igmp);
1092
1093 if (!listcount(pim_ifp->gm_socket_list))
1094 pim_igmp_if_reset(pim_ifp);
1095 }
1096
1097 void igmp_sock_delete_all(struct interface *ifp)
1098 {
1099 struct pim_interface *pim_ifp;
1100 struct listnode *igmp_node, *igmp_nextnode;
1101 struct gm_sock *igmp;
1102
1103 pim_ifp = ifp->info;
1104
1105 for (ALL_LIST_ELEMENTS(pim_ifp->gm_socket_list, igmp_node,
1106 igmp_nextnode, igmp)) {
1107 igmp_sock_delete(igmp);
1108 }
1109 }
1110
1111 static unsigned int igmp_group_hash_key(const void *arg)
1112 {
1113 const struct gm_group *group = arg;
1114
1115 return jhash_1word(group->group_addr.s_addr, 0);
1116 }
1117
1118 static bool igmp_group_hash_equal(const void *arg1, const void *arg2)
1119 {
1120 const struct gm_group *g1 = (const struct gm_group *)arg1;
1121 const struct gm_group *g2 = (const struct gm_group *)arg2;
1122
1123 if (g1->group_addr.s_addr == g2->group_addr.s_addr)
1124 return true;
1125
1126 return false;
1127 }
1128
1129 void pim_igmp_if_init(struct pim_interface *pim_ifp, struct interface *ifp)
1130 {
1131 char hash_name[64];
1132
1133 pim_ifp->gm_socket_list = list_new();
1134 pim_ifp->gm_socket_list->del = (void (*)(void *))igmp_sock_free;
1135
1136 pim_ifp->gm_group_list = list_new();
1137 pim_ifp->gm_group_list->del = (void (*)(void *))igmp_group_free;
1138
1139 snprintf(hash_name, sizeof(hash_name), "IGMP %s hash", ifp->name);
1140 pim_ifp->gm_group_hash = hash_create(igmp_group_hash_key,
1141 igmp_group_hash_equal, hash_name);
1142 }
1143
1144 void pim_igmp_if_reset(struct pim_interface *pim_ifp)
1145 {
1146 struct listnode *grp_node, *grp_nextnode;
1147 struct gm_group *grp;
1148
1149 for (ALL_LIST_ELEMENTS(pim_ifp->gm_group_list, grp_node, grp_nextnode,
1150 grp)) {
1151 igmp_group_delete(grp);
1152 }
1153 }
1154
1155 void pim_igmp_if_fini(struct pim_interface *pim_ifp)
1156 {
1157 pim_igmp_if_reset(pim_ifp);
1158
1159 assert(pim_ifp->gm_group_list);
1160 assert(!listcount(pim_ifp->gm_group_list));
1161
1162 list_delete(&pim_ifp->gm_group_list);
1163 hash_free(pim_ifp->gm_group_hash);
1164
1165 list_delete(&pim_ifp->gm_socket_list);
1166 }
1167
1168 static struct gm_sock *igmp_sock_new(int fd, struct in_addr ifaddr,
1169 struct interface *ifp, int mtrace_only)
1170 {
1171 struct pim_interface *pim_ifp;
1172 struct gm_sock *igmp;
1173
1174 pim_ifp = ifp->info;
1175
1176 if (PIM_DEBUG_IGMP_TRACE) {
1177 zlog_debug(
1178 "Creating IGMP socket fd=%d for address %pI4 on interface %s",
1179 fd, &ifaddr, ifp->name);
1180 }
1181
1182 igmp = XCALLOC(MTYPE_PIM_IGMP_SOCKET, sizeof(*igmp));
1183
1184 igmp->fd = fd;
1185 igmp->interface = ifp;
1186 igmp->ifaddr = ifaddr;
1187 igmp->querier_addr = ifaddr;
1188 igmp->t_igmp_read = NULL;
1189 igmp->t_igmp_query_timer = NULL;
1190 igmp->t_other_querier_timer = NULL; /* no other querier present */
1191 igmp->querier_robustness_variable =
1192 pim_ifp->gm_default_robustness_variable;
1193 igmp->sock_creation = pim_time_monotonic_sec();
1194
1195 igmp_stats_init(&igmp->igmp_stats);
1196
1197 if (mtrace_only) {
1198 igmp->mtrace_only = mtrace_only;
1199 return igmp;
1200 }
1201
1202 igmp->mtrace_only = false;
1203
1204 /*
1205 igmp_startup_mode_on() will reset QQI:
1206
1207 igmp->querier_query_interval = pim_ifp->gm_default_query_interval;
1208 */
1209 igmp_startup_mode_on(igmp);
1210 pim_igmp_general_query_on(igmp);
1211
1212 return igmp;
1213 }
1214
1215 static void igmp_read_on(struct gm_sock *igmp);
1216
1217 static void pim_igmp_read(struct thread *t)
1218 {
1219 uint8_t buf[10000];
1220 struct gm_sock *igmp = (struct gm_sock *)THREAD_ARG(t);
1221 struct sockaddr_storage from;
1222 struct sockaddr_storage to;
1223 socklen_t fromlen = sizeof(from);
1224 socklen_t tolen = sizeof(to);
1225 ifindex_t ifindex = -1;
1226 int len;
1227
1228 while (1) {
1229 len = pim_socket_recvfromto(igmp->fd, buf, sizeof(buf), &from,
1230 &fromlen, &to, &tolen, &ifindex);
1231 if (len < 0) {
1232 if (errno == EINTR)
1233 continue;
1234 if (errno == EWOULDBLOCK || errno == EAGAIN)
1235 break;
1236
1237 goto done;
1238 }
1239 }
1240
1241 done:
1242 igmp_read_on(igmp);
1243 }
1244
1245 static void igmp_read_on(struct gm_sock *igmp)
1246 {
1247
1248 if (PIM_DEBUG_IGMP_TRACE_DETAIL) {
1249 zlog_debug("Scheduling READ event on IGMP socket fd=%d",
1250 igmp->fd);
1251 }
1252 thread_add_read(router->master, pim_igmp_read, igmp, igmp->fd,
1253 &igmp->t_igmp_read);
1254 }
1255
1256 struct gm_sock *pim_igmp_sock_add(struct list *igmp_sock_list,
1257 struct in_addr ifaddr, struct interface *ifp,
1258 bool mtrace_only)
1259 {
1260 struct gm_sock *igmp;
1261 struct sockaddr_in sin;
1262 int fd;
1263
1264 fd = igmp_sock_open(ifaddr, ifp);
1265 if (fd < 0) {
1266 zlog_warn("Could not open IGMP socket for %pI4 on %s",
1267 &ifaddr, ifp->name);
1268 return NULL;
1269 }
1270
1271 sin.sin_family = AF_INET;
1272 sin.sin_addr = ifaddr;
1273 sin.sin_port = 0;
1274 if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) != 0) {
1275 zlog_warn("Could not bind IGMP socket for %pI4 on %s: %s(%d)",
1276 &ifaddr, ifp->name, strerror(errno), errno);
1277 close(fd);
1278
1279 return NULL;
1280 }
1281
1282 igmp = igmp_sock_new(fd, ifaddr, ifp, mtrace_only);
1283
1284 igmp_read_on(igmp);
1285
1286 listnode_add(igmp_sock_list, igmp);
1287
1288 #ifdef IGMP_SOCK_DUMP
1289 igmp_sock_dump(igmp_sock_array);
1290 #endif
1291
1292 return igmp;
1293 }
1294
1295 /*
1296 RFC 3376: 6.5. Switching Router Filter-Modes
1297
1298 When a router's filter-mode for a group is EXCLUDE and the group
1299 timer expires, the router filter-mode for the group transitions to
1300 INCLUDE.
1301
1302 A router uses source records with running source timers as its state
1303 for the switch to a filter-mode of INCLUDE. If there are any source
1304 records with source timers greater than zero (i.e., requested to be
1305 forwarded), a router switches to filter-mode of INCLUDE using those
1306 source records. Source records whose timers are zero (from the
1307 previous EXCLUDE mode) are deleted.
1308 */
1309 static void igmp_group_timer(struct thread *t)
1310 {
1311 struct gm_group *group;
1312
1313 group = THREAD_ARG(t);
1314
1315 if (PIM_DEBUG_IGMP_TRACE) {
1316 char group_str[INET_ADDRSTRLEN];
1317 pim_inet4_dump("<group?>", group->group_addr, group_str,
1318 sizeof(group_str));
1319 zlog_debug("%s: Timer for group %s on interface %s", __func__,
1320 group_str, group->interface->name);
1321 }
1322
1323 assert(group->group_filtermode_isexcl);
1324
1325 group->group_filtermode_isexcl = 0;
1326
1327 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1328 igmp_anysource_forward_stop(group);
1329
1330 igmp_source_delete_expired(group->group_source_list);
1331
1332 assert(!group->group_filtermode_isexcl);
1333
1334 /*
1335 RFC 3376: 6.2.2. Definition of Group Timers
1336
1337 If there are no more source records for the group, delete group
1338 record.
1339 */
1340 if (listcount(group->group_source_list) < 1) {
1341 igmp_group_delete_empty_include(group);
1342 }
1343 }
1344
1345 static void group_timer_off(struct gm_group *group)
1346 {
1347 if (!group->t_group_timer)
1348 return;
1349
1350 if (PIM_DEBUG_IGMP_TRACE) {
1351 char group_str[INET_ADDRSTRLEN];
1352 pim_inet4_dump("<group?>", group->group_addr, group_str,
1353 sizeof(group_str));
1354 zlog_debug("Cancelling TIMER event for group %s on %s",
1355 group_str, group->interface->name);
1356 }
1357 THREAD_OFF(group->t_group_timer);
1358 }
1359
1360 void igmp_group_timer_on(struct gm_group *group, long interval_msec,
1361 const char *ifname)
1362 {
1363 group_timer_off(group);
1364
1365 if (PIM_DEBUG_IGMP_EVENTS) {
1366 char group_str[INET_ADDRSTRLEN];
1367 pim_inet4_dump("<group?>", group->group_addr, group_str,
1368 sizeof(group_str));
1369 zlog_debug(
1370 "Scheduling %ld.%03ld sec TIMER event for group %s on %s",
1371 interval_msec / 1000, interval_msec % 1000, group_str,
1372 ifname);
1373 }
1374
1375 /*
1376 RFC 3376: 6.2.2. Definition of Group Timers
1377
1378 The group timer is only used when a group is in EXCLUDE mode and
1379 it represents the time for the *filter-mode* of the group to
1380 expire and switch to INCLUDE mode.
1381 */
1382 assert(group->group_filtermode_isexcl);
1383
1384 thread_add_timer_msec(router->master, igmp_group_timer, group,
1385 interval_msec, &group->t_group_timer);
1386 }
1387
1388 struct gm_group *find_group_by_addr(struct gm_sock *igmp,
1389 struct in_addr group_addr)
1390 {
1391 struct gm_group lookup;
1392 struct pim_interface *pim_ifp = igmp->interface->info;
1393
1394 lookup.group_addr.s_addr = group_addr.s_addr;
1395
1396 return hash_lookup(pim_ifp->gm_group_hash, &lookup);
1397 }
1398
1399 struct gm_group *igmp_add_group_by_addr(struct gm_sock *igmp,
1400 struct in_addr group_addr)
1401 {
1402 struct gm_group *group;
1403 struct pim_interface *pim_ifp = igmp->interface->info;
1404
1405 group = find_group_by_addr(igmp, group_addr);
1406 if (group) {
1407 return group;
1408 }
1409
1410 if (!pim_is_group_224_4(group_addr)) {
1411 zlog_warn("%s: Group Specified is not part of 224.0.0.0/4",
1412 __func__);
1413 return NULL;
1414 }
1415
1416 if (pim_is_group_224_0_0_0_24(group_addr)) {
1417 if (PIM_DEBUG_IGMP_TRACE)
1418 zlog_debug(
1419 "%s: Group specified %pI4 is part of 224.0.0.0/24",
1420 __func__, &group_addr);
1421 return NULL;
1422 }
1423 /*
1424 Non-existant group is created as INCLUDE {empty}:
1425
1426 RFC 3376 - 5.1. Action on Change of Interface State
1427
1428 If no interface state existed for that multicast address before
1429 the change (i.e., the change consisted of creating a new
1430 per-interface record), or if no state exists after the change
1431 (i.e., the change consisted of deleting a per-interface record),
1432 then the "non-existent" state is considered to have a filter mode
1433 of INCLUDE and an empty source list.
1434 */
1435
1436 group = XCALLOC(MTYPE_PIM_IGMP_GROUP, sizeof(*group));
1437
1438 group->group_source_list = list_new();
1439 group->group_source_list->del = (void (*)(void *))igmp_source_free;
1440
1441 group->t_group_timer = NULL;
1442 group->t_group_query_retransmit_timer = NULL;
1443 group->group_specific_query_retransmit_count = 0;
1444 group->group_addr = group_addr;
1445 group->interface = igmp->interface;
1446 group->last_igmp_v1_report_dsec = -1;
1447 group->last_igmp_v2_report_dsec = -1;
1448 group->group_creation = pim_time_monotonic_sec();
1449 group->igmp_version = IGMP_DEFAULT_VERSION;
1450
1451 /* initialize new group as INCLUDE {empty} */
1452 group->group_filtermode_isexcl = 0; /* 0=INCLUDE, 1=EXCLUDE */
1453
1454 listnode_add(pim_ifp->gm_group_list, group);
1455 group = hash_get(pim_ifp->gm_group_hash, group, hash_alloc_intern);
1456
1457 if (PIM_DEBUG_IGMP_TRACE) {
1458 char group_str[INET_ADDRSTRLEN];
1459 pim_inet4_dump("<group?>", group->group_addr, group_str,
1460 sizeof(group_str));
1461 zlog_debug(
1462 "Creating new IGMP group %s on socket %d interface %s",
1463 group_str, igmp->fd, igmp->interface->name);
1464 }
1465
1466 igmp_group_count_incr(pim_ifp);
1467
1468 /*
1469 RFC 3376: 6.2.2. Definition of Group Timers
1470
1471 The group timer is only used when a group is in EXCLUDE mode and
1472 it represents the time for the *filter-mode* of the group to
1473 expire and switch to INCLUDE mode.
1474 */
1475 assert(!group->group_filtermode_isexcl); /* INCLUDE mode */
1476 assert(!group->t_group_timer); /* group timer == 0 */
1477
1478 /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
1479 igmp_anysource_forward_stop(group);
1480
1481 return group;
1482 }
1483
1484 void igmp_send_query(int igmp_version, struct gm_group *group, char *query_buf,
1485 int query_buf_size, int num_sources,
1486 struct in_addr dst_addr, struct in_addr group_addr,
1487 int query_max_response_time_dsec, uint8_t s_flag,
1488 struct gm_sock *igmp)
1489 {
1490 if (pim_addr_is_any(group_addr) &&
1491 ntohl(dst_addr.s_addr) == INADDR_ALLHOSTS_GROUP)
1492 igmp->igmp_stats.general_queries_sent++;
1493 else if (group)
1494 igmp->igmp_stats.group_queries_sent++;
1495
1496 if (igmp_version == 3) {
1497 igmp_v3_send_query(group, igmp->fd, igmp->interface->name,
1498 query_buf, query_buf_size, num_sources,
1499 dst_addr, group_addr,
1500 query_max_response_time_dsec, s_flag,
1501 igmp->querier_robustness_variable,
1502 igmp->querier_query_interval);
1503 } else if (igmp_version == 2) {
1504 igmp_v2_send_query(group, igmp->fd, igmp->interface->name,
1505 query_buf, dst_addr, group_addr,
1506 query_max_response_time_dsec);
1507 }
1508 }
1509
1510 void igmp_send_query_on_intf(struct interface *ifp, int igmp_ver)
1511 {
1512 struct pim_interface *pim_ifp = ifp->info;
1513 struct listnode *sock_node = NULL;
1514 struct gm_sock *igmp = NULL;
1515 struct in_addr dst_addr;
1516 struct in_addr group_addr;
1517 int query_buf_size;
1518
1519 if (!igmp_ver)
1520 igmp_ver = 2;
1521
1522 if (igmp_ver == 3)
1523 query_buf_size = PIM_IGMP_BUFSIZE_WRITE;
1524 else
1525 query_buf_size = IGMP_V12_MSG_SIZE;
1526
1527 dst_addr.s_addr = htonl(INADDR_ALLHOSTS_GROUP);
1528 group_addr.s_addr = PIM_NET_INADDR_ANY;
1529
1530 if (PIM_DEBUG_IGMP_TRACE)
1531 zlog_debug("Issuing general query on request on %s", ifp->name);
1532
1533 for (ALL_LIST_ELEMENTS_RO(pim_ifp->gm_socket_list, sock_node, igmp)) {
1534
1535 char query_buf[query_buf_size];
1536
1537 igmp_send_query(
1538 igmp_ver, 0 /* igmp_group */, query_buf,
1539 sizeof(query_buf), 0 /* num_sources */, dst_addr,
1540 group_addr, pim_ifp->gm_query_max_response_time_dsec,
1541 1 /* s_flag: always set for general queries */, igmp);
1542 }
1543 }