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