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