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