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 "linklist.h"
23 : #include "thread.h"
24 : #include "memory.h"
25 : #include "if.h"
26 : #include "vrf.h"
27 : #include "hash.h"
28 : #include "jhash.h"
29 : #include "prefix.h"
30 :
31 : #include "pimd.h"
32 : #include "pim_instance.h"
33 : #include "pim_str.h"
34 : #include "pim_iface.h"
35 : #include "pim_ifchannel.h"
36 : #include "pim_zebra.h"
37 : #include "pim_time.h"
38 : #include "pim_msg.h"
39 : #include "pim_pim.h"
40 : #include "pim_join.h"
41 : #include "pim_rpf.h"
42 : #include "pim_macro.h"
43 : #include "pim_oil.h"
44 : #include "pim_upstream.h"
45 : #include "pim_ssm.h"
46 : #include "pim_rp.h"
47 : #include "pim_mlag.h"
48 :
49 0 : RB_GENERATE(pim_ifchannel_rb, pim_ifchannel, pim_ifp_rb, pim_ifchannel_compare);
50 :
51 0 : int pim_ifchannel_compare(const struct pim_ifchannel *ch1,
52 : const struct pim_ifchannel *ch2)
53 : {
54 0 : struct pim_interface *pim_ifp1;
55 0 : struct pim_interface *pim_ifp2;
56 :
57 0 : pim_ifp1 = ch1->interface->info;
58 0 : pim_ifp2 = ch2->interface->info;
59 :
60 0 : if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index)
61 : return -1;
62 :
63 0 : if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index)
64 : return 1;
65 :
66 0 : return pim_sgaddr_cmp(ch1->sg, ch2->sg);
67 : }
68 :
69 : /*
70 : * A (*,G) or a (*,*) is going away
71 : * remove the parent pointer from
72 : * those pointing at us
73 : */
74 0 : static void pim_ifchannel_remove_children(struct pim_ifchannel *ch)
75 : {
76 0 : struct pim_ifchannel *child;
77 :
78 0 : if (!ch->sources)
79 : return;
80 :
81 0 : while (!list_isempty(ch->sources)) {
82 0 : child = listnode_head(ch->sources);
83 0 : child->parent = NULL;
84 0 : listnode_delete(ch->sources, child);
85 : }
86 : }
87 :
88 : /*
89 : * A (*,G) or a (*,*) is being created
90 : * find all the children that would point
91 : * at us.
92 : */
93 0 : static void pim_ifchannel_find_new_children(struct pim_ifchannel *ch)
94 : {
95 0 : struct pim_interface *pim_ifp = ch->interface->info;
96 0 : struct pim_ifchannel *child;
97 :
98 : // Basic Sanity that we are not being silly
99 0 : if (!pim_addr_is_any(ch->sg.src) && !pim_addr_is_any(ch->sg.grp))
100 : return;
101 :
102 0 : if (pim_addr_is_any(ch->sg.src) && pim_addr_is_any(ch->sg.grp))
103 : return;
104 :
105 0 : RB_FOREACH (child, pim_ifchannel_rb, &pim_ifp->ifchannel_rb) {
106 0 : if (!pim_addr_is_any(ch->sg.grp) &&
107 0 : !pim_addr_cmp(child->sg.grp, ch->sg.grp) && (child != ch)) {
108 0 : child->parent = ch;
109 0 : listnode_add_sort(ch->sources, child);
110 : }
111 : }
112 : }
113 :
114 0 : void pim_ifchannel_delete(struct pim_ifchannel *ch)
115 : {
116 0 : struct pim_interface *pim_ifp;
117 0 : struct pim_upstream *up;
118 :
119 0 : pim_ifp = ch->interface->info;
120 :
121 0 : if (PIM_DEBUG_PIM_TRACE)
122 0 : zlog_debug("%s: ifchannel entry %s(%s) del start", __func__,
123 : ch->sg_str, ch->interface->name);
124 :
125 0 : if (PIM_I_am_DualActive(pim_ifp)) {
126 0 : if (PIM_DEBUG_MLAG)
127 0 : zlog_debug(
128 : "%s: if-chnanel-%s is deleted from a Dual active Interface",
129 : __func__, ch->sg_str);
130 : /* Post Delete only if it is the last Dual-active Interface */
131 0 : if (ch->upstream->dualactive_ifchannel_count == 1) {
132 0 : pim_mlag_up_local_del(pim_ifp->pim, ch->upstream);
133 0 : PIM_UPSTREAM_FLAG_UNSET_MLAG_INTERFACE(
134 : ch->upstream->flags);
135 : }
136 0 : ch->upstream->dualactive_ifchannel_count--;
137 : }
138 :
139 0 : if (ch->upstream->channel_oil) {
140 0 : uint32_t mask = PIM_OIF_FLAG_PROTO_PIM;
141 0 : if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
142 0 : mask |= PIM_OIF_FLAG_PROTO_GM;
143 :
144 : /*
145 : * A S,G RPT channel can have an empty oil, we also
146 : * need to take into account the fact that a ifchannel
147 : * might have been suppressing a *,G ifchannel from
148 : * being inherited. So let's figure out what
149 : * needs to be done here
150 : */
151 0 : if (!pim_addr_is_any(ch->sg.src) &&
152 0 : pim_upstream_evaluate_join_desired_interface(
153 : ch->upstream, ch, ch->parent))
154 0 : pim_channel_add_oif(ch->upstream->channel_oil,
155 : ch->interface,
156 : PIM_OIF_FLAG_PROTO_STAR,
157 : __func__);
158 :
159 0 : pim_channel_del_oif(ch->upstream->channel_oil,
160 : ch->interface, mask, __func__);
161 : /*
162 : * Do we have any S,G's that are inheriting?
163 : * Nuke from on high too.
164 : */
165 0 : if (ch->upstream->sources) {
166 0 : struct pim_upstream *child;
167 0 : struct listnode *up_node;
168 :
169 0 : for (ALL_LIST_ELEMENTS_RO(ch->upstream->sources,
170 : up_node, child))
171 0 : pim_channel_del_inherited_oif(
172 : child->channel_oil,
173 : ch->interface,
174 : __func__);
175 : }
176 : }
177 :
178 : /*
179 : * When this channel is removed
180 : * we need to find all our children
181 : * and make sure our pointers are fixed
182 : */
183 0 : pim_ifchannel_remove_children(ch);
184 :
185 0 : if (ch->sources)
186 0 : list_delete(&ch->sources);
187 :
188 0 : listnode_delete(ch->upstream->ifchannels, ch);
189 :
190 0 : up = ch->upstream;
191 :
192 : /* upstream is common across ifchannels, check if upstream's
193 : ifchannel list is empty before deleting upstream_del
194 : ref count will take care of it.
195 : */
196 0 : if (ch->upstream->ref_count > 0)
197 0 : up = pim_upstream_del(pim_ifp->pim, ch->upstream, __func__);
198 :
199 : else {
200 0 : if (PIM_DEBUG_PIM_TRACE)
201 0 : zlog_debug(
202 : "%s: Avoiding deletion of upstream with ref_count %d from ifchannel(%s): %s",
203 : __func__, ch->upstream->ref_count,
204 : ch->interface->name, ch->sg_str);
205 : }
206 :
207 0 : ch->upstream = NULL;
208 :
209 0 : THREAD_OFF(ch->t_ifjoin_expiry_timer);
210 0 : THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
211 0 : THREAD_OFF(ch->t_ifassert_timer);
212 :
213 0 : if (ch->parent) {
214 0 : listnode_delete(ch->parent->sources, ch);
215 0 : ch->parent = NULL;
216 : }
217 :
218 0 : RB_REMOVE(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
219 :
220 0 : if (PIM_DEBUG_PIM_TRACE)
221 0 : zlog_debug("%s: ifchannel entry %s(%s) is deleted ", __func__,
222 : ch->sg_str, ch->interface->name);
223 :
224 0 : XFREE(MTYPE_PIM_IFCHANNEL, ch);
225 :
226 0 : if (up)
227 0 : pim_upstream_update_join_desired(pim_ifp->pim, up);
228 0 : }
229 :
230 17 : void pim_ifchannel_delete_all(struct interface *ifp)
231 : {
232 17 : struct pim_interface *pim_ifp;
233 17 : struct pim_ifchannel *ch;
234 :
235 17 : pim_ifp = ifp->info;
236 17 : if (!pim_ifp)
237 : return;
238 :
239 17 : while (!RB_EMPTY(pim_ifchannel_rb, &pim_ifp->ifchannel_rb)) {
240 0 : ch = RB_ROOT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb);
241 :
242 0 : pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
243 0 : pim_ifchannel_delete(ch);
244 : }
245 : }
246 :
247 0 : void delete_on_noinfo(struct pim_ifchannel *ch)
248 : {
249 0 : if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO
250 0 : && ch->ifjoin_state == PIM_IFJOIN_NOINFO
251 0 : && ch->t_ifjoin_expiry_timer == NULL)
252 0 : pim_ifchannel_delete(ch);
253 0 : }
254 :
255 0 : void pim_ifchannel_ifjoin_switch(const char *caller, struct pim_ifchannel *ch,
256 : enum pim_ifjoin_state new_state)
257 : {
258 0 : enum pim_ifjoin_state old_state = ch->ifjoin_state;
259 0 : struct pim_interface *pim_ifp = ch->interface->info;
260 0 : struct pim_ifchannel *child_ch;
261 :
262 0 : if (PIM_DEBUG_PIM_EVENTS)
263 0 : zlog_debug(
264 : "PIM_IFCHANNEL(%s): %s is switching from %s to %s",
265 : ch->interface->name, ch->sg_str,
266 : pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
267 : pim_ifchannel_ifjoin_name(new_state, 0));
268 :
269 :
270 0 : if (old_state == new_state) {
271 0 : if (PIM_DEBUG_PIM_EVENTS) {
272 0 : zlog_debug(
273 : "%s called by %s: non-transition on state %d (%s)",
274 : __func__, caller, new_state,
275 : pim_ifchannel_ifjoin_name(new_state, 0));
276 : }
277 0 : return;
278 : }
279 :
280 0 : ch->ifjoin_state = new_state;
281 :
282 0 : if (pim_addr_is_any(ch->sg.src)) {
283 0 : struct pim_upstream *up = ch->upstream;
284 0 : struct pim_upstream *child;
285 0 : struct listnode *up_node;
286 :
287 0 : if (up) {
288 0 : if (ch->ifjoin_state == PIM_IFJOIN_NOINFO) {
289 0 : for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
290 : child)) {
291 0 : struct channel_oil *c_oil =
292 : child->channel_oil;
293 :
294 0 : if (PIM_DEBUG_PIM_TRACE)
295 0 : zlog_debug(
296 : "%s %s: Prune(S,G)=%s from %s",
297 : __FILE__, __func__,
298 : child->sg_str,
299 : up->sg_str);
300 0 : if (!c_oil)
301 0 : continue;
302 :
303 : /*
304 : * If the S,G has no if channel and the
305 : * c_oil still
306 : * has output here then the *,G was
307 : * supplying the implied
308 : * if channel. So remove it.
309 : */
310 0 : if (oil_if_has(c_oil,
311 0 : pim_ifp->mroute_vif_index))
312 0 : pim_channel_del_inherited_oif(
313 : c_oil, ch->interface,
314 : __func__);
315 : }
316 : }
317 0 : if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
318 0 : for (ALL_LIST_ELEMENTS_RO(up->sources, up_node,
319 : child)) {
320 0 : if (PIM_DEBUG_PIM_TRACE)
321 0 : zlog_debug(
322 : "%s %s: Join(S,G)=%s from %s",
323 : __FILE__, __func__,
324 : child->sg_str,
325 : up->sg_str);
326 :
327 : /* check if the channel can be
328 : * inherited into the SG's OIL
329 : */
330 0 : child_ch = pim_ifchannel_find(
331 : ch->interface,
332 : &child->sg);
333 0 : if (pim_upstream_eval_inherit_if(
334 : child, child_ch, ch)) {
335 0 : pim_channel_add_oif(
336 : child->channel_oil,
337 : ch->interface,
338 : PIM_OIF_FLAG_PROTO_STAR,
339 : __func__);
340 0 : pim_upstream_update_join_desired(
341 : pim_ifp->pim, child);
342 : }
343 : }
344 : }
345 : }
346 : }
347 : /* Transition to/from NOINFO ? */
348 0 : if ((old_state == PIM_IFJOIN_NOINFO)
349 0 : || (new_state == PIM_IFJOIN_NOINFO)) {
350 :
351 0 : if (PIM_DEBUG_PIM_EVENTS) {
352 0 : zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
353 : ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN"
354 : : "UP"),
355 : ch->sg_str, ch->interface->name);
356 : }
357 :
358 : /*
359 : Record uptime of state transition to/from NOINFO
360 : */
361 0 : ch->ifjoin_creation = pim_time_monotonic_sec();
362 :
363 0 : pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
364 0 : pim_ifchannel_update_could_assert(ch);
365 0 : pim_ifchannel_update_assert_tracking_desired(ch);
366 : }
367 : }
368 :
369 0 : const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state,
370 : int flags)
371 : {
372 0 : switch (ifjoin_state) {
373 0 : case PIM_IFJOIN_NOINFO:
374 0 : if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
375 : return "SGRpt(NI)";
376 : else
377 0 : return "NOINFO";
378 : case PIM_IFJOIN_JOIN:
379 : return "JOIN";
380 0 : case PIM_IFJOIN_PRUNE:
381 0 : if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
382 : return "SGRpt(P)";
383 : else
384 0 : return "PRUNE";
385 0 : case PIM_IFJOIN_PRUNE_PENDING:
386 0 : if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
387 : return "SGRpt(PP)";
388 : else
389 0 : return "PRUNEP";
390 0 : case PIM_IFJOIN_PRUNE_TMP:
391 0 : if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
392 : return "SGRpt(P')";
393 : else
394 0 : return "PRUNET";
395 0 : case PIM_IFJOIN_PRUNE_PENDING_TMP:
396 0 : if (PIM_IF_FLAG_TEST_S_G_RPT(flags))
397 : return "SGRpt(PP')";
398 : else
399 0 : return "PRUNEPT";
400 : }
401 :
402 0 : return "ifjoin_bad_state";
403 : }
404 :
405 0 : const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
406 : {
407 0 : switch (ifassert_state) {
408 : case PIM_IFASSERT_NOINFO:
409 : return "NOINFO";
410 0 : case PIM_IFASSERT_I_AM_WINNER:
411 0 : return "WINNER";
412 0 : case PIM_IFASSERT_I_AM_LOSER:
413 0 : return "LOSER";
414 : }
415 :
416 0 : return "ifassert_bad_state";
417 : }
418 :
419 : /*
420 : RFC 4601: 4.6.5. Assert State Macros
421 :
422 : AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
423 : defaults to Infinity when in the NoInfo state.
424 : */
425 0 : void reset_ifassert_state(struct pim_ifchannel *ch)
426 : {
427 0 : THREAD_OFF(ch->t_ifassert_timer);
428 :
429 0 : pim_ifassert_winner_set(ch, PIM_IFASSERT_NOINFO, PIMADDR_ANY,
430 0 : router->infinite_assert_metric);
431 0 : }
432 :
433 0 : struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, pim_sgaddr *sg)
434 : {
435 0 : struct pim_interface *pim_ifp;
436 0 : struct pim_ifchannel *ch;
437 0 : struct pim_ifchannel lookup;
438 :
439 0 : pim_ifp = ifp->info;
440 :
441 0 : if (!pim_ifp) {
442 0 : zlog_warn("%s: (S,G)=%pSG: multicast not enabled on interface %s",
443 : __func__, sg, ifp->name);
444 0 : return NULL;
445 : }
446 :
447 0 : lookup.sg = *sg;
448 0 : lookup.interface = ifp;
449 0 : ch = RB_FIND(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, &lookup);
450 :
451 0 : return ch;
452 : }
453 :
454 0 : static void ifmembership_set(struct pim_ifchannel *ch,
455 : enum pim_ifmembership membership)
456 : {
457 0 : struct pim_interface *pim_ifp = ch->interface->info;
458 :
459 0 : if (ch->local_ifmembership == membership)
460 : return;
461 :
462 0 : if (PIM_DEBUG_PIM_EVENTS) {
463 0 : zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
464 : __func__, ch->sg_str,
465 : membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE"
466 : : "NOINFO",
467 : ch->interface->name);
468 : }
469 :
470 0 : ch->local_ifmembership = membership;
471 :
472 0 : pim_upstream_update_join_desired(pim_ifp->pim, ch->upstream);
473 0 : pim_ifchannel_update_could_assert(ch);
474 0 : pim_ifchannel_update_assert_tracking_desired(ch);
475 : }
476 :
477 :
478 0 : void pim_ifchannel_membership_clear(struct interface *ifp)
479 : {
480 0 : struct pim_interface *pim_ifp;
481 0 : struct pim_ifchannel *ch;
482 :
483 0 : pim_ifp = ifp->info;
484 0 : assert(pim_ifp);
485 :
486 0 : RB_FOREACH (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb)
487 0 : ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
488 0 : }
489 :
490 0 : void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
491 : {
492 0 : struct pim_interface *pim_ifp;
493 0 : struct pim_ifchannel *ch, *ch_tmp;
494 :
495 0 : pim_ifp = ifp->info;
496 0 : assert(pim_ifp);
497 :
498 0 : RB_FOREACH_SAFE (ch, pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch_tmp)
499 0 : delete_on_noinfo(ch);
500 0 : }
501 :
502 : /*
503 : * For a given Interface, if we are given a S,G
504 : * Find the *,G (If we have it).
505 : * If we are passed a *,G, find the *,* ifchannel
506 : * if we have it.
507 : */
508 0 : static struct pim_ifchannel *pim_ifchannel_find_parent(struct pim_ifchannel *ch)
509 : {
510 0 : pim_sgaddr parent_sg = ch->sg;
511 0 : struct pim_ifchannel *parent = NULL;
512 :
513 : // (S,G)
514 0 : if (!pim_addr_is_any(parent_sg.src) &&
515 0 : !pim_addr_is_any(parent_sg.grp)) {
516 0 : parent_sg.src = PIMADDR_ANY;
517 0 : parent = pim_ifchannel_find(ch->interface, &parent_sg);
518 :
519 0 : if (parent)
520 0 : listnode_add(parent->sources, ch);
521 0 : return parent;
522 : }
523 :
524 : return NULL;
525 : }
526 :
527 0 : struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, pim_sgaddr *sg,
528 : uint8_t source_flags, int up_flags)
529 : {
530 0 : struct pim_interface *pim_ifp;
531 0 : struct pim_ifchannel *ch;
532 0 : struct pim_upstream *up;
533 :
534 0 : ch = pim_ifchannel_find(ifp, sg);
535 0 : if (ch) {
536 0 : if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
537 0 : PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
538 :
539 0 : if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
540 0 : PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
541 :
542 0 : ch->upstream->flags |= up_flags;
543 :
544 0 : return ch;
545 : }
546 :
547 0 : pim_ifp = ifp->info;
548 :
549 0 : ch = XCALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
550 :
551 0 : ch->flags = 0;
552 0 : if ((source_flags & PIM_ENCODE_RPT_BIT)
553 : && !(source_flags & PIM_ENCODE_WC_BIT))
554 0 : PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
555 :
556 0 : ch->interface = ifp;
557 0 : ch->sg = *sg;
558 0 : snprintfrr(ch->sg_str, sizeof(ch->sg_str), "%pSG", sg);
559 0 : ch->parent = pim_ifchannel_find_parent(ch);
560 0 : if (pim_addr_is_any(ch->sg.src)) {
561 0 : ch->sources = list_new();
562 0 : ch->sources->cmp =
563 : (int (*)(void *, void *))pim_ifchannel_compare;
564 : } else
565 0 : ch->sources = NULL;
566 :
567 0 : pim_ifchannel_find_new_children(ch);
568 0 : ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
569 :
570 0 : ch->ifjoin_state = PIM_IFJOIN_NOINFO;
571 0 : ch->t_ifjoin_expiry_timer = NULL;
572 0 : ch->t_ifjoin_prune_pending_timer = NULL;
573 0 : ch->ifjoin_creation = 0;
574 :
575 0 : RB_INSERT(pim_ifchannel_rb, &pim_ifp->ifchannel_rb, ch);
576 :
577 0 : up = pim_upstream_add(pim_ifp->pim, sg, NULL, up_flags, __func__, ch);
578 :
579 0 : ch->upstream = up;
580 :
581 0 : listnode_add_sort(up->ifchannels, ch);
582 :
583 0 : ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
584 0 : ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval(ch);
585 :
586 0 : ch->ifassert_winner = PIMADDR_ANY;
587 :
588 : /* Assert state */
589 0 : ch->t_ifassert_timer = NULL;
590 0 : ch->ifassert_state = PIM_IFASSERT_NOINFO;
591 0 : reset_ifassert_state(ch);
592 0 : if (pim_macro_ch_could_assert_eval(ch))
593 0 : PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
594 : else
595 0 : PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
596 :
597 0 : if (pim_macro_assert_tracking_desired_eval(ch))
598 0 : PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
599 : else
600 0 : PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
601 :
602 : /*
603 : * advertise MLAG Data to MLAG peer
604 : */
605 0 : if (PIM_I_am_DualActive(pim_ifp)) {
606 0 : up->dualactive_ifchannel_count++;
607 : /* Sync once for upstream */
608 0 : if (up->dualactive_ifchannel_count == 1) {
609 0 : PIM_UPSTREAM_FLAG_SET_MLAG_INTERFACE(up->flags);
610 0 : pim_mlag_up_local_add(pim_ifp->pim, up);
611 : }
612 0 : if (PIM_DEBUG_MLAG)
613 0 : zlog_debug(
614 : "%s: New Dual active if-chnanel is added to upstream:%s count:%d, flags:0x%x",
615 : __func__, up->sg_str,
616 : up->dualactive_ifchannel_count, up->flags);
617 : }
618 :
619 0 : if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_PIM)
620 0 : PIM_IF_FLAG_SET_PROTO_PIM(ch->flags);
621 :
622 0 : if (up_flags == PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
623 0 : PIM_IF_FLAG_SET_PROTO_IGMP(ch->flags);
624 :
625 0 : if (PIM_DEBUG_PIM_TRACE)
626 0 : zlog_debug("%s: ifchannel %s(%s) is created ", __func__,
627 : ch->sg_str, ch->interface->name);
628 :
629 : return ch;
630 : }
631 :
632 0 : static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
633 : {
634 0 : pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_NOINFO);
635 0 : pim_forward_stop(ch);
636 :
637 0 : PIM_UPSTREAM_FLAG_UNSET_SRC_PIM(ch->upstream->flags);
638 :
639 0 : PIM_IF_FLAG_UNSET_PROTO_PIM(ch->flags);
640 :
641 0 : delete_on_noinfo(ch);
642 0 : }
643 :
644 0 : static void on_ifjoin_expiry_timer(struct thread *t)
645 : {
646 0 : struct pim_ifchannel *ch;
647 :
648 0 : ch = THREAD_ARG(t);
649 :
650 0 : if (PIM_DEBUG_PIM_TRACE)
651 0 : zlog_debug("%s: ifchannel %s expiry timer", __func__,
652 : ch->sg_str);
653 :
654 0 : ifjoin_to_noinfo(ch);
655 : /* ch may have been deleted */
656 0 : }
657 :
658 0 : static void on_ifjoin_prune_pending_timer(struct thread *t)
659 : {
660 0 : struct pim_ifchannel *ch;
661 0 : int send_prune_echo; /* boolean */
662 0 : struct interface *ifp;
663 0 : struct pim_interface *pim_ifp;
664 :
665 0 : ch = THREAD_ARG(t);
666 :
667 0 : if (PIM_DEBUG_PIM_TRACE)
668 0 : zlog_debug("%s: IFCHANNEL%pSG %s Prune Pending Timer Popped",
669 : __func__, &ch->sg,
670 : pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags));
671 :
672 0 : if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) {
673 0 : ifp = ch->interface;
674 0 : pim_ifp = ifp->info;
675 0 : if (!PIM_IF_FLAG_TEST_S_G_RPT(ch->flags)) {
676 : /* Send PruneEcho(S,G) ? */
677 0 : send_prune_echo =
678 0 : (listcount(pim_ifp->pim_neighbor_list) > 1);
679 :
680 0 : if (send_prune_echo) {
681 0 : struct pim_rpf rpf;
682 :
683 0 : rpf.source_nexthop.interface = ifp;
684 0 : rpf.rpf_addr = pim_ifp->primary_address;
685 0 : pim_jp_agg_single_upstream_send(
686 : &rpf, ch->upstream, 0);
687 : }
688 :
689 0 : ifjoin_to_noinfo(ch);
690 : } else {
691 : /* If SGRpt flag is set on ifchannel, Trigger SGRpt
692 : * message on RP path upon prune timer expiry.
693 : */
694 0 : ch->ifjoin_state = PIM_IFJOIN_PRUNE;
695 0 : struct pim_upstream *parent =
696 0 : ch->upstream->parent;
697 :
698 0 : pim_upstream_update_join_desired(pim_ifp->pim,
699 : ch->upstream);
700 :
701 0 : pim_jp_agg_single_upstream_send(&parent->rpf,
702 : parent, true);
703 : /*
704 : * SGRpt prune pending expiry has to install
705 : * SG entry with empty olist to drop the SG
706 : * traffic incase no other intf exists.
707 : * On that scenario, SG entry wouldn't have
708 : * got installed until Prune pending timer
709 : * expired. So install now.
710 : */
711 0 : pim_channel_del_oif(
712 0 : ch->upstream->channel_oil, ifp,
713 : PIM_OIF_FLAG_PROTO_STAR, __func__);
714 0 : pim_channel_del_oif(ch->upstream->channel_oil, ifp,
715 : PIM_OIF_FLAG_PROTO_PIM, __func__);
716 0 : if (!ch->upstream->channel_oil->installed)
717 0 : pim_upstream_mroute_add(
718 : ch->upstream->channel_oil,
719 : __func__);
720 : }
721 : /* from here ch may have been deleted */
722 : }
723 0 : }
724 :
725 0 : static void check_recv_upstream(int is_join, struct interface *recv_ifp,
726 : pim_addr upstream, pim_sgaddr *sg,
727 : uint8_t source_flags, int holdtime)
728 : {
729 0 : struct pim_upstream *up;
730 0 : struct pim_interface *pim_ifp = recv_ifp->info;
731 0 : pim_addr rpf_addr;
732 :
733 : /* Upstream (S,G) in Joined state ? */
734 0 : up = pim_upstream_find(pim_ifp->pim, sg);
735 0 : if (!up)
736 0 : return;
737 0 : if (up->join_state != PIM_UPSTREAM_JOINED)
738 : return;
739 :
740 : /* Upstream (S,G) in Joined state */
741 :
742 0 : if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
743 : /* RPF'(S,G) not found */
744 0 : zlog_warn("%s %s: RPF'%s not found", __FILE__, __func__,
745 : up->sg_str);
746 0 : return;
747 : }
748 :
749 0 : rpf_addr = up->rpf.rpf_addr;
750 :
751 : /* upstream directed to RPF'(S,G) ? */
752 0 : if (pim_addr_cmp(upstream, rpf_addr)) {
753 0 : zlog_warn(
754 : "%s %s: (S,G)=%s upstream=%pPAs not directed to RPF'(S,G)=%pPAs on interface %s",
755 : __FILE__, __func__, up->sg_str, &upstream, &rpf_addr,
756 : recv_ifp->name);
757 0 : return;
758 : }
759 : /* upstream directed to RPF'(S,G) */
760 :
761 0 : if (is_join) {
762 : /* Join(S,G) to RPF'(S,G) */
763 0 : pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime);
764 0 : return;
765 : }
766 :
767 : /* Prune to RPF'(S,G) */
768 :
769 0 : if (source_flags & PIM_RPT_BIT_MASK) {
770 0 : if (source_flags & PIM_WILDCARD_BIT_MASK) {
771 : /* Prune(*,G) to RPF'(S,G) */
772 0 : pim_upstream_join_timer_decrease_to_t_override(
773 : "Prune(*,G)", up);
774 0 : return;
775 : }
776 :
777 : /* Prune(S,G,rpt) to RPF'(S,G) */
778 0 : pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
779 : up);
780 0 : return;
781 : }
782 :
783 : /* Prune(S,G) to RPF'(S,G) */
784 0 : pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
785 : }
786 :
787 0 : static int nonlocal_upstream(int is_join, struct interface *recv_ifp,
788 : pim_addr upstream, pim_sgaddr *sg,
789 : uint8_t source_flags, uint16_t holdtime)
790 : {
791 0 : struct pim_interface *recv_pim_ifp;
792 0 : int is_local; /* boolean */
793 :
794 0 : recv_pim_ifp = recv_ifp->info;
795 0 : assert(recv_pim_ifp);
796 :
797 0 : is_local = !pim_addr_cmp(upstream, recv_pim_ifp->primary_address);
798 :
799 0 : if (is_local)
800 : return 0;
801 :
802 0 : if (PIM_DEBUG_PIM_TRACE_DETAIL)
803 0 : zlog_warn(
804 : "%s: recv %s (S,G)=%pSG to non-local upstream=%pPAs on %s",
805 : __func__, is_join ? "join" : "prune", sg, &upstream,
806 : recv_ifp->name);
807 :
808 : /*
809 : * Since recv upstream addr was not directed to our primary
810 : * address, check if we should react to it in any way.
811 : */
812 0 : check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags,
813 : holdtime);
814 :
815 0 : return 1; /* non-local */
816 : }
817 :
818 0 : static void pim_ifchannel_ifjoin_handler(struct pim_ifchannel *ch,
819 : struct pim_interface *pim_ifp)
820 : {
821 0 : pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
822 0 : PIM_IF_FLAG_UNSET_S_G_RPT(ch->flags);
823 : /* check if the interface qualifies as an immediate
824 : * OIF
825 : */
826 0 : if (pim_upstream_evaluate_join_desired_interface(
827 : ch->upstream, ch,
828 : NULL /*starch*/)) {
829 0 : pim_channel_add_oif(ch->upstream->channel_oil,
830 : ch->interface,
831 : PIM_OIF_FLAG_PROTO_PIM,
832 : __func__);
833 0 : pim_upstream_update_join_desired(pim_ifp->pim,
834 : ch->upstream);
835 : }
836 0 : }
837 :
838 :
839 0 : void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr,
840 : pim_addr upstream, pim_sgaddr *sg,
841 : uint8_t source_flags, uint16_t holdtime)
842 : {
843 0 : struct pim_interface *pim_ifp;
844 0 : struct pim_ifchannel *ch;
845 :
846 0 : if (nonlocal_upstream(1 /* join */, ifp, upstream, sg, source_flags,
847 : holdtime)) {
848 : return;
849 : }
850 :
851 0 : ch = pim_ifchannel_add(ifp, sg, source_flags,
852 : PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
853 :
854 : /*
855 : RFC 4601: 4.6.1. (S,G) Assert Message State Machine
856 :
857 : Transitions from "I am Assert Loser" State
858 :
859 : Receive Join(S,G) on Interface I
860 :
861 : We receive a Join(S,G) that has the Upstream Neighbor Address
862 : field set to my primary IP address on interface I. The action is
863 : to transition to NoInfo state, delete this (S,G) assert state
864 : (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
865 : to operate.
866 :
867 : Notice: The nonlocal_upstream() test above ensures the upstream
868 : address of the join message is our primary address.
869 : */
870 0 : if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
871 0 : zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s",
872 : __func__, ch->sg_str, &neigh_addr, ifp->name);
873 :
874 0 : assert_action_a5(ch);
875 : }
876 :
877 0 : pim_ifp = ifp->info;
878 0 : assert(pim_ifp);
879 :
880 0 : switch (ch->ifjoin_state) {
881 0 : case PIM_IFJOIN_NOINFO:
882 0 : pim_ifchannel_ifjoin_switch(__func__, ch, PIM_IFJOIN_JOIN);
883 0 : if (pim_macro_chisin_oiflist(ch)) {
884 0 : pim_upstream_inherited_olist(pim_ifp->pim,
885 : ch->upstream);
886 0 : pim_forward_start(ch);
887 : }
888 : /*
889 : * If we are going to be a LHR, we need to note it
890 : */
891 0 : if (ch->upstream->parent &&
892 0 : (PIM_UPSTREAM_FLAG_TEST_CAN_BE_LHR(
893 : ch->upstream->parent->flags))
894 0 : && !(ch->upstream->flags
895 0 : & PIM_UPSTREAM_FLAG_MASK_SRC_LHR)) {
896 0 : pim_upstream_ref(ch->upstream,
897 : PIM_UPSTREAM_FLAG_MASK_SRC_LHR,
898 : __func__);
899 0 : pim_upstream_keep_alive_timer_start(
900 0 : ch->upstream, pim_ifp->pim->keep_alive_time);
901 : }
902 : break;
903 0 : case PIM_IFJOIN_JOIN:
904 0 : assert(!ch->t_ifjoin_prune_pending_timer);
905 :
906 : /*
907 : In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to
908 : a
909 : previously received join message with holdtime=0xFFFF.
910 : */
911 0 : if (ch->t_ifjoin_expiry_timer) {
912 0 : unsigned long remain = thread_timer_remain_second(
913 : ch->t_ifjoin_expiry_timer);
914 0 : if (remain > holdtime) {
915 : /*
916 : RFC 4601: 4.5.3. Receiving (S,G) Join/Prune
917 : Messages
918 :
919 : Transitions from Join State
920 :
921 : The (S,G) downstream state machine on
922 : interface I remains in
923 : Join state, and the Expiry Timer (ET) is
924 : restarted, set to
925 : maximum of its current value and the HoldTime
926 : from the
927 : triggering Join/Prune message.
928 :
929 : Conclusion: Do not change the ET if the
930 : current value is
931 : higher than the received join holdtime.
932 : */
933 : return;
934 : }
935 : }
936 0 : THREAD_OFF(ch->t_ifjoin_expiry_timer);
937 : break;
938 0 : case PIM_IFJOIN_PRUNE:
939 0 : if (source_flags & PIM_ENCODE_RPT_BIT) {
940 0 : pim_ifchannel_ifjoin_switch(__func__, ch,
941 : PIM_IFJOIN_NOINFO);
942 0 : THREAD_OFF(ch->t_ifjoin_expiry_timer);
943 0 : delete_on_noinfo(ch);
944 0 : return;
945 : } else
946 0 : pim_ifchannel_ifjoin_handler(ch, pim_ifp);
947 0 : break;
948 0 : case PIM_IFJOIN_PRUNE_PENDING:
949 : /*
950 : * Transitions from Prune-Pending State (Receive Join)
951 : * RFC 7761 Sec 4.5.2:
952 : * The (S,G) downstream state machine on interface I
953 : * transitions to the Join state. The Prune-Pending Timer is
954 : * canceled (without triggering an expiry event). The
955 : * Expiry Timer (ET) is restarted and is then set to the
956 : * maximum of its current value and the HoldTime from the
957 : * triggering Join/Prune message.
958 : */
959 0 : THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
960 :
961 : /* Check if SGRpt join Received */
962 0 : if ((source_flags & PIM_ENCODE_RPT_BIT) &&
963 0 : !pim_addr_is_any(sg->src)) {
964 : /*
965 : * Transitions from Prune-Pending State (Rcv SGRpt Join)
966 : * RFC 7761 Sec 4.5.3:
967 : * The (S,G,rpt) downstream state machine on interface
968 : * I transitions to the NoInfo state.The ET and PPT are
969 : * cancelled.
970 : */
971 0 : THREAD_OFF(ch->t_ifjoin_expiry_timer);
972 0 : pim_ifchannel_ifjoin_switch(__func__, ch,
973 : PIM_IFJOIN_NOINFO);
974 0 : return;
975 : }
976 :
977 0 : pim_ifchannel_ifjoin_handler(ch, pim_ifp);
978 :
979 0 : if (ch->t_ifjoin_expiry_timer) {
980 0 : unsigned long remain = thread_timer_remain_second(
981 : ch->t_ifjoin_expiry_timer);
982 :
983 0 : if (remain > holdtime)
984 : return;
985 : }
986 0 : THREAD_OFF(ch->t_ifjoin_expiry_timer);
987 :
988 : break;
989 : case PIM_IFJOIN_PRUNE_TMP:
990 : break;
991 : case PIM_IFJOIN_PRUNE_PENDING_TMP:
992 : break;
993 : }
994 :
995 0 : if (holdtime != 0xFFFF) {
996 0 : thread_add_timer(router->master, on_ifjoin_expiry_timer, ch,
997 : holdtime, &ch->t_ifjoin_expiry_timer);
998 : }
999 : }
1000 :
1001 0 : void pim_ifchannel_prune(struct interface *ifp, pim_addr upstream,
1002 : pim_sgaddr *sg, uint8_t source_flags,
1003 : uint16_t holdtime)
1004 : {
1005 0 : struct pim_ifchannel *ch;
1006 0 : struct pim_interface *pim_ifp;
1007 0 : int jp_override_interval_msec;
1008 :
1009 0 : if (nonlocal_upstream(0 /* prune */, ifp, upstream, sg, source_flags,
1010 : holdtime)) {
1011 : return;
1012 : }
1013 :
1014 0 : ch = pim_ifchannel_find(ifp, sg);
1015 0 : if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) {
1016 0 : if (PIM_DEBUG_PIM_TRACE)
1017 0 : zlog_debug("%s: Received prune with no relevant ifchannel %s%pSG state: %d",
1018 : __func__, ifp->name, sg,
1019 : source_flags);
1020 0 : return;
1021 : }
1022 :
1023 0 : ch = pim_ifchannel_add(ifp, sg, source_flags,
1024 : PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
1025 :
1026 0 : pim_ifp = ifp->info;
1027 :
1028 0 : switch (ch->ifjoin_state) {
1029 0 : case PIM_IFJOIN_NOINFO:
1030 0 : if (source_flags & PIM_ENCODE_RPT_BIT) {
1031 0 : if (!(source_flags & PIM_ENCODE_WC_BIT))
1032 0 : PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
1033 :
1034 0 : ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1035 0 : if (listcount(pim_ifp->pim_neighbor_list) > 1)
1036 0 : jp_override_interval_msec =
1037 0 : pim_if_jp_override_interval_msec(ifp);
1038 : else
1039 : jp_override_interval_msec =
1040 : 0; /* schedule to expire immediately */
1041 : /* If we called ifjoin_prune() directly instead, care
1042 : should
1043 : be taken not to use "ch" afterwards since it would be
1044 : deleted. */
1045 :
1046 0 : THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1047 0 : THREAD_OFF(ch->t_ifjoin_expiry_timer);
1048 0 : thread_add_timer_msec(
1049 : router->master, on_ifjoin_prune_pending_timer,
1050 : ch, jp_override_interval_msec,
1051 : &ch->t_ifjoin_prune_pending_timer);
1052 0 : thread_add_timer(router->master, on_ifjoin_expiry_timer,
1053 : ch, holdtime,
1054 : &ch->t_ifjoin_expiry_timer);
1055 0 : pim_upstream_update_join_desired(pim_ifp->pim,
1056 : ch->upstream);
1057 : }
1058 : break;
1059 : case PIM_IFJOIN_PRUNE_PENDING:
1060 : /* nothing to do */
1061 : break;
1062 0 : case PIM_IFJOIN_JOIN:
1063 : /*
1064 : * The (S,G) downstream state machine on interface I
1065 : * transitions to the Prune-Pending state. The
1066 : * Prune-Pending Timer is started. It is set to the
1067 : * J/P_Override_Interval(I) if the router has more than one
1068 : * neighbor on that interface; otherwise, it is set to zero,
1069 : * causing it to expire immediately.
1070 : */
1071 :
1072 0 : pim_ifchannel_ifjoin_switch(__func__, ch,
1073 : PIM_IFJOIN_PRUNE_PENDING);
1074 :
1075 0 : if (listcount(pim_ifp->pim_neighbor_list) > 1)
1076 0 : jp_override_interval_msec =
1077 0 : pim_if_jp_override_interval_msec(ifp);
1078 : else
1079 : jp_override_interval_msec =
1080 : 0; /* schedule to expire immediately */
1081 : /* If we called ifjoin_prune() directly instead, care should
1082 : be taken not to use "ch" afterwards since it would be
1083 : deleted. */
1084 0 : THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1085 0 : thread_add_timer_msec(router->master,
1086 : on_ifjoin_prune_pending_timer, ch,
1087 : jp_override_interval_msec,
1088 : &ch->t_ifjoin_prune_pending_timer);
1089 0 : break;
1090 0 : case PIM_IFJOIN_PRUNE:
1091 0 : if (source_flags & PIM_ENCODE_RPT_BIT) {
1092 0 : THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
1093 : /*
1094 : * While in Prune State, Receive SGRpt Prune.
1095 : * RFC 7761 Sec 4.5.3:
1096 : * The (S,G,rpt) downstream state machine on interface I
1097 : * remains in Prune state. The Expiry Timer (ET) is
1098 : * restarted and is then set to the maximum of its
1099 : * current value and the HoldTime from the triggering
1100 : * Join/Prune message.
1101 : */
1102 0 : if (ch->t_ifjoin_expiry_timer) {
1103 0 : unsigned long rem = thread_timer_remain_second(
1104 : ch->t_ifjoin_expiry_timer);
1105 :
1106 0 : if (rem > holdtime)
1107 : return;
1108 0 : THREAD_OFF(ch->t_ifjoin_expiry_timer);
1109 : }
1110 :
1111 0 : thread_add_timer(router->master, on_ifjoin_expiry_timer,
1112 : ch, holdtime,
1113 : &ch->t_ifjoin_expiry_timer);
1114 : }
1115 : break;
1116 0 : case PIM_IFJOIN_PRUNE_TMP:
1117 0 : if (source_flags & PIM_ENCODE_RPT_BIT) {
1118 0 : ch->ifjoin_state = PIM_IFJOIN_PRUNE;
1119 0 : THREAD_OFF(ch->t_ifjoin_expiry_timer);
1120 0 : thread_add_timer(router->master, on_ifjoin_expiry_timer,
1121 : ch, holdtime,
1122 : &ch->t_ifjoin_expiry_timer);
1123 : }
1124 : break;
1125 0 : case PIM_IFJOIN_PRUNE_PENDING_TMP:
1126 0 : if (source_flags & PIM_ENCODE_RPT_BIT) {
1127 0 : ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
1128 0 : THREAD_OFF(ch->t_ifjoin_expiry_timer);
1129 0 : thread_add_timer(router->master, on_ifjoin_expiry_timer,
1130 : ch, holdtime,
1131 : &ch->t_ifjoin_expiry_timer);
1132 : }
1133 : break;
1134 : }
1135 : }
1136 :
1137 0 : int pim_ifchannel_local_membership_add(struct interface *ifp, pim_sgaddr *sg,
1138 : bool is_vxlan)
1139 : {
1140 0 : struct pim_ifchannel *ch, *starch;
1141 0 : struct pim_interface *pim_ifp;
1142 0 : struct pim_instance *pim;
1143 0 : int up_flags;
1144 :
1145 : /* PIM enabled on interface? */
1146 0 : pim_ifp = ifp->info;
1147 0 : if (!pim_ifp) {
1148 0 : if (PIM_DEBUG_EVENTS)
1149 0 : zlog_debug("%s:%pSG Expected pim interface setup for %s",
1150 : __func__, sg, ifp->name);
1151 0 : return 0;
1152 : }
1153 :
1154 0 : if (!pim_ifp->pim_enable) {
1155 0 : if (PIM_DEBUG_EVENTS)
1156 0 : zlog_debug("%s:%pSG PIM is not configured on this interface %s",
1157 : __func__, sg, ifp->name);
1158 0 : return 0;
1159 : }
1160 :
1161 0 : pim = pim_ifp->pim;
1162 :
1163 : /* skip (*,G) ch creation if G is of type SSM */
1164 0 : if (pim_addr_is_any(sg->src)) {
1165 0 : if (pim_is_grp_ssm(pim, sg->grp)) {
1166 0 : if (PIM_DEBUG_PIM_EVENTS)
1167 0 : zlog_debug("%s: local membership (S,G)=%pSG ignored as group is SSM",
1168 : __func__, sg);
1169 0 : return 1;
1170 : }
1171 : }
1172 :
1173 : /* vxlan term mroutes use ipmr-lo as local member to
1174 : * pull down multicast vxlan tunnel traffic
1175 : */
1176 0 : up_flags = is_vxlan ? PIM_UPSTREAM_FLAG_MASK_SRC_VXLAN_TERM :
1177 : PIM_UPSTREAM_FLAG_MASK_SRC_IGMP;
1178 0 : ch = pim_ifchannel_add(ifp, sg, 0, up_flags);
1179 :
1180 0 : ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
1181 :
1182 0 : if (pim_addr_is_any(sg->src)) {
1183 0 : struct pim_upstream *up = pim_upstream_find(pim, sg);
1184 0 : struct pim_upstream *child;
1185 0 : struct listnode *up_node;
1186 :
1187 0 : starch = ch;
1188 :
1189 0 : for (ALL_LIST_ELEMENTS_RO(up->sources, up_node, child)) {
1190 0 : if (PIM_DEBUG_EVENTS)
1191 0 : zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1192 : __FILE__, __func__, child->sg_str,
1193 : ifp->name, up->sg_str);
1194 :
1195 0 : if (!child->rpf.source_nexthop.interface) {
1196 : /* when iif unknown, do not inherit */
1197 0 : if (PIM_DEBUG_EVENTS)
1198 0 : zlog_debug(
1199 : "Skipped (S,G)=%s(%s) from %s: no iif",
1200 : child->sg_str, ifp->name,
1201 : up->sg_str);
1202 0 : continue;
1203 : }
1204 :
1205 0 : ch = pim_ifchannel_find(ifp, &child->sg);
1206 0 : if (pim_upstream_evaluate_join_desired_interface(
1207 : child, ch, starch)) {
1208 0 : pim_channel_add_oif(child->channel_oil, ifp,
1209 : PIM_OIF_FLAG_PROTO_STAR,
1210 : __func__);
1211 0 : pim_upstream_update_join_desired(pim, child);
1212 : }
1213 : }
1214 :
1215 0 : if (pim->spt.switchover == PIM_SPT_INFINITY) {
1216 0 : if (pim->spt.plist) {
1217 0 : struct prefix_list *plist = prefix_list_lookup(
1218 : AFI_IP, pim->spt.plist);
1219 0 : struct prefix g;
1220 :
1221 0 : pim_addr_to_prefix(&g, up->sg.grp);
1222 0 : if (prefix_list_apply_ext(plist, NULL, &g,
1223 : true) ==
1224 : PREFIX_DENY) {
1225 0 : pim_channel_add_oif(
1226 : up->channel_oil, pim->regiface,
1227 : PIM_OIF_FLAG_PROTO_GM,
1228 : __func__);
1229 : }
1230 : }
1231 : } else
1232 0 : pim_channel_add_oif(up->channel_oil, pim->regiface,
1233 : PIM_OIF_FLAG_PROTO_GM, __func__);
1234 : }
1235 :
1236 : return 1;
1237 : }
1238 :
1239 0 : void pim_ifchannel_local_membership_del(struct interface *ifp, pim_sgaddr *sg)
1240 : {
1241 0 : struct pim_ifchannel *starch, *ch, *orig;
1242 0 : struct pim_interface *pim_ifp;
1243 :
1244 : /* PIM enabled on interface? */
1245 0 : pim_ifp = ifp->info;
1246 0 : if (!pim_ifp)
1247 : return;
1248 0 : if (!pim_ifp->pim_enable)
1249 : return;
1250 :
1251 0 : orig = ch = pim_ifchannel_find(ifp, sg);
1252 0 : if (!ch)
1253 : return;
1254 0 : ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
1255 :
1256 0 : if (pim_addr_is_any(sg->src)) {
1257 0 : struct pim_upstream *up = pim_upstream_find(pim_ifp->pim, sg);
1258 0 : struct pim_upstream *child;
1259 0 : struct listnode *up_node, *up_nnode;
1260 :
1261 0 : starch = ch;
1262 :
1263 0 : for (ALL_LIST_ELEMENTS(up->sources, up_node, up_nnode, child)) {
1264 0 : struct channel_oil *c_oil = child->channel_oil;
1265 0 : struct pim_ifchannel *chchannel =
1266 0 : pim_ifchannel_find(ifp, &child->sg);
1267 :
1268 0 : pim_ifp = ifp->info;
1269 :
1270 0 : if (PIM_DEBUG_EVENTS)
1271 0 : zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1272 : __FILE__, __func__, up->sg_str,
1273 : ifp->name, child->sg_str);
1274 :
1275 0 : ch = pim_ifchannel_find(ifp, &child->sg);
1276 : /*
1277 : * If the S,G has no if channel and the c_oil still
1278 : * has output here then the *,G was supplying the
1279 : * implied
1280 : * if channel. So remove it.
1281 : */
1282 0 : if (!pim_upstream_evaluate_join_desired_interface(
1283 0 : child, ch, starch) ||
1284 0 : (!chchannel &&
1285 0 : oil_if_has(c_oil, pim_ifp->mroute_vif_index))) {
1286 0 : pim_channel_del_inherited_oif(c_oil, ifp,
1287 : __func__);
1288 : }
1289 :
1290 : /* Child node removal/ref count-- will happen as part of
1291 : * parent' delete_no_info */
1292 : }
1293 : }
1294 :
1295 : /* Resettng the IGMP flags here */
1296 0 : if (orig->upstream)
1297 0 : PIM_UPSTREAM_FLAG_UNSET_SRC_IGMP(orig->upstream->flags);
1298 :
1299 0 : PIM_IF_FLAG_UNSET_PROTO_IGMP(orig->flags);
1300 :
1301 0 : delete_on_noinfo(orig);
1302 : }
1303 :
1304 0 : void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1305 : {
1306 0 : int old_couldassert =
1307 0 : PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1308 0 : int new_couldassert =
1309 0 : PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1310 :
1311 0 : if (new_couldassert == old_couldassert)
1312 : return;
1313 :
1314 0 : if (PIM_DEBUG_PIM_EVENTS)
1315 0 : zlog_debug("%s: CouldAssert(%pPAs,%pPAs,%s) changed from %d to %d",
1316 : __func__, &ch->sg.src, &ch->sg.grp,
1317 : ch->interface->name, old_couldassert,
1318 : new_couldassert);
1319 :
1320 0 : if (new_couldassert) {
1321 : /* CouldAssert(S,G,I) switched from false to true */
1322 0 : PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1323 : } else {
1324 : /* CouldAssert(S,G,I) switched from true to false */
1325 0 : PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1326 :
1327 0 : if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1328 0 : assert_action_a4(ch);
1329 : }
1330 : }
1331 :
1332 0 : pim_ifchannel_update_my_assert_metric(ch);
1333 : }
1334 :
1335 : /*
1336 : my_assert_metric may be affected by:
1337 :
1338 : CouldAssert(S,G)
1339 : pim_ifp->primary_address
1340 : rpf->source_nexthop.mrib_metric_preference;
1341 : rpf->source_nexthop.mrib_route_metric;
1342 : */
1343 0 : void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
1344 : {
1345 0 : struct pim_assert_metric my_metric_new =
1346 0 : pim_macro_ch_my_assert_metric_eval(ch);
1347 :
1348 0 : if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1349 0 : return;
1350 :
1351 0 : if (PIM_DEBUG_PIM_EVENTS)
1352 0 : zlog_debug(
1353 : "%s: my_assert_metric(%pPAs,%pPAs,%s) changed from %u,%u,%u,%pPAs to %u,%u,%u,%pPAs",
1354 : __func__, &ch->sg.src, &ch->sg.grp, ch->interface->name,
1355 : ch->ifassert_my_metric.rpt_bit_flag,
1356 : ch->ifassert_my_metric.metric_preference,
1357 : ch->ifassert_my_metric.route_metric,
1358 : &ch->ifassert_my_metric.ip_address,
1359 : my_metric_new.rpt_bit_flag,
1360 : my_metric_new.metric_preference,
1361 : my_metric_new.route_metric, &my_metric_new.ip_address);
1362 :
1363 0 : ch->ifassert_my_metric = my_metric_new;
1364 :
1365 0 : if (pim_assert_metric_better(&ch->ifassert_my_metric,
1366 0 : &ch->ifassert_winner_metric)) {
1367 0 : assert_action_a5(ch);
1368 : }
1369 : }
1370 :
1371 0 : void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1372 : {
1373 0 : int old_atd = PIM_FORCE_BOOLEAN(
1374 : PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1375 0 : int new_atd =
1376 0 : PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1377 :
1378 0 : if (new_atd == old_atd)
1379 : return;
1380 :
1381 0 : if (PIM_DEBUG_PIM_EVENTS)
1382 0 : zlog_debug(
1383 : "%s: AssertTrackingDesired(%pPAs,%pPAs,%s) changed from %d to %d",
1384 : __func__, &ch->sg.src, &ch->sg.grp, ch->interface->name,
1385 : old_atd, new_atd);
1386 :
1387 0 : if (new_atd) {
1388 : /* AssertTrackingDesired(S,G,I) switched from false to true */
1389 0 : PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1390 : } else {
1391 : /* AssertTrackingDesired(S,G,I) switched from true to false */
1392 0 : PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1393 :
1394 0 : if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1395 0 : assert_action_a5(ch);
1396 : }
1397 : }
1398 : }
1399 :
1400 : /*
1401 : * If we have a new pim interface, check to
1402 : * see if any of the pre-existing channels have
1403 : * their upstream out that way and turn on forwarding
1404 : * for that ifchannel then.
1405 : */
1406 85 : void pim_ifchannel_scan_forward_start(struct interface *new_ifp)
1407 : {
1408 85 : struct pim_interface *new_pim_ifp = new_ifp->info;
1409 85 : struct pim_instance *pim = new_pim_ifp->pim;
1410 85 : struct interface *ifp;
1411 :
1412 492 : FOR_ALL_INTERFACES (pim->vrf, ifp) {
1413 322 : struct pim_interface *loop_pim_ifp = ifp->info;
1414 322 : struct pim_ifchannel *ch;
1415 :
1416 322 : if (!loop_pim_ifp)
1417 79 : continue;
1418 :
1419 243 : if (new_pim_ifp == loop_pim_ifp)
1420 85 : continue;
1421 :
1422 316 : RB_FOREACH (ch, pim_ifchannel_rb, &loop_pim_ifp->ifchannel_rb) {
1423 0 : if (ch->ifjoin_state == PIM_IFJOIN_JOIN) {
1424 0 : struct pim_upstream *up = ch->upstream;
1425 0 : if ((!up->channel_oil)
1426 0 : && (up->rpf.source_nexthop
1427 0 : .interface == new_ifp))
1428 0 : pim_forward_start(ch);
1429 : }
1430 : }
1431 : }
1432 85 : }
1433 :
1434 : /*
1435 : * Downstream per-interface (S,G,rpt) state machine
1436 : * states that we need to move (S,G,rpt) items
1437 : * into different states at the start of the
1438 : * reception of a *,G join as well, when
1439 : * we get End of Message
1440 : */
1441 0 : void pim_ifchannel_set_star_g_join_state(struct pim_ifchannel *ch, int eom,
1442 : uint8_t join)
1443 : {
1444 0 : bool send_upstream_starg = false;
1445 0 : struct pim_ifchannel *child;
1446 0 : struct listnode *ch_node, *nch_node;
1447 0 : struct pim_instance *pim =
1448 0 : ((struct pim_interface *)ch->interface->info)->pim;
1449 0 : struct pim_upstream *starup = ch->upstream;
1450 :
1451 0 : if (PIM_DEBUG_PIM_TRACE)
1452 0 : zlog_debug(
1453 : "%s: %s %s eom: %d join %u", __func__,
1454 : pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
1455 : ch->sg_str, eom, join);
1456 0 : if (!ch->sources)
1457 : return;
1458 :
1459 0 : for (ALL_LIST_ELEMENTS(ch->sources, ch_node, nch_node, child)) {
1460 0 : if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1461 0 : continue;
1462 :
1463 0 : switch (child->ifjoin_state) {
1464 : case PIM_IFJOIN_NOINFO:
1465 : case PIM_IFJOIN_JOIN:
1466 : break;
1467 0 : case PIM_IFJOIN_PRUNE:
1468 0 : if (!eom)
1469 0 : child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1470 : break;
1471 0 : case PIM_IFJOIN_PRUNE_PENDING:
1472 0 : if (!eom)
1473 0 : child->ifjoin_state =
1474 : PIM_IFJOIN_PRUNE_PENDING_TMP;
1475 : break;
1476 0 : case PIM_IFJOIN_PRUNE_TMP:
1477 : case PIM_IFJOIN_PRUNE_PENDING_TMP:
1478 0 : if (!eom)
1479 : break;
1480 :
1481 0 : if (child->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING_TMP)
1482 0 : THREAD_OFF(child->t_ifjoin_prune_pending_timer);
1483 0 : THREAD_OFF(child->t_ifjoin_expiry_timer);
1484 :
1485 0 : PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
1486 0 : child->ifjoin_state = PIM_IFJOIN_NOINFO;
1487 :
1488 0 : if ((I_am_RP(pim, child->sg.grp)) &&
1489 0 : (!pim_upstream_empty_inherited_olist(
1490 : child->upstream))) {
1491 0 : pim_channel_add_oif(
1492 0 : child->upstream->channel_oil,
1493 : ch->interface, PIM_OIF_FLAG_PROTO_STAR,
1494 : __func__);
1495 0 : pim_upstream_update_join_desired(pim,
1496 : child->upstream);
1497 : }
1498 0 : send_upstream_starg = true;
1499 :
1500 0 : delete_on_noinfo(child);
1501 0 : break;
1502 : }
1503 : }
1504 :
1505 0 : if (send_upstream_starg)
1506 0 : pim_jp_agg_single_upstream_send(&starup->rpf, starup, true);
1507 : }
|