]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_ifchannel.c
pimd: Fix connected data structure not being freed
[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 599 if (send_prune_echo)
b5e2377c
DS
600 {
601 struct pim_rpf rpf;
602
603 rpf.source_nexthop.interface = ifp;
604 rpf.rpf_addr.u.prefix4 = pim_ifp->primary_address;
982bff89 605 pim_jp_agg_single_upstream_send(&rpf, ch->upstream, 0);
b5e2377c 606 }
1e3a5132
DS
607 }
608 else
609 {
610 zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
611 __PRETTY_FUNCTION__, pim_str_sg_dump (&ch->sg),
612 pim_ifchannel_ifjoin_name (ch->ifjoin_state));
613 }
12e41d03
DL
614
615 return 0;
616}
617
618static void check_recv_upstream(int is_join,
619 struct interface *recv_ifp,
620 struct in_addr upstream,
4ed0af70 621 struct prefix_sg *sg,
12e41d03
DL
622 uint8_t source_flags,
623 int holdtime)
624{
625 struct pim_upstream *up;
626
627 /* Upstream (S,G) in Joined state ? */
01d45d04 628 up = pim_upstream_find(sg);
12e41d03
DL
629 if (!up)
630 return;
631 if (up->join_state != PIM_UPSTREAM_JOINED)
632 return;
633
634 /* Upstream (S,G) in Joined state */
635
63c59d0c 636 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
12e41d03 637 /* RPF'(S,G) not found */
01d45d04 638 zlog_warn("%s %s: RPF'%s not found",
12e41d03 639 __FILE__, __PRETTY_FUNCTION__,
8bfb8b67 640 up->sg_str);
12e41d03
DL
641 return;
642 }
643
644 /* upstream directed to RPF'(S,G) ? */
63c59d0c 645 if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) {
eaa54bdb
DW
646 char up_str[INET_ADDRSTRLEN];
647 char rpf_str[PREFIX_STRLEN];
12e41d03 648 pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
63c59d0c 649 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
01d45d04 650 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
12e41d03 651 __FILE__, __PRETTY_FUNCTION__,
8bfb8b67 652 up->sg_str,
12e41d03
DL
653 up_str, rpf_str, recv_ifp->name);
654 return;
655 }
656 /* upstream directed to RPF'(S,G) */
657
658 if (is_join) {
659 /* Join(S,G) to RPF'(S,G) */
63c59d0c 660 pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4, holdtime);
12e41d03
DL
661 return;
662 }
663
664 /* Prune to RPF'(S,G) */
665
666 if (source_flags & PIM_RPT_BIT_MASK) {
667 if (source_flags & PIM_WILDCARD_BIT_MASK) {
668 /* Prune(*,G) to RPF'(S,G) */
c48a612c 669 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", up);
12e41d03
DL
670 return;
671 }
672
673 /* Prune(S,G,rpt) to RPF'(S,G) */
c48a612c 674 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", up);
12e41d03
DL
675 return;
676 }
677
678 /* Prune(S,G) to RPF'(S,G) */
c48a612c 679 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
12e41d03
DL
680}
681
116b989a
DS
682static int
683nonlocal_upstream(int is_join,
684 struct interface *recv_ifp,
685 struct in_addr upstream,
686 struct prefix_sg *sg,
687 uint8_t source_flags,
688 uint16_t holdtime)
12e41d03
DL
689{
690 struct pim_interface *recv_pim_ifp;
691 int is_local; /* boolean */
692
693 recv_pim_ifp = recv_ifp->info;
694 zassert(recv_pim_ifp);
695
696 is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
116b989a
DS
697
698 if (is_local)
699 return 0;
700
a770ef90 701 if (PIM_DEBUG_PIM_TRACE_DETAIL) {
eaa54bdb 702 char up_str[INET_ADDRSTRLEN];
12e41d03 703 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
116b989a
DS
704 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
705 __PRETTY_FUNCTION__,
706 is_join ? "join" : "prune",
707 pim_str_sg_dump (sg),
708 up_str, recv_ifp->name);
12e41d03
DL
709 }
710
12e41d03 711 /*
116b989a
DS
712 * Since recv upstream addr was not directed to our primary
713 * address, check if we should react to it in any way.
714 */
01d45d04 715 check_recv_upstream(is_join, recv_ifp, upstream, sg,
116b989a 716 source_flags, holdtime);
12e41d03
DL
717
718 return 1; /* non-local */
719}
720
721void pim_ifchannel_join_add(struct interface *ifp,
722 struct in_addr neigh_addr,
723 struct in_addr upstream,
4ed0af70 724 struct prefix_sg *sg,
12e41d03
DL
725 uint8_t source_flags,
726 uint16_t holdtime)
727{
728 struct pim_interface *pim_ifp;
729 struct pim_ifchannel *ch;
730
731 if (nonlocal_upstream(1 /* join */, ifp, upstream,
01d45d04 732 sg, source_flags, holdtime)) {
12e41d03
DL
733 return;
734 }
735
4a40c37a 736 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
12e41d03
DL
737 if (!ch)
738 return;
739
740 /*
741 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
742
743 Transitions from "I am Assert Loser" State
744
745 Receive Join(S,G) on Interface I
746
747 We receive a Join(S,G) that has the Upstream Neighbor Address
748 field set to my primary IP address on interface I. The action is
749 to transition to NoInfo state, delete this (S,G) assert state
750 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
751 to operate.
752
753 Notice: The nonlocal_upstream() test above ensures the upstream
754 address of the join message is our primary address.
755 */
756 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
eaa54bdb 757 char neigh_str[INET_ADDRSTRLEN];
12e41d03 758 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
5074a423 759 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
12e41d03 760 __PRETTY_FUNCTION__,
8bfb8b67 761 ch->sg_str, neigh_str, ifp->name);
12e41d03
DL
762
763 assert_action_a5(ch);
764 }
765
766 pim_ifp = ifp->info;
767 zassert(pim_ifp);
768
769 switch (ch->ifjoin_state) {
770 case PIM_IFJOIN_NOINFO:
771 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
772 if (pim_macro_chisin_oiflist(ch)) {
5b668dd7 773 pim_upstream_inherited_olist (ch->upstream);
12e41d03
DL
774 pim_forward_start(ch);
775 }
776 break;
777 case PIM_IFJOIN_JOIN:
778 zassert(!ch->t_ifjoin_prune_pending_timer);
779
780 /*
781 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
782 previously received join message with holdtime=0xFFFF.
783 */
784 if (ch->t_ifjoin_expiry_timer) {
785 unsigned long remain =
786 thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
787 if (remain > holdtime) {
788 /*
789 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
790
791 Transitions from Join State
792
793 The (S,G) downstream state machine on interface I remains in
794 Join state, and the Expiry Timer (ET) is restarted, set to
795 maximum of its current value and the HoldTime from the
796 triggering Join/Prune message.
797
798 Conclusion: Do not change the ET if the current value is
799 higher than the received join holdtime.
800 */
801 return;
802 }
803 }
804 THREAD_OFF(ch->t_ifjoin_expiry_timer);
805 break;
be4791f2 806 case PIM_IFJOIN_PRUNE:
220d8a49
DS
807 if (source_flags & PIM_ENCODE_RPT_BIT)
808 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
be4791f2 809 break;
12e41d03 810 case PIM_IFJOIN_PRUNE_PENDING:
1e3a5132 811 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
220d8a49 812 if (source_flags & PIM_ENCODE_RPT_BIT)
220d8a49 813 {
1e3a5132
DS
814 THREAD_OFF(ch->t_ifjoin_expiry_timer);
815 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
220d8a49 816 }
1e3a5132
DS
817 else
818 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
12e41d03 819 break;
be4791f2 820 case PIM_IFJOIN_PRUNE_TMP:
be4791f2
DS
821 break;
822 case PIM_IFJOIN_PRUNE_PENDING_TMP:
be4791f2 823 break;
12e41d03
DL
824 }
825
12e41d03
DL
826 if (holdtime != 0xFFFF) {
827 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
828 on_ifjoin_expiry_timer,
829 ch, holdtime);
830 }
831}
832
833void pim_ifchannel_prune(struct interface *ifp,
834 struct in_addr upstream,
4ed0af70 835 struct prefix_sg *sg,
12e41d03
DL
836 uint8_t source_flags,
837 uint16_t holdtime)
838{
839 struct pim_ifchannel *ch;
1405c852 840 struct pim_interface *pim_ifp;
12e41d03
DL
841 int jp_override_interval_msec;
842
843 if (nonlocal_upstream(0 /* prune */, ifp, upstream,
01d45d04 844 sg, source_flags, holdtime)) {
12e41d03
DL
845 return;
846 }
847
df90067a
DS
848 ch = pim_ifchannel_find (ifp, sg);
849 if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT))
850 {
851 if (PIM_DEBUG_TRACE)
852 zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
853 __PRETTY_FUNCTION__, ifp->name, pim_str_sg_dump (sg), source_flags);
854 return;
855 }
856
4a40c37a 857 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
12e41d03
DL
858 if (!ch)
859 return;
860
1405c852
DS
861 pim_ifp = ifp->info;
862
12e41d03
DL
863 switch (ch->ifjoin_state) {
864 case PIM_IFJOIN_NOINFO:
1405c852
DS
865 if (source_flags & PIM_ENCODE_RPT_BIT)
866 {
867 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
868 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
869 if (listcount(pim_ifp->pim_neighbor_list) > 1)
870 jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
871 else
872 jp_override_interval_msec = 0; /* schedule to expire immediately */
873 /* If we called ifjoin_prune() directly instead, care should
874 be taken not to use "ch" afterwards since it would be
875 deleted. */
876
877 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
878 THREAD_OFF(ch->t_ifjoin_expiry_timer);
879 THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
880 on_ifjoin_prune_pending_timer,
881 ch, jp_override_interval_msec);
882 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
883 on_ifjoin_expiry_timer,
884 ch, holdtime);
885 }
886 break;
12e41d03
DL
887 case PIM_IFJOIN_PRUNE_PENDING:
888 /* nothing to do */
889 break;
890 case PIM_IFJOIN_JOIN:
1405c852 891 THREAD_OFF(ch->t_ifjoin_expiry_timer);
12e41d03 892
1405c852 893 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
12e41d03 894
1405c852
DS
895 if (listcount(pim_ifp->pim_neighbor_list) > 1)
896 jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
897 else
898 jp_override_interval_msec = 0; /* schedule to expire immediately */
899 /* If we called ifjoin_prune() directly instead, care should
900 be taken not to use "ch" afterwards since it would be
901 deleted. */
902 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
903 THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
904 on_ifjoin_prune_pending_timer,
905 ch, jp_override_interval_msec);
12e41d03 906 break;
be4791f2 907 case PIM_IFJOIN_PRUNE:
1405c852
DS
908 if (source_flags & PIM_ENCODE_RPT_BIT)
909 {
910 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
911 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
912 on_ifjoin_expiry_timer,
913 ch, holdtime);
914 }
915 break;
be4791f2 916 case PIM_IFJOIN_PRUNE_TMP:
1405c852
DS
917 if (source_flags & PIM_ENCODE_RPT_BIT)
918 {
919 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
920 THREAD_OFF(ch->t_ifjoin_expiry_timer);
921 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
922 on_ifjoin_expiry_timer,
923 ch, holdtime);
924 }
925 break;
be4791f2 926 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1405c852
DS
927 if (source_flags & PIM_ENCODE_RPT_BIT)
928 {
929 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
930 THREAD_OFF(ch->t_ifjoin_expiry_timer);
931 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
932 on_ifjoin_expiry_timer,
933 ch, holdtime);
934 }
be4791f2 935 break;
12e41d03 936 }
12e41d03
DL
937}
938
f663aa7a
DS
939int
940pim_ifchannel_local_membership_add(struct interface *ifp,
941 struct prefix_sg *sg)
12e41d03
DL
942{
943 struct pim_ifchannel *ch;
944 struct pim_interface *pim_ifp;
945
946 /* PIM enabled on interface? */
947 pim_ifp = ifp->info;
948 if (!pim_ifp)
f663aa7a 949 return 0;
12e41d03 950 if (!PIM_IF_TEST_PIM(pim_ifp->options))
f663aa7a 951 return 0;
12e41d03 952
4a40c37a 953 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP);
12e41d03 954 if (!ch) {
f663aa7a 955 return 0;
12e41d03
DL
956 }
957
958 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
959
dfbbce1d
DS
960 if (sg->src.s_addr == INADDR_ANY)
961 {
962 struct pim_upstream *up = pim_upstream_find (sg);
963 struct pim_upstream *child;
964 struct listnode *up_node;
965
03417ccd 966 for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
dfbbce1d 967 {
03417ccd 968 if (PIM_DEBUG_EVENTS)
8bfb8b67
DS
969 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
970 __FILE__, __PRETTY_FUNCTION__,
971 child->sg_str, ifp->name, up->sg_str);
03417ccd 972
9f44d042 973 if (pim_upstream_evaluate_join_desired_interface (child, ch))
03417ccd 974 {
781a1745 975 pim_channel_add_oif (child->channel_oil, ifp, PIM_OIF_FLAG_PROTO_STAR);
03417ccd
DS
976 pim_upstream_switch (child, PIM_UPSTREAM_JOINED);
977 }
dfbbce1d
DS
978 }
979 }
f663aa7a
DS
980
981 return 1;
12e41d03
DL
982}
983
984void pim_ifchannel_local_membership_del(struct interface *ifp,
4ed0af70 985 struct prefix_sg *sg)
12e41d03
DL
986{
987 struct pim_ifchannel *ch;
988 struct pim_interface *pim_ifp;
989
990 /* PIM enabled on interface? */
991 pim_ifp = ifp->info;
992 if (!pim_ifp)
993 return;
994 if (!PIM_IF_TEST_PIM(pim_ifp->options))
995 return;
996
1103466b 997 ch = pim_ifchannel_find(ifp, sg);
12e41d03
DL
998 if (!ch)
999 return;
1000
1001 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
1002
dfbbce1d
DS
1003 if (sg->src.s_addr == INADDR_ANY)
1004 {
1005 struct pim_upstream *up = pim_upstream_find (sg);
1006 struct pim_upstream *child;
1007 struct listnode *up_node;
1008
03417ccd 1009 for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
dfbbce1d 1010 {
03417ccd
DS
1011 struct channel_oil *c_oil = child->channel_oil;
1012 struct pim_ifchannel *chchannel = pim_ifchannel_find (ifp, &child->sg);
1013 struct pim_interface *pim_ifp = ifp->info;
1014
1015 if (PIM_DEBUG_EVENTS)
8bfb8b67
DS
1016 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1017 __FILE__, __PRETTY_FUNCTION__,
1018 up->sg_str, ifp->name, child->sg_str);
03417ccd 1019
9f44d042 1020 if (c_oil && !pim_upstream_evaluate_join_desired_interface (child, ch))
781a1745 1021 pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_STAR);
03417ccd
DS
1022
1023 /*
1024 * If the S,G has no if channel and the c_oil still
1025 * has output here then the *,G was supplying the implied
1026 * if channel. So remove it.
1027 */
b72f79a9 1028 if (!chchannel && c_oil && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
781a1745 1029 pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_STAR);
dfbbce1d
DS
1030 }
1031 }
12e41d03
DL
1032 delete_on_noinfo(ch);
1033}
1034
1035void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1036{
1037 int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1038 int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1039
1040 if (new_couldassert == old_couldassert)
1041 return;
1042
1043 if (PIM_DEBUG_PIM_EVENTS) {
eaa54bdb
DW
1044 char src_str[INET_ADDRSTRLEN];
1045 char grp_str[INET_ADDRSTRLEN];
4ed0af70
DS
1046 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1047 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
1048 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1049 __PRETTY_FUNCTION__,
1050 src_str, grp_str, ch->interface->name,
1051 old_couldassert, new_couldassert);
1052 }
1053
1054 if (new_couldassert) {
1055 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1056 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1057 }
1058 else {
1059 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1060 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1061
1062 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1063 assert_action_a4(ch);
1064 }
1065 }
1066
1067 pim_ifchannel_update_my_assert_metric(ch);
1068}
1069
1070/*
1071 my_assert_metric may be affected by:
1072
1073 CouldAssert(S,G)
1074 pim_ifp->primary_address
1075 rpf->source_nexthop.mrib_metric_preference;
1076 rpf->source_nexthop.mrib_route_metric;
1077 */
1078void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
1079{
1080 struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
1081
1082 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1083 return;
1084
1085 if (PIM_DEBUG_PIM_EVENTS) {
eaa54bdb
DW
1086 char src_str[INET_ADDRSTRLEN];
1087 char grp_str[INET_ADDRSTRLEN];
1088 char old_addr_str[INET_ADDRSTRLEN];
1089 char new_addr_str[INET_ADDRSTRLEN];
4ed0af70
DS
1090 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1091 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
1092 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
1093 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
1094 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1095 __PRETTY_FUNCTION__,
1096 src_str, grp_str, ch->interface->name,
1097 ch->ifassert_my_metric.rpt_bit_flag,
1098 ch->ifassert_my_metric.metric_preference,
1099 ch->ifassert_my_metric.route_metric,
1100 old_addr_str,
1101 my_metric_new.rpt_bit_flag,
1102 my_metric_new.metric_preference,
1103 my_metric_new.route_metric,
1104 new_addr_str);
1105 }
1106
1107 ch->ifassert_my_metric = my_metric_new;
1108
1109 if (pim_assert_metric_better(&ch->ifassert_my_metric,
1110 &ch->ifassert_winner_metric)) {
1111 assert_action_a5(ch);
1112 }
1113}
1114
1115void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1116{
1117 int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1118 int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1119
1120 if (new_atd == old_atd)
1121 return;
1122
1123 if (PIM_DEBUG_PIM_EVENTS) {
eaa54bdb
DW
1124 char src_str[INET_ADDRSTRLEN];
1125 char grp_str[INET_ADDRSTRLEN];
4ed0af70
DS
1126 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1127 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
1128 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1129 __PRETTY_FUNCTION__,
1130 src_str, grp_str, ch->interface->name,
1131 old_atd, new_atd);
1132 }
1133
1134 if (new_atd) {
1135 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1136 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1137 }
1138 else {
1139 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1140 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1141
1142 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1143 assert_action_a5(ch);
1144 }
1145 }
1146}
c8507a16
DS
1147
1148/*
1149 * If we have a new pim interface, check to
1150 * see if any of the pre-existing channels have
1151 * their upstream out that way and turn on forwarding
1152 * for that ifchannel then.
1153 */
1154void
1155pim_ifchannel_scan_forward_start (struct interface *new_ifp)
1156{
1157 struct listnode *ifnode;
1158 struct interface *ifp;
1159 struct pim_interface *new_pim_ifp = new_ifp->info;
1160
1161 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp))
1162 {
1163 struct pim_interface *loop_pim_ifp = ifp->info;
1164 struct listnode *ch_node;
1165 struct pim_ifchannel *ch;
1166
1167 if (!loop_pim_ifp)
1168 continue;
1169
1170 if (new_pim_ifp == loop_pim_ifp)
1171 continue;
1172
1173 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp->pim_ifchannel_list, ch_node, ch))
1174 {
1175 if (ch->ifjoin_state == PIM_IFJOIN_JOIN)
1176 {
1177 struct pim_upstream *up = ch->upstream;
1178 if ((!up->channel_oil) &&
1179 (up->rpf.source_nexthop.interface == new_ifp))
1180 pim_forward_start (ch);
1181 }
1182 }
1183 }
1184}
220d8a49
DS
1185
1186/*
1187 * Downstream per-interface (S,G,rpt) state machine
1188 * states that we need to move (S,G,rpt) items
1189 * into different states at the start of the
1190 * reception of a *,G join as well, when
1191 * we get End of Message
1192 */
1193void
1194pim_ifchannel_set_star_g_join_state (struct pim_ifchannel *ch, int eom)
1195{
1196 struct pim_ifchannel *child;
1197 struct listnode *ch_node;
1198
1199 if (PIM_DEBUG_PIM_TRACE)
1200 zlog_debug ("%s: %s %s eom: %d", __PRETTY_FUNCTION__,
1201 pim_ifchannel_ifjoin_name(ch->ifjoin_state),
8bfb8b67 1202 ch->sg_str, eom);
220d8a49
DS
1203 if (!ch->sources)
1204 return;
1205
1206 for (ALL_LIST_ELEMENTS_RO (ch->sources, ch_node, child))
1207 {
1208 if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1209 continue;
1210
1211 switch (child->ifjoin_state)
1405c852
DS
1212 {
1213 case PIM_IFJOIN_NOINFO:
1214 case PIM_IFJOIN_JOIN:
1215 break;
1216 case PIM_IFJOIN_PRUNE:
1217 if (!eom)
1218 child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1219 break;
1220 case PIM_IFJOIN_PRUNE_PENDING:
1221 if (!eom)
1222 child->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING_TMP;
1223 break;
1224 case PIM_IFJOIN_PRUNE_TMP:
1225 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1226 if (eom)
1227 child->ifjoin_state = PIM_IFJOIN_NOINFO;
1228 break;
1229 }
220d8a49
DS
1230 }
1231}
a625e937
DS
1232
1233unsigned int
1234pim_ifchannel_hash_key (void *arg)
1235{
1236 struct pim_ifchannel *ch = (struct pim_ifchannel *)arg;
1237
1238 return jhash_2words (ch->sg.src.s_addr, ch->sg.grp.s_addr, 0);
1239}
1240
1241int
1242pim_ifchannel_equal (const void *arg1, const void *arg2)
1243{
1244 const struct pim_ifchannel *ch1 = (const struct pim_ifchannel *)arg1;
1245 const struct pim_ifchannel *ch2 = (const struct pim_ifchannel *)arg2;
1246
1247 if ((ch1->sg.grp.s_addr == ch2->sg.grp.s_addr) &&
1248 (ch1->sg.src.s_addr == ch2->sg.src.s_addr))
1249 return 1;
1250
1251 return 0;
1252}