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