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