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