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