]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_ifchannel.c
Merge pull request #561 from donaldsharp/static_config2
[mirror_frr.git] / pimd / pim_ifchannel.c
CommitLineData
12e41d03 1/*
896014f4
DL
2 * PIM for Quagga
3 * Copyright (C) 2008 Everton da Silva Marques
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
12e41d03
DL
19
20#include <zebra.h>
21
22#include "linklist.h"
23#include "thread.h"
24#include "memory.h"
744d91b3 25#include "if.h"
c8507a16 26#include "vrf.h"
a625e937
DS
27#include "hash.h"
28#include "jhash.h"
df94f9a9 29#include "prefix.h"
12e41d03
DL
30
31#include "pimd.h"
32#include "pim_str.h"
33#include "pim_iface.h"
34#include "pim_ifchannel.h"
35#include "pim_zebra.h"
36#include "pim_time.h"
37#include "pim_msg.h"
38#include "pim_pim.h"
39#include "pim_join.h"
40#include "pim_rpf.h"
41#include "pim_macro.h"
dfbbce1d 42#include "pim_oil.h"
4a40c37a 43#include "pim_upstream.h"
15a5dafe 44#include "pim_ssm.h"
12e41d03 45
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
e3a55f03
DS
52 pim_ifp1 = ch1->interface->info;
53 pim_ifp2 = ch2->interface->info;
3fdfd943 54
e3a55f03 55 if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index)
3fdfd943
DS
56 return -1;
57
e3a55f03 58 if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index)
3fdfd943
DS
59 return 1;
60
e3a55f03 61 if (ntohl(ch1->sg.grp.s_addr) < ntohl(ch2->sg.grp.s_addr))
3fdfd943
DS
62 return -1;
63
e3a55f03 64 if (ntohl(ch1->sg.grp.s_addr) > ntohl(ch2->sg.grp.s_addr))
3fdfd943
DS
65 return 1;
66
e3a55f03 67 if (ntohl(ch1->sg.src.s_addr) < ntohl(ch2->sg.src.s_addr))
3fdfd943
DS
68 return -1;
69
e3a55f03 70 if (ntohl(ch1->sg.src.s_addr) > ntohl(ch2->sg.src.s_addr))
3fdfd943
DS
71 return 1;
72
73 return 0;
74}
75
1a10fc74
DS
76/*
77 * A (*,G) or a (*,*) is going away
78 * remove the parent pointer from
79 * those pointing at us
80 */
81static void
82pim_ifchannel_remove_children (struct pim_ifchannel *ch)
83{
1a10fc74
DS
84 struct pim_ifchannel *child;
85
3fdfd943 86 if (!ch->sources)
1a10fc74
DS
87 return;
88
3fdfd943 89 while (!list_isempty (ch->sources))
1a10fc74 90 {
3fdfd943
DS
91 child = listnode_head (ch->sources);
92 child->parent = NULL;
93 listnode_delete (ch->sources, child);
1a10fc74
DS
94 }
95}
96
97/*
98 * A (*,G) or a (*,*) is being created
99 * find all the children that would point
100 * at us.
101 */
102static void
103pim_ifchannel_find_new_children (struct pim_ifchannel *ch)
104{
105 struct pim_interface *pim_ifp = ch->interface->info;
106 struct pim_ifchannel *child;
107 struct listnode *ch_node;
108
109 // Basic Sanity that we are not being silly
4ed0af70
DS
110 if ((ch->sg.src.s_addr != INADDR_ANY) &&
111 (ch->sg.grp.s_addr != INADDR_ANY))
1a10fc74
DS
112 return;
113
4ed0af70
DS
114 if ((ch->sg.src.s_addr == INADDR_ANY) &&
115 (ch->sg.grp.s_addr == INADDR_ANY))
1a10fc74
DS
116 return;
117
118 for (ALL_LIST_ELEMENTS_RO (pim_ifp->pim_ifchannel_list, ch_node, child))
119 {
4ed0af70
DS
120 if ((ch->sg.grp.s_addr != INADDR_ANY) &&
121 (child->sg.grp.s_addr == ch->sg.grp.s_addr) &&
1a10fc74 122 (child != ch))
3fdfd943
DS
123 {
124 child->parent = ch;
125 listnode_add_sort (ch->sources, child);
126 }
1a10fc74
DS
127 }
128}
129
12e41d03
DL
130void pim_ifchannel_free(struct pim_ifchannel *ch)
131{
12e41d03
DL
132 XFREE(MTYPE_PIM_IFCHANNEL, ch);
133}
134
135void pim_ifchannel_delete(struct pim_ifchannel *ch)
136{
137 struct pim_interface *pim_ifp;
138
139 pim_ifp = ch->interface->info;
12e41d03 140
ef71d1f8
DS
141 if (ch->upstream->channel_oil)
142 {
f663aa7a
DS
143 uint32_t mask = PIM_OIF_FLAG_PROTO_PIM;
144 if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP)
145 mask = PIM_OIF_FLAG_PROTO_IGMP;
146
4ba87bb9
CS
147 /* SGRpt entry could have empty oil */
148 if (ch->upstream->channel_oil)
149 pim_channel_del_oif (ch->upstream->channel_oil, ch->interface, mask);
f663aa7a 150 pim_channel_del_oif (ch->upstream->channel_oil, ch->interface, mask);
ef71d1f8
DS
151 /*
152 * Do we have any S,G's that are inheriting?
153 * Nuke from on high too.
154 */
155 if (ch->upstream->sources)
156 {
157 struct pim_upstream *child;
158 struct listnode *up_node;
159
160 for (ALL_LIST_ELEMENTS_RO (ch->upstream->sources, up_node, child))
781a1745 161 pim_channel_del_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR);
ef71d1f8
DS
162 }
163 }
164
1a10fc74
DS
165 /*
166 * When this channel is removed
167 * we need to find all our children
168 * and make sure our pointers are fixed
169 */
170 pim_ifchannel_remove_children (ch);
171
3fdfd943
DS
172 if (ch->sources)
173 list_delete (ch->sources);
174
2a28f7a0
DS
175 listnode_delete(ch->upstream->ifchannels, ch);
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
2a28f7a0
DS
571 listnode_add_sort(up->ifchannels, ch);
572
535db05b 573 return ch;
12e41d03
DL
574}
575
576static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
577{
578 pim_forward_stop(ch);
579 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
580 delete_on_noinfo(ch);
581}
582
583static int on_ifjoin_expiry_timer(struct thread *t)
584{
585 struct pim_ifchannel *ch;
586
12e41d03 587 ch = THREAD_ARG(t);
12e41d03 588
59ba0ac3 589 ch->t_ifjoin_expiry_timer = NULL;
12e41d03 590
12e41d03
DL
591 ifjoin_to_noinfo(ch);
592 /* ch may have been deleted */
593
594 return 0;
595}
596
12e41d03
DL
597static int on_ifjoin_prune_pending_timer(struct thread *t)
598{
599 struct pim_ifchannel *ch;
600 int send_prune_echo; /* boolean */
601 struct interface *ifp;
602 struct pim_interface *pim_ifp;
12e41d03 603
12e41d03 604 ch = THREAD_ARG(t);
12e41d03 605
59ba0ac3 606 ch->t_ifjoin_prune_pending_timer = NULL;
12e41d03 607
1e3a5132
DS
608 if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING)
609 {
610 /* Send PruneEcho(S,G) ? */
611 ifp = ch->interface;
612 pim_ifp = ifp->info;
613 send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
12e41d03 614
4ba87bb9 615 //ch->ifjoin_state transition to NOINFO
1e3a5132
DS
616 ifjoin_to_noinfo(ch);
617 /* from here ch may have been deleted */
12e41d03 618
1e3a5132 619 if (send_prune_echo)
b5e2377c
DS
620 {
621 struct pim_rpf rpf;
622
623 rpf.source_nexthop.interface = ifp;
624 rpf.rpf_addr.u.prefix4 = pim_ifp->primary_address;
982bff89 625 pim_jp_agg_single_upstream_send(&rpf, ch->upstream, 0);
b5e2377c 626 }
1e3a5132
DS
627 }
628 else
629 {
630 zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state",
631 __PRETTY_FUNCTION__, pim_str_sg_dump (&ch->sg),
d1e77284 632 pim_ifchannel_ifjoin_name (ch->ifjoin_state, ch->flags));
1e3a5132 633 }
12e41d03
DL
634
635 return 0;
636}
637
638static void check_recv_upstream(int is_join,
639 struct interface *recv_ifp,
640 struct in_addr upstream,
4ed0af70 641 struct prefix_sg *sg,
12e41d03
DL
642 uint8_t source_flags,
643 int holdtime)
644{
645 struct pim_upstream *up;
646
647 /* Upstream (S,G) in Joined state ? */
01d45d04 648 up = pim_upstream_find(sg);
12e41d03
DL
649 if (!up)
650 return;
651 if (up->join_state != PIM_UPSTREAM_JOINED)
652 return;
653
654 /* Upstream (S,G) in Joined state */
655
63c59d0c 656 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
12e41d03 657 /* RPF'(S,G) not found */
01d45d04 658 zlog_warn("%s %s: RPF'%s not found",
12e41d03 659 __FILE__, __PRETTY_FUNCTION__,
8bfb8b67 660 up->sg_str);
12e41d03
DL
661 return;
662 }
663
664 /* upstream directed to RPF'(S,G) ? */
63c59d0c 665 if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) {
eaa54bdb
DW
666 char up_str[INET_ADDRSTRLEN];
667 char rpf_str[PREFIX_STRLEN];
12e41d03 668 pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
63c59d0c 669 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
01d45d04 670 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
12e41d03 671 __FILE__, __PRETTY_FUNCTION__,
8bfb8b67 672 up->sg_str,
12e41d03
DL
673 up_str, rpf_str, recv_ifp->name);
674 return;
675 }
676 /* upstream directed to RPF'(S,G) */
677
678 if (is_join) {
679 /* Join(S,G) to RPF'(S,G) */
63c59d0c 680 pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4, holdtime);
12e41d03
DL
681 return;
682 }
683
684 /* Prune to RPF'(S,G) */
685
686 if (source_flags & PIM_RPT_BIT_MASK) {
687 if (source_flags & PIM_WILDCARD_BIT_MASK) {
688 /* Prune(*,G) to RPF'(S,G) */
c48a612c 689 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", up);
12e41d03
DL
690 return;
691 }
692
693 /* Prune(S,G,rpt) to RPF'(S,G) */
c48a612c 694 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", up);
12e41d03
DL
695 return;
696 }
697
698 /* Prune(S,G) to RPF'(S,G) */
c48a612c 699 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up);
12e41d03
DL
700}
701
116b989a
DS
702static int
703nonlocal_upstream(int is_join,
704 struct interface *recv_ifp,
705 struct in_addr upstream,
706 struct prefix_sg *sg,
707 uint8_t source_flags,
708 uint16_t holdtime)
12e41d03
DL
709{
710 struct pim_interface *recv_pim_ifp;
711 int is_local; /* boolean */
712
713 recv_pim_ifp = recv_ifp->info;
714 zassert(recv_pim_ifp);
715
716 is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
116b989a
DS
717
718 if (is_local)
719 return 0;
720
a770ef90 721 if (PIM_DEBUG_PIM_TRACE_DETAIL) {
eaa54bdb 722 char up_str[INET_ADDRSTRLEN];
12e41d03 723 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
116b989a
DS
724 zlog_warn("%s: recv %s (S,G)=%s to non-local upstream=%s on %s",
725 __PRETTY_FUNCTION__,
726 is_join ? "join" : "prune",
727 pim_str_sg_dump (sg),
728 up_str, recv_ifp->name);
12e41d03
DL
729 }
730
12e41d03 731 /*
116b989a
DS
732 * Since recv upstream addr was not directed to our primary
733 * address, check if we should react to it in any way.
734 */
01d45d04 735 check_recv_upstream(is_join, recv_ifp, upstream, sg,
116b989a 736 source_flags, holdtime);
12e41d03
DL
737
738 return 1; /* non-local */
739}
740
741void pim_ifchannel_join_add(struct interface *ifp,
742 struct in_addr neigh_addr,
743 struct in_addr upstream,
4ed0af70 744 struct prefix_sg *sg,
12e41d03
DL
745 uint8_t source_flags,
746 uint16_t holdtime)
747{
748 struct pim_interface *pim_ifp;
749 struct pim_ifchannel *ch;
750
751 if (nonlocal_upstream(1 /* join */, ifp, upstream,
01d45d04 752 sg, source_flags, holdtime)) {
12e41d03
DL
753 return;
754 }
755
4a40c37a 756 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
12e41d03
DL
757 if (!ch)
758 return;
759
760 /*
761 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
762
763 Transitions from "I am Assert Loser" State
764
765 Receive Join(S,G) on Interface I
766
767 We receive a Join(S,G) that has the Upstream Neighbor Address
768 field set to my primary IP address on interface I. The action is
769 to transition to NoInfo state, delete this (S,G) assert state
770 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
771 to operate.
772
773 Notice: The nonlocal_upstream() test above ensures the upstream
774 address of the join message is our primary address.
775 */
776 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
eaa54bdb 777 char neigh_str[INET_ADDRSTRLEN];
12e41d03 778 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
5074a423 779 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
12e41d03 780 __PRETTY_FUNCTION__,
8bfb8b67 781 ch->sg_str, neigh_str, ifp->name);
12e41d03
DL
782
783 assert_action_a5(ch);
784 }
785
786 pim_ifp = ifp->info;
787 zassert(pim_ifp);
788
789 switch (ch->ifjoin_state) {
790 case PIM_IFJOIN_NOINFO:
791 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
792 if (pim_macro_chisin_oiflist(ch)) {
5b668dd7 793 pim_upstream_inherited_olist (ch->upstream);
12e41d03
DL
794 pim_forward_start(ch);
795 }
e6d700e3
DS
796 /*
797 * If we are going to be a LHR, we need to note it
798 */
799 if (ch->upstream->parent &&
800 (ch->upstream->parent->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) &&
801 !(ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_LHR))
802 {
803 pim_upstream_ref (ch->upstream, PIM_UPSTREAM_FLAG_MASK_SRC_LHR);
804 pim_upstream_keep_alive_timer_start (ch->upstream, qpim_keep_alive_time);
805 }
12e41d03
DL
806 break;
807 case PIM_IFJOIN_JOIN:
808 zassert(!ch->t_ifjoin_prune_pending_timer);
809
810 /*
811 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
812 previously received join message with holdtime=0xFFFF.
813 */
814 if (ch->t_ifjoin_expiry_timer) {
815 unsigned long remain =
816 thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
817 if (remain > holdtime) {
818 /*
819 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
820
821 Transitions from Join State
822
823 The (S,G) downstream state machine on interface I remains in
824 Join state, and the Expiry Timer (ET) is restarted, set to
825 maximum of its current value and the HoldTime from the
826 triggering Join/Prune message.
827
828 Conclusion: Do not change the ET if the current value is
829 higher than the received join holdtime.
830 */
831 return;
832 }
833 }
834 THREAD_OFF(ch->t_ifjoin_expiry_timer);
835 break;
be4791f2 836 case PIM_IFJOIN_PRUNE:
220d8a49
DS
837 if (source_flags & PIM_ENCODE_RPT_BIT)
838 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
be4791f2 839 break;
12e41d03 840 case PIM_IFJOIN_PRUNE_PENDING:
1e3a5132 841 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
220d8a49 842 if (source_flags & PIM_ENCODE_RPT_BIT)
220d8a49 843 {
1e3a5132
DS
844 THREAD_OFF(ch->t_ifjoin_expiry_timer);
845 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
220d8a49 846 }
1e3a5132
DS
847 else
848 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
12e41d03 849 break;
be4791f2 850 case PIM_IFJOIN_PRUNE_TMP:
be4791f2
DS
851 break;
852 case PIM_IFJOIN_PRUNE_PENDING_TMP:
be4791f2 853 break;
12e41d03
DL
854 }
855
12e41d03 856 if (holdtime != 0xFFFF) {
ffa2c898
QY
857 thread_add_timer(master, on_ifjoin_expiry_timer, ch, holdtime,
858 &ch->t_ifjoin_expiry_timer);
12e41d03
DL
859 }
860}
861
862void pim_ifchannel_prune(struct interface *ifp,
863 struct in_addr upstream,
4ed0af70 864 struct prefix_sg *sg,
12e41d03
DL
865 uint8_t source_flags,
866 uint16_t holdtime)
867{
868 struct pim_ifchannel *ch;
1405c852 869 struct pim_interface *pim_ifp;
12e41d03
DL
870 int jp_override_interval_msec;
871
872 if (nonlocal_upstream(0 /* prune */, ifp, upstream,
01d45d04 873 sg, source_flags, holdtime)) {
12e41d03
DL
874 return;
875 }
876
df90067a
DS
877 ch = pim_ifchannel_find (ifp, sg);
878 if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT))
879 {
880 if (PIM_DEBUG_TRACE)
881 zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d",
882 __PRETTY_FUNCTION__, ifp->name, pim_str_sg_dump (sg), source_flags);
883 return;
884 }
885
4a40c37a 886 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
12e41d03
DL
887 if (!ch)
888 return;
889
1405c852
DS
890 pim_ifp = ifp->info;
891
12e41d03
DL
892 switch (ch->ifjoin_state) {
893 case PIM_IFJOIN_NOINFO:
1405c852
DS
894 if (source_flags & PIM_ENCODE_RPT_BIT)
895 {
896 PIM_IF_FLAG_SET_S_G_RPT(ch->flags);
897 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
898 if (listcount(pim_ifp->pim_neighbor_list) > 1)
899 jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
900 else
901 jp_override_interval_msec = 0; /* schedule to expire immediately */
902 /* If we called ifjoin_prune() directly instead, care should
903 be taken not to use "ch" afterwards since it would be
904 deleted. */
905
906 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
907 THREAD_OFF(ch->t_ifjoin_expiry_timer);
ffa2c898
QY
908 thread_add_timer_msec(master, on_ifjoin_prune_pending_timer, ch,
909 jp_override_interval_msec,
910 &ch->t_ifjoin_prune_pending_timer);
911 thread_add_timer(master, on_ifjoin_expiry_timer, ch, holdtime,
912 &ch->t_ifjoin_expiry_timer);
d78f0ac1 913 pim_upstream_update_join_desired(ch->upstream);
1405c852
DS
914 }
915 break;
12e41d03
DL
916 case PIM_IFJOIN_PRUNE_PENDING:
917 /* nothing to do */
918 break;
919 case PIM_IFJOIN_JOIN:
1405c852 920 THREAD_OFF(ch->t_ifjoin_expiry_timer);
12e41d03 921
1405c852 922 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
12e41d03 923
1405c852
DS
924 if (listcount(pim_ifp->pim_neighbor_list) > 1)
925 jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
926 else
927 jp_override_interval_msec = 0; /* schedule to expire immediately */
928 /* If we called ifjoin_prune() directly instead, care should
929 be taken not to use "ch" afterwards since it would be
930 deleted. */
931 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
ffa2c898
QY
932 thread_add_timer_msec(master, on_ifjoin_prune_pending_timer, ch,
933 jp_override_interval_msec,
934 &ch->t_ifjoin_prune_pending_timer);
12e41d03 935 break;
be4791f2 936 case PIM_IFJOIN_PRUNE:
1405c852
DS
937 if (source_flags & PIM_ENCODE_RPT_BIT)
938 {
939 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
ffa2c898
QY
940 thread_add_timer(master, on_ifjoin_expiry_timer, ch, holdtime,
941 &ch->t_ifjoin_expiry_timer);
1405c852
DS
942 }
943 break;
be4791f2 944 case PIM_IFJOIN_PRUNE_TMP:
1405c852
DS
945 if (source_flags & PIM_ENCODE_RPT_BIT)
946 {
947 ch->ifjoin_state = PIM_IFJOIN_PRUNE;
948 THREAD_OFF(ch->t_ifjoin_expiry_timer);
ffa2c898
QY
949 thread_add_timer(master, on_ifjoin_expiry_timer, ch, holdtime,
950 &ch->t_ifjoin_expiry_timer);
1405c852
DS
951 }
952 break;
be4791f2 953 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1405c852
DS
954 if (source_flags & PIM_ENCODE_RPT_BIT)
955 {
956 ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING;
957 THREAD_OFF(ch->t_ifjoin_expiry_timer);
ffa2c898
QY
958 thread_add_timer(master, on_ifjoin_expiry_timer, ch, holdtime,
959 &ch->t_ifjoin_expiry_timer);
1405c852 960 }
be4791f2 961 break;
12e41d03 962 }
12e41d03
DL
963}
964
f663aa7a
DS
965int
966pim_ifchannel_local_membership_add(struct interface *ifp,
967 struct prefix_sg *sg)
12e41d03 968{
c8fc07cb 969 struct pim_ifchannel *ch, *starch;
12e41d03
DL
970 struct pim_interface *pim_ifp;
971
972 /* PIM enabled on interface? */
973 pim_ifp = ifp->info;
974 if (!pim_ifp)
f663aa7a 975 return 0;
12e41d03 976 if (!PIM_IF_TEST_PIM(pim_ifp->options))
f663aa7a 977 return 0;
12e41d03 978
15a5dafe 979 /* skip (*,G) ch creation if G is of type SSM */
980 if (sg->src.s_addr == INADDR_ANY)
981 {
982 if (pim_is_grp_ssm (sg->grp))
983 {
984 if (PIM_DEBUG_PIM_EVENTS)
985 zlog_debug("%s: local membership (S,G)=%s ignored as group is SSM",
986 __PRETTY_FUNCTION__, pim_str_sg_dump (sg));
987 return 1;
988 }
989 }
990
4a40c37a 991 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP);
12e41d03 992 if (!ch) {
f663aa7a 993 return 0;
12e41d03
DL
994 }
995
996 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
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
c8fc07cb
DS
1004 starch = ch;
1005
03417ccd 1006 for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child))
dfbbce1d 1007 {
c8fc07cb
DS
1008 if (PIM_DEBUG_EVENTS)
1009 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
1010 __FILE__, __PRETTY_FUNCTION__,
1011 child->sg_str, ifp->name, up->sg_str);
03417ccd 1012
c8fc07cb
DS
1013 ch = pim_ifchannel_find (ifp, &child->sg);
1014 if (pim_upstream_evaluate_join_desired_interface (child, ch, starch))
1015 {
1016 pim_channel_add_oif (child->channel_oil, ifp, PIM_OIF_FLAG_PROTO_STAR);
1017 pim_upstream_switch (child, PIM_UPSTREAM_JOINED);
1018 }
dfbbce1d 1019 }
df94f9a9
DS
1020
1021 if (pimg->spt.switchover == PIM_SPT_INFINITY)
1022 {
1023 if (pimg->spt.plist)
1024 {
1025 struct prefix_list *plist = prefix_list_lookup (AFI_IP, pimg->spt.plist);
1026 struct prefix g;
1027 g.family = AF_INET;
1028 g.prefixlen = IPV4_MAX_PREFIXLEN;
1029 g.u.prefix4 = up->sg.grp;
1030
1031 if (prefix_list_apply (plist, &g) == PREFIX_DENY)
1032 {
1033 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
1034 }
1035 }
1036 }
1037 else
1038 pim_channel_add_oif (up->channel_oil, pim_regiface, PIM_OIF_FLAG_PROTO_IGMP);
dfbbce1d 1039 }
f663aa7a
DS
1040
1041 return 1;
12e41d03
DL
1042}
1043
1044void pim_ifchannel_local_membership_del(struct interface *ifp,
4ed0af70 1045 struct prefix_sg *sg)
12e41d03 1046{
c8fc07cb 1047 struct pim_ifchannel *starch, *ch, *orig;
12e41d03
DL
1048 struct pim_interface *pim_ifp;
1049
1050 /* PIM enabled on interface? */
1051 pim_ifp = ifp->info;
1052 if (!pim_ifp)
1053 return;
1054 if (!PIM_IF_TEST_PIM(pim_ifp->options))
1055 return;
1056
c8fc07cb 1057 orig = ch = pim_ifchannel_find(ifp, sg);
12e41d03
DL
1058 if (!ch)
1059 return;
1060
1061 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
1062
dfbbce1d
DS
1063 if (sg->src.s_addr == INADDR_ANY)
1064 {
1065 struct pim_upstream *up = pim_upstream_find (sg);
1066 struct pim_upstream *child;
cf528e4f 1067 struct listnode *up_node, *up_nnode;
dfbbce1d 1068
c8fc07cb
DS
1069 starch = ch;
1070
cf528e4f 1071 for (ALL_LIST_ELEMENTS (up->sources, up_node, up_nnode, child))
dfbbce1d 1072 {
03417ccd
DS
1073 struct channel_oil *c_oil = child->channel_oil;
1074 struct pim_ifchannel *chchannel = pim_ifchannel_find (ifp, &child->sg);
1075 struct pim_interface *pim_ifp = ifp->info;
1076
1077 if (PIM_DEBUG_EVENTS)
8bfb8b67
DS
1078 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
1079 __FILE__, __PRETTY_FUNCTION__,
1080 up->sg_str, ifp->name, child->sg_str);
03417ccd 1081
c8fc07cb
DS
1082 ch = pim_ifchannel_find (ifp, &child->sg);
1083 if (c_oil && !pim_upstream_evaluate_join_desired_interface (child, ch, starch))
781a1745 1084 pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_STAR);
03417ccd
DS
1085
1086 /*
1087 * If the S,G has no if channel and the c_oil still
1088 * has output here then the *,G was supplying the implied
1089 * if channel. So remove it.
1090 */
b72f79a9 1091 if (!chchannel && c_oil && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
781a1745 1092 pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_STAR);
cf528e4f 1093
4ba87bb9 1094 /* Child node removal/ref count-- will happen as part of parent' delete_no_info */
dfbbce1d
DS
1095 }
1096 }
c8fc07cb 1097 delete_on_noinfo(orig);
12e41d03
DL
1098}
1099
1100void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
1101{
1102 int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
1103 int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
1104
1105 if (new_couldassert == old_couldassert)
1106 return;
1107
1108 if (PIM_DEBUG_PIM_EVENTS) {
eaa54bdb
DW
1109 char src_str[INET_ADDRSTRLEN];
1110 char grp_str[INET_ADDRSTRLEN];
4ed0af70
DS
1111 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1112 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
1113 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
1114 __PRETTY_FUNCTION__,
1115 src_str, grp_str, ch->interface->name,
1116 old_couldassert, new_couldassert);
1117 }
1118
1119 if (new_couldassert) {
1120 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
1121 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
1122 }
1123 else {
1124 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
1125 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
1126
1127 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
1128 assert_action_a4(ch);
1129 }
1130 }
1131
1132 pim_ifchannel_update_my_assert_metric(ch);
1133}
1134
1135/*
1136 my_assert_metric may be affected by:
1137
1138 CouldAssert(S,G)
1139 pim_ifp->primary_address
1140 rpf->source_nexthop.mrib_metric_preference;
1141 rpf->source_nexthop.mrib_route_metric;
1142 */
1143void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
1144{
1145 struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
1146
1147 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
1148 return;
1149
1150 if (PIM_DEBUG_PIM_EVENTS) {
eaa54bdb
DW
1151 char src_str[INET_ADDRSTRLEN];
1152 char grp_str[INET_ADDRSTRLEN];
1153 char old_addr_str[INET_ADDRSTRLEN];
1154 char new_addr_str[INET_ADDRSTRLEN];
4ed0af70
DS
1155 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1156 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
1157 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
1158 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
1159 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
1160 __PRETTY_FUNCTION__,
1161 src_str, grp_str, ch->interface->name,
1162 ch->ifassert_my_metric.rpt_bit_flag,
1163 ch->ifassert_my_metric.metric_preference,
1164 ch->ifassert_my_metric.route_metric,
1165 old_addr_str,
1166 my_metric_new.rpt_bit_flag,
1167 my_metric_new.metric_preference,
1168 my_metric_new.route_metric,
1169 new_addr_str);
1170 }
1171
1172 ch->ifassert_my_metric = my_metric_new;
1173
1174 if (pim_assert_metric_better(&ch->ifassert_my_metric,
1175 &ch->ifassert_winner_metric)) {
1176 assert_action_a5(ch);
1177 }
1178}
1179
1180void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
1181{
1182 int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
1183 int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
1184
1185 if (new_atd == old_atd)
1186 return;
1187
1188 if (PIM_DEBUG_PIM_EVENTS) {
eaa54bdb
DW
1189 char src_str[INET_ADDRSTRLEN];
1190 char grp_str[INET_ADDRSTRLEN];
4ed0af70
DS
1191 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
1192 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
1193 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
1194 __PRETTY_FUNCTION__,
1195 src_str, grp_str, ch->interface->name,
1196 old_atd, new_atd);
1197 }
1198
1199 if (new_atd) {
1200 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
1201 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
1202 }
1203 else {
1204 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
1205 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
1206
1207 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
1208 assert_action_a5(ch);
1209 }
1210 }
1211}
c8507a16
DS
1212
1213/*
1214 * If we have a new pim interface, check to
1215 * see if any of the pre-existing channels have
1216 * their upstream out that way and turn on forwarding
1217 * for that ifchannel then.
1218 */
1219void
1220pim_ifchannel_scan_forward_start (struct interface *new_ifp)
1221{
1222 struct listnode *ifnode;
1223 struct interface *ifp;
1224 struct pim_interface *new_pim_ifp = new_ifp->info;
1225
1226 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp))
1227 {
1228 struct pim_interface *loop_pim_ifp = ifp->info;
1229 struct listnode *ch_node;
1230 struct pim_ifchannel *ch;
1231
1232 if (!loop_pim_ifp)
1233 continue;
1234
1235 if (new_pim_ifp == loop_pim_ifp)
1236 continue;
1237
1238 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp->pim_ifchannel_list, ch_node, ch))
1239 {
1240 if (ch->ifjoin_state == PIM_IFJOIN_JOIN)
1241 {
1242 struct pim_upstream *up = ch->upstream;
1243 if ((!up->channel_oil) &&
1244 (up->rpf.source_nexthop.interface == new_ifp))
1245 pim_forward_start (ch);
1246 }
1247 }
1248 }
1249}
220d8a49
DS
1250
1251/*
1252 * Downstream per-interface (S,G,rpt) state machine
1253 * states that we need to move (S,G,rpt) items
1254 * into different states at the start of the
1255 * reception of a *,G join as well, when
1256 * we get End of Message
1257 */
1258void
1f58d560 1259pim_ifchannel_set_star_g_join_state (struct pim_ifchannel *ch, int eom, uint8_t source_flags, uint8_t join)
220d8a49
DS
1260{
1261 struct pim_ifchannel *child;
1262 struct listnode *ch_node;
1263
1264 if (PIM_DEBUG_PIM_TRACE)
1f58d560 1265 zlog_debug ("%s: %s %s eom: %d join %u", __PRETTY_FUNCTION__,
d1e77284 1266 pim_ifchannel_ifjoin_name(ch->ifjoin_state, ch->flags),
1f58d560 1267 ch->sg_str, eom, join);
220d8a49
DS
1268 if (!ch->sources)
1269 return;
1270
1271 for (ALL_LIST_ELEMENTS_RO (ch->sources, ch_node, child))
1272 {
1f58d560
CS
1273 /* Only *,G Join received and no (SG-RPT) prune.
1274 Scan all S,G associated to G and if any SG-RPT
1275 remove the SG-RPT flag.
1276 */
1277 if (join && (source_flags & PIM_RPT_BIT_MASK) &&
1278 (source_flags & PIM_WILDCARD_BIT_MASK))
1279 {
1280 if (PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1281 {
1282 struct pim_upstream *up = child->upstream;
1283
1284 PIM_IF_FLAG_UNSET_S_G_RPT(child->flags);
1285 if (up)
1286 {
1287 if (PIM_DEBUG_TRACE)
4ba87bb9 1288 zlog_debug ("%s: clearing SGRpt flag, add inherit oif to up %s ", __PRETTY_FUNCTION__, up->sg_str);
1f58d560
CS
1289 pim_channel_add_oif (up->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR);
1290 }
1291 }
1292 }
1293 /* Received SG-RPT Prune delete oif from S,G */
1294 else if (join == 0 && (source_flags & PIM_RPT_BIT_MASK) &&
1295 !(source_flags & PIM_WILDCARD_BIT_MASK))
1296 {
1297 struct pim_upstream *up = child->upstream;
1298
1299 PIM_IF_FLAG_SET_S_G_RPT(child->flags);
1300 if (up)
1301 {
1302 if (PIM_DEBUG_TRACE)
1303 zlog_debug ("%s: del inherit oif from up %s", __PRETTY_FUNCTION__, up->sg_str);
1304 pim_channel_del_oif (up->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR);
1305 }
1306 }
1307
220d8a49
DS
1308 if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags))
1309 continue;
1310
1311 switch (child->ifjoin_state)
1405c852
DS
1312 {
1313 case PIM_IFJOIN_NOINFO:
1314 case PIM_IFJOIN_JOIN:
1315 break;
1316 case PIM_IFJOIN_PRUNE:
1317 if (!eom)
1318 child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP;
1319 break;
1320 case PIM_IFJOIN_PRUNE_PENDING:
1321 if (!eom)
1322 child->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING_TMP;
1323 break;
1324 case PIM_IFJOIN_PRUNE_TMP:
1325 case PIM_IFJOIN_PRUNE_PENDING_TMP:
1326 if (eom)
1327 child->ifjoin_state = PIM_IFJOIN_NOINFO;
1328 break;
1329 }
220d8a49
DS
1330 }
1331}
a625e937
DS
1332
1333unsigned int
1334pim_ifchannel_hash_key (void *arg)
1335{
1336 struct pim_ifchannel *ch = (struct pim_ifchannel *)arg;
1337
1338 return jhash_2words (ch->sg.src.s_addr, ch->sg.grp.s_addr, 0);
1339}
1340
1341int
1342pim_ifchannel_equal (const void *arg1, const void *arg2)
1343{
1344 const struct pim_ifchannel *ch1 = (const struct pim_ifchannel *)arg1;
1345 const struct pim_ifchannel *ch2 = (const struct pim_ifchannel *)arg2;
1346
1347 if ((ch1->sg.grp.s_addr == ch2->sg.grp.s_addr) &&
1348 (ch1->sg.src.s_addr == ch2->sg.src.s_addr))
1349 return 1;
1350
1351 return 0;
1352}