]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_ifchannel.c
pimd: Make sure we have valid pointer when printing debugs
[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
19
12e41d03
DL
20*/
21
22#include <zebra.h>
23
24#include "linklist.h"
25#include "thread.h"
26#include "memory.h"
744d91b3 27#include "if.h"
c8507a16 28#include "vrf.h"
12e41d03
DL
29
30#include "pimd.h"
31#include "pim_str.h"
32#include "pim_iface.h"
33#include "pim_ifchannel.h"
34#include "pim_zebra.h"
35#include "pim_time.h"
36#include "pim_msg.h"
37#include "pim_pim.h"
38#include "pim_join.h"
39#include "pim_rpf.h"
40#include "pim_macro.h"
dfbbce1d 41#include "pim_oil.h"
4a40c37a 42#include "pim_upstream.h"
12e41d03 43
1a10fc74
DS
44/*
45 * A (*,G) or a (*,*) is going away
46 * remove the parent pointer from
47 * those pointing at us
48 */
49static void
50pim_ifchannel_remove_children (struct pim_ifchannel *ch)
51{
52 struct pim_interface *pim_ifp = ch->interface->info;
53 struct listnode *ch_node;
54 struct pim_ifchannel *child;
55
56 // Basic sanity, (*,*) not currently supported
4ed0af70
DS
57 if ((ch->sg.src.s_addr == INADDR_ANY) &&
58 (ch->sg.grp.s_addr == INADDR_ANY))
1a10fc74
DS
59 return;
60
61 // Basic sanity (S,G) have no children
4ed0af70
DS
62 if ((ch->sg.src.s_addr != INADDR_ANY) &&
63 (ch->sg.grp.s_addr != INADDR_ANY))
1a10fc74
DS
64 return;
65
66 for (ALL_LIST_ELEMENTS_RO (pim_ifp->pim_ifchannel_list, ch_node, child))
67 {
68 if (child->parent == ch)
69 child->parent = NULL;
70 }
71}
72
73/*
74 * A (*,G) or a (*,*) is being created
75 * find all the children that would point
76 * at us.
77 */
78static void
79pim_ifchannel_find_new_children (struct pim_ifchannel *ch)
80{
81 struct pim_interface *pim_ifp = ch->interface->info;
82 struct pim_ifchannel *child;
83 struct listnode *ch_node;
84
85 // Basic Sanity that we are not being silly
4ed0af70
DS
86 if ((ch->sg.src.s_addr != INADDR_ANY) &&
87 (ch->sg.grp.s_addr != INADDR_ANY))
1a10fc74
DS
88 return;
89
4ed0af70
DS
90 if ((ch->sg.src.s_addr == INADDR_ANY) &&
91 (ch->sg.grp.s_addr == INADDR_ANY))
1a10fc74
DS
92 return;
93
94 for (ALL_LIST_ELEMENTS_RO (pim_ifp->pim_ifchannel_list, ch_node, child))
95 {
4ed0af70
DS
96 if ((ch->sg.grp.s_addr != INADDR_ANY) &&
97 (child->sg.grp.s_addr == ch->sg.grp.s_addr) &&
1a10fc74
DS
98 (child != ch))
99 child->parent = ch;
100 }
101}
102
12e41d03
DL
103void pim_ifchannel_free(struct pim_ifchannel *ch)
104{
105 zassert(!ch->t_ifjoin_expiry_timer);
106 zassert(!ch->t_ifjoin_prune_pending_timer);
107 zassert(!ch->t_ifassert_timer);
108
109 XFREE(MTYPE_PIM_IFCHANNEL, ch);
110}
111
112void pim_ifchannel_delete(struct pim_ifchannel *ch)
113{
114 struct pim_interface *pim_ifp;
115
116 pim_ifp = ch->interface->info;
117 zassert(pim_ifp);
118
1a10fc74
DS
119 /*
120 * When this channel is removed
121 * we need to find all our children
122 * and make sure our pointers are fixed
123 */
124 pim_ifchannel_remove_children (ch);
125
12e41d03
DL
126 if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) {
127 pim_upstream_update_join_desired(ch->upstream);
128 }
129
e5905a3b 130 pim_upstream_del(ch->upstream, __PRETTY_FUNCTION__);
5ce79466 131 ch->upstream = NULL;
12e41d03
DL
132
133 THREAD_OFF(ch->t_ifjoin_expiry_timer);
134 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
135 THREAD_OFF(ch->t_ifassert_timer);
136
137 /*
138 notice that listnode_delete() can't be moved
139 into pim_ifchannel_free() because the later is
140 called by list_delete_all_node()
141 */
142 listnode_delete(pim_ifp->pim_ifchannel_list, ch);
143
144 pim_ifchannel_free(ch);
145}
12e41d03
DL
146
147static void delete_on_noinfo(struct pim_ifchannel *ch)
148{
1be8f9d3
DS
149 if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO &&
150 ch->ifjoin_state == PIM_IFJOIN_NOINFO)
12e41d03 151 pim_ifchannel_delete(ch);
1be8f9d3 152
12e41d03
DL
153}
154
155void pim_ifchannel_ifjoin_switch(const char *caller,
156 struct pim_ifchannel *ch,
157 enum pim_ifjoin_state new_state)
158{
159 enum pim_ifjoin_state old_state = ch->ifjoin_state;
160
04329d4e 161 if (PIM_DEBUG_PIM_EVENTS)
594a78cc
DS
162 zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s",
163 ch->interface->name,
04329d4e
DS
164 pim_str_sg_dump (&ch->sg),
165 pim_ifchannel_ifjoin_name (ch->ifjoin_state),
166 pim_ifchannel_ifjoin_name (new_state));
167
168
12e41d03 169 if (old_state == new_state) {
7adf0260
DS
170 if (PIM_DEBUG_PIM_EVENTS) {
171 zlog_debug("%s calledby %s: non-transition on state %d (%s)",
172 __PRETTY_FUNCTION__, caller, new_state,
173 pim_ifchannel_ifjoin_name(new_state));
174 }
12e41d03
DL
175 return;
176 }
177
12e41d03
DL
178 ch->ifjoin_state = new_state;
179
180 /* Transition to/from NOINFO ? */
1be8f9d3
DS
181 if ((old_state == PIM_IFJOIN_NOINFO) ||
182 (new_state == PIM_IFJOIN_NOINFO)) {
12e41d03
DL
183
184 if (PIM_DEBUG_PIM_EVENTS) {
ee1a477a 185 zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s",
12e41d03 186 ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"),
ee1a477a 187 pim_str_sg_dump (&ch->sg), ch->interface->name);
12e41d03
DL
188 }
189
190 /*
191 Record uptime of state transition to/from NOINFO
192 */
193 ch->ifjoin_creation = pim_time_monotonic_sec();
194
195 pim_upstream_update_join_desired(ch->upstream);
196 pim_ifchannel_update_could_assert(ch);
197 pim_ifchannel_update_assert_tracking_desired(ch);
198 }
199}
200
201const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state)
202{
203 switch (ifjoin_state) {
204 case PIM_IFJOIN_NOINFO: return "NOINFO";
205 case PIM_IFJOIN_JOIN: return "JOIN";
206 case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP";
207 }
208
209 return "ifjoin_bad_state";
210}
211
212const char *pim_ifchannel_ifassert_name(enum pim_ifassert_state ifassert_state)
213{
214 switch (ifassert_state) {
215 case PIM_IFASSERT_NOINFO: return "NOINFO";
216 case PIM_IFASSERT_I_AM_WINNER: return "WINNER";
217 case PIM_IFASSERT_I_AM_LOSER: return "LOSER";
218 }
219
220 return "ifassert_bad_state";
221}
222
223/*
224 RFC 4601: 4.6.5. Assert State Macros
225
226 AssertWinner(S,G,I) defaults to NULL and AssertWinnerMetric(S,G,I)
227 defaults to Infinity when in the NoInfo state.
228*/
229void reset_ifassert_state(struct pim_ifchannel *ch)
230{
231 THREAD_OFF(ch->t_ifassert_timer);
232
233 pim_ifassert_winner_set(ch,
234 PIM_IFASSERT_NOINFO,
235 qpim_inaddr_any,
236 qpim_infinite_assert_metric);
237}
238
12e41d03 239struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp,
4ed0af70 240 struct prefix_sg *sg)
12e41d03
DL
241{
242 struct pim_interface *pim_ifp;
243 struct listnode *ch_node;
244 struct pim_ifchannel *ch;
245
246 zassert(ifp);
247
248 pim_ifp = ifp->info;
249
250 if (!pim_ifp) {
5074a423 251 zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s",
12e41d03 252 __PRETTY_FUNCTION__,
5074a423 253 pim_str_sg_dump (sg),
12e41d03
DL
254 ifp->name);
255 return 0;
256 }
257
258 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
259 if (
4ed0af70
DS
260 (sg->src.s_addr == ch->sg.src.s_addr) &&
261 (sg->grp.s_addr == ch->sg.grp.s_addr)
12e41d03
DL
262 ) {
263 return ch;
264 }
265 }
266
267 return 0;
268}
269
270static void ifmembership_set(struct pim_ifchannel *ch,
271 enum pim_ifmembership membership)
272{
273 if (ch->local_ifmembership == membership)
274 return;
275
7adf0260 276 if (PIM_DEBUG_PIM_EVENTS) {
ee1a477a 277 zlog_debug("%s: (S,G)=%s membership now is %s on interface %s",
12e41d03 278 __PRETTY_FUNCTION__,
ee1a477a 279 pim_str_sg_dump (&ch->sg),
12e41d03
DL
280 membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO",
281 ch->interface->name);
282 }
283
284 ch->local_ifmembership = membership;
285
286 pim_upstream_update_join_desired(ch->upstream);
287 pim_ifchannel_update_could_assert(ch);
288 pim_ifchannel_update_assert_tracking_desired(ch);
289}
290
291
292void pim_ifchannel_membership_clear(struct interface *ifp)
293{
294 struct pim_interface *pim_ifp;
295 struct listnode *ch_node;
296 struct pim_ifchannel *ch;
297
298 pim_ifp = ifp->info;
299 zassert(pim_ifp);
300
301 for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) {
302 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
303 }
304}
305
306void pim_ifchannel_delete_on_noinfo(struct interface *ifp)
307{
308 struct pim_interface *pim_ifp;
309 struct listnode *node;
310 struct listnode *next_node;
311 struct pim_ifchannel *ch;
312
313 pim_ifp = ifp->info;
314 zassert(pim_ifp);
315
316 for (ALL_LIST_ELEMENTS(pim_ifp->pim_ifchannel_list, node, next_node, ch)) {
317 delete_on_noinfo(ch);
318 }
319}
320
1a10fc74
DS
321/*
322 * For a given Interface, if we are given a S,G
323 * Find the *,G (If we have it).
324 * If we are passed a *,G, find the *,* ifchannel
325 * if we have it.
326 */
327static struct pim_ifchannel *
328pim_ifchannel_find_parent (struct interface *ifp,
4ed0af70 329 struct prefix_sg *sg)
1a10fc74 330{
4ed0af70 331 struct prefix_sg parent_sg = *sg;
1a10fc74
DS
332
333 // (*,*) || (S,*)
4ed0af70
DS
334 if (((sg->src.s_addr == INADDR_ANY) &&
335 (sg->grp.s_addr == INADDR_ANY)) ||
336 ((sg->src.s_addr != INADDR_ANY) &&
337 (sg->grp.s_addr == INADDR_ANY)))
1a10fc74
DS
338 return NULL;
339
340 // (S,G)
4ed0af70
DS
341 if ((sg->src.s_addr != INADDR_ANY) &&
342 (sg->grp.s_addr != INADDR_ANY))
1a10fc74 343 {
4ed0af70 344 parent_sg.src.s_addr = INADDR_ANY;
1a10fc74
DS
345 return pim_ifchannel_find (ifp, &parent_sg);
346 }
347
348 // (*,G) -- Not going to find anything currently
4ed0af70 349 parent_sg.grp.s_addr = INADDR_ANY;
1a10fc74
DS
350 return pim_ifchannel_find (ifp, &parent_sg);
351}
352
353struct pim_ifchannel *
354pim_ifchannel_add(struct interface *ifp,
4a40c37a 355 struct prefix_sg *sg, int flags)
12e41d03 356{
535db05b 357 struct pim_interface *pim_ifp;
12e41d03 358 struct pim_ifchannel *ch;
535db05b 359 struct pim_upstream *up;
12e41d03 360
5074a423 361 ch = pim_ifchannel_find(ifp, sg);
12e41d03
DL
362 if (ch)
363 return ch;
364
535db05b
DS
365 pim_ifp = ifp->info;
366 zassert(pim_ifp);
367
e5905a3b 368 up = pim_upstream_add(sg, NULL, flags, __PRETTY_FUNCTION__);
535db05b 369 if (!up) {
5074a423 370 zlog_err("%s: could not attach upstream (S,G)=%s on interface %s",
535db05b 371 __PRETTY_FUNCTION__,
5074a423 372 pim_str_sg_dump (sg), ifp->name);
535db05b
DS
373 return NULL;
374 }
375
376 ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch));
377 if (!ch) {
5074a423 378 zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s",
535db05b 379 __PRETTY_FUNCTION__,
5074a423 380 pim_str_sg_dump (sg), ifp->name);
12e41d03 381
535db05b
DS
382 return NULL;
383 }
12e41d03 384
535db05b
DS
385 ch->flags = 0;
386 ch->upstream = up;
387 ch->interface = ifp;
5074a423 388 ch->sg = *sg;
1a10fc74
DS
389 ch->parent = pim_ifchannel_find_parent (ifp, sg);
390 pim_ifchannel_find_new_children (ch);
535db05b
DS
391 ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO;
392
393 ch->ifjoin_state = PIM_IFJOIN_NOINFO;
394 ch->t_ifjoin_expiry_timer = NULL;
395 ch->t_ifjoin_prune_pending_timer = NULL;
396 ch->ifjoin_creation = 0;
397
398 ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch);
399 ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch);
400
401 ch->ifassert_winner.s_addr = 0;
402
403 /* Assert state */
404 ch->t_ifassert_timer = NULL;
405 reset_ifassert_state(ch);
406 if (pim_macro_ch_could_assert_eval(ch))
407 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
408 else
409 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
410
411 if (pim_macro_assert_tracking_desired_eval(ch))
412 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
413 else
414 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
415
416 /* Attach to list */
bbd64ce1 417 listnode_add_sort(pim_ifp->pim_ifchannel_list, ch);
535db05b 418
535db05b 419 return ch;
12e41d03
DL
420}
421
422static void ifjoin_to_noinfo(struct pim_ifchannel *ch)
423{
424 pim_forward_stop(ch);
425 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO);
426 delete_on_noinfo(ch);
427}
428
429static int on_ifjoin_expiry_timer(struct thread *t)
430{
431 struct pim_ifchannel *ch;
432
433 zassert(t);
434 ch = THREAD_ARG(t);
435 zassert(ch);
436
59ba0ac3 437 ch->t_ifjoin_expiry_timer = NULL;
12e41d03
DL
438
439 zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN);
440
441 ifjoin_to_noinfo(ch);
442 /* ch may have been deleted */
443
444 return 0;
445}
446
12e41d03
DL
447static int on_ifjoin_prune_pending_timer(struct thread *t)
448{
449 struct pim_ifchannel *ch;
450 int send_prune_echo; /* boolean */
451 struct interface *ifp;
452 struct pim_interface *pim_ifp;
4ed0af70 453 struct prefix_sg sg;
12e41d03
DL
454
455 zassert(t);
456 ch = THREAD_ARG(t);
457 zassert(ch);
458
59ba0ac3 459 ch->t_ifjoin_prune_pending_timer = NULL;
12e41d03
DL
460
461 zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING);
462
463 /* Send PruneEcho(S,G) ? */
464 ifp = ch->interface;
465 pim_ifp = ifp->info;
466 send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1);
467
468 /* Save (S,G) */
05e451f8 469 sg = ch->sg;
12e41d03
DL
470
471 ifjoin_to_noinfo(ch);
472 /* from here ch may have been deleted */
473
474 if (send_prune_echo)
64a70f46 475 pim_joinprune_send (ifp, pim_ifp->primary_address,
05e451f8 476 &sg, 0);
12e41d03
DL
477
478 return 0;
479}
480
481static void check_recv_upstream(int is_join,
482 struct interface *recv_ifp,
483 struct in_addr upstream,
4ed0af70 484 struct prefix_sg *sg,
12e41d03
DL
485 uint8_t source_flags,
486 int holdtime)
487{
488 struct pim_upstream *up;
489
490 /* Upstream (S,G) in Joined state ? */
01d45d04 491 up = pim_upstream_find(sg);
12e41d03
DL
492 if (!up)
493 return;
494 if (up->join_state != PIM_UPSTREAM_JOINED)
495 return;
496
497 /* Upstream (S,G) in Joined state */
498
63c59d0c 499 if (pim_rpf_addr_is_inaddr_any(&up->rpf)) {
12e41d03 500 /* RPF'(S,G) not found */
01d45d04 501 zlog_warn("%s %s: RPF'%s not found",
12e41d03 502 __FILE__, __PRETTY_FUNCTION__,
01d45d04 503 pim_str_sg_dump (sg));
12e41d03
DL
504 return;
505 }
506
507 /* upstream directed to RPF'(S,G) ? */
63c59d0c 508 if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) {
eaa54bdb
DW
509 char up_str[INET_ADDRSTRLEN];
510 char rpf_str[PREFIX_STRLEN];
12e41d03 511 pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str));
63c59d0c 512 pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str));
01d45d04 513 zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s",
12e41d03 514 __FILE__, __PRETTY_FUNCTION__,
01d45d04 515 pim_str_sg_dump (sg),
12e41d03
DL
516 up_str, rpf_str, recv_ifp->name);
517 return;
518 }
519 /* upstream directed to RPF'(S,G) */
520
521 if (is_join) {
522 /* Join(S,G) to RPF'(S,G) */
63c59d0c 523 pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4, holdtime);
12e41d03
DL
524 return;
525 }
526
527 /* Prune to RPF'(S,G) */
528
529 if (source_flags & PIM_RPT_BIT_MASK) {
530 if (source_flags & PIM_WILDCARD_BIT_MASK) {
531 /* Prune(*,G) to RPF'(S,G) */
532 pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)",
63c59d0c 533 up, up->rpf.rpf_addr.u.prefix4);
12e41d03
DL
534 return;
535 }
536
537 /* Prune(S,G,rpt) to RPF'(S,G) */
538 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)",
63c59d0c 539 up, up->rpf.rpf_addr.u.prefix4);
12e41d03
DL
540 return;
541 }
542
543 /* Prune(S,G) to RPF'(S,G) */
544 pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up,
63c59d0c 545 up->rpf.rpf_addr.u.prefix4);
12e41d03
DL
546}
547
548static int nonlocal_upstream(int is_join,
549 struct interface *recv_ifp,
550 struct in_addr upstream,
4ed0af70 551 struct prefix_sg *sg,
12e41d03
DL
552 uint8_t source_flags,
553 uint16_t holdtime)
554{
555 struct pim_interface *recv_pim_ifp;
556 int is_local; /* boolean */
557
558 recv_pim_ifp = recv_ifp->info;
559 zassert(recv_pim_ifp);
560
561 is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr);
562
563 if (PIM_DEBUG_PIM_TRACE) {
eaa54bdb 564 char up_str[INET_ADDRSTRLEN];
12e41d03 565 pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str));
01d45d04 566 zlog_warn("%s: recv %s (S,G)=%s to %s upstream=%s on %s",
12e41d03
DL
567 __PRETTY_FUNCTION__,
568 is_join ? "join" : "prune",
01d45d04 569 pim_str_sg_dump (sg),
12e41d03
DL
570 is_local ? "local" : "non-local",
571 up_str, recv_ifp->name);
572 }
573
574 if (is_local)
575 return 0;
576
577 /*
578 Since recv upstream addr was not directed to our primary
579 address, check if we should react to it in any way.
580 */
01d45d04 581 check_recv_upstream(is_join, recv_ifp, upstream, sg,
12e41d03
DL
582 source_flags, holdtime);
583
584 return 1; /* non-local */
585}
586
587void pim_ifchannel_join_add(struct interface *ifp,
588 struct in_addr neigh_addr,
589 struct in_addr upstream,
4ed0af70 590 struct prefix_sg *sg,
12e41d03
DL
591 uint8_t source_flags,
592 uint16_t holdtime)
593{
594 struct pim_interface *pim_ifp;
595 struct pim_ifchannel *ch;
596
597 if (nonlocal_upstream(1 /* join */, ifp, upstream,
01d45d04 598 sg, source_flags, holdtime)) {
12e41d03
DL
599 return;
600 }
601
4a40c37a 602 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
12e41d03
DL
603 if (!ch)
604 return;
605
606 /*
607 RFC 4601: 4.6.1. (S,G) Assert Message State Machine
608
609 Transitions from "I am Assert Loser" State
610
611 Receive Join(S,G) on Interface I
612
613 We receive a Join(S,G) that has the Upstream Neighbor Address
614 field set to my primary IP address on interface I. The action is
615 to transition to NoInfo state, delete this (S,G) assert state
616 (Actions A5 below), and allow the normal PIM Join/Prune mechanisms
617 to operate.
618
619 Notice: The nonlocal_upstream() test above ensures the upstream
620 address of the join message is our primary address.
621 */
622 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
eaa54bdb 623 char neigh_str[INET_ADDRSTRLEN];
12e41d03 624 pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str));
5074a423 625 zlog_warn("%s: Assert Loser recv Join%s from %s on %s",
12e41d03 626 __PRETTY_FUNCTION__,
ee1a477a 627 pim_str_sg_dump (sg), neigh_str, ifp->name);
12e41d03
DL
628
629 assert_action_a5(ch);
630 }
631
632 pim_ifp = ifp->info;
633 zassert(pim_ifp);
634
635 switch (ch->ifjoin_state) {
636 case PIM_IFJOIN_NOINFO:
637 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
638 if (pim_macro_chisin_oiflist(ch)) {
5b668dd7 639 pim_upstream_inherited_olist (ch->upstream);
12e41d03
DL
640 pim_forward_start(ch);
641 }
642 break;
643 case PIM_IFJOIN_JOIN:
644 zassert(!ch->t_ifjoin_prune_pending_timer);
645
646 /*
647 In the JOIN state ch->t_ifjoin_expiry_timer may be NULL due to a
648 previously received join message with holdtime=0xFFFF.
649 */
650 if (ch->t_ifjoin_expiry_timer) {
651 unsigned long remain =
652 thread_timer_remain_second(ch->t_ifjoin_expiry_timer);
653 if (remain > holdtime) {
654 /*
655 RFC 4601: 4.5.3. Receiving (S,G) Join/Prune Messages
656
657 Transitions from Join State
658
659 The (S,G) downstream state machine on interface I remains in
660 Join state, and the Expiry Timer (ET) is restarted, set to
661 maximum of its current value and the HoldTime from the
662 triggering Join/Prune message.
663
664 Conclusion: Do not change the ET if the current value is
665 higher than the received join holdtime.
666 */
667 return;
668 }
669 }
670 THREAD_OFF(ch->t_ifjoin_expiry_timer);
671 break;
672 case PIM_IFJOIN_PRUNE_PENDING:
673 zassert(!ch->t_ifjoin_expiry_timer);
674 zassert(ch->t_ifjoin_prune_pending_timer);
675 THREAD_OFF(ch->t_ifjoin_prune_pending_timer);
676 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN);
677 break;
678 }
679
12e41d03
DL
680 if (holdtime != 0xFFFF) {
681 THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer,
682 on_ifjoin_expiry_timer,
683 ch, holdtime);
684 }
685}
686
687void pim_ifchannel_prune(struct interface *ifp,
688 struct in_addr upstream,
4ed0af70 689 struct prefix_sg *sg,
12e41d03
DL
690 uint8_t source_flags,
691 uint16_t holdtime)
692{
693 struct pim_ifchannel *ch;
694 int jp_override_interval_msec;
695
696 if (nonlocal_upstream(0 /* prune */, ifp, upstream,
01d45d04 697 sg, source_flags, holdtime)) {
12e41d03
DL
698 return;
699 }
700
4a40c37a 701 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM);
12e41d03
DL
702 if (!ch)
703 return;
704
705 switch (ch->ifjoin_state) {
706 case PIM_IFJOIN_NOINFO:
707 case PIM_IFJOIN_PRUNE_PENDING:
708 /* nothing to do */
709 break;
710 case PIM_IFJOIN_JOIN:
711 {
712 struct pim_interface *pim_ifp;
713
714 pim_ifp = ifp->info;
715
716 zassert(ch->t_ifjoin_expiry_timer);
717 zassert(!ch->t_ifjoin_prune_pending_timer);
718
719 THREAD_OFF(ch->t_ifjoin_expiry_timer);
720
721 pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING);
722
723 if (listcount(pim_ifp->pim_neighbor_list) > 1) {
724 jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp);
725 }
726 else {
727 jp_override_interval_msec = 0; /* schedule to expire immediately */
728 /* If we called ifjoin_prune() directly instead, care should
729 be taken not to use "ch" afterwards since it would be
730 deleted. */
731 }
732
733 THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer,
734 on_ifjoin_prune_pending_timer,
735 ch, jp_override_interval_msec);
736
737 zassert(!ch->t_ifjoin_expiry_timer);
738 zassert(ch->t_ifjoin_prune_pending_timer);
739 }
740 break;
741 }
742
743}
744
745void pim_ifchannel_local_membership_add(struct interface *ifp,
4ed0af70 746 struct prefix_sg *sg)
12e41d03
DL
747{
748 struct pim_ifchannel *ch;
749 struct pim_interface *pim_ifp;
750
751 /* PIM enabled on interface? */
752 pim_ifp = ifp->info;
753 if (!pim_ifp)
754 return;
755 if (!PIM_IF_TEST_PIM(pim_ifp->options))
756 return;
757
4a40c37a 758 ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP);
12e41d03
DL
759 if (!ch) {
760 return;
761 }
762
763 ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE);
764
dfbbce1d
DS
765 if (sg->src.s_addr == INADDR_ANY)
766 {
767 struct pim_upstream *up = pim_upstream_find (sg);
768 struct pim_upstream *child;
769 struct listnode *up_node;
770
0f588989 771 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, up_node, child))
dfbbce1d
DS
772 {
773 if (child->parent == up)
774 {
775 if (PIM_DEBUG_EVENTS)
776 {
777 char buff[100];
778
779 strcpy (buff, pim_str_sg_dump (&up->sg));
594a78cc 780 zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s",
dfbbce1d 781 __FILE__, __PRETTY_FUNCTION__,
594a78cc 782 buff, ifp->name, pim_str_sg_dump (sg));
dfbbce1d
DS
783 }
784
785 if (pim_upstream_evaluate_join_desired (child))
786 {
787 pim_channel_add_oif (child->channel_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
788 pim_upstream_switch (child, PIM_UPSTREAM_JOINED);
789 }
790 }
791 }
792 }
12e41d03
DL
793}
794
795void pim_ifchannel_local_membership_del(struct interface *ifp,
4ed0af70 796 struct prefix_sg *sg)
12e41d03
DL
797{
798 struct pim_ifchannel *ch;
799 struct pim_interface *pim_ifp;
800
801 /* PIM enabled on interface? */
802 pim_ifp = ifp->info;
803 if (!pim_ifp)
804 return;
805 if (!PIM_IF_TEST_PIM(pim_ifp->options))
806 return;
807
1103466b 808 ch = pim_ifchannel_find(ifp, sg);
12e41d03
DL
809 if (!ch)
810 return;
811
812 ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO);
813
dfbbce1d
DS
814 if (sg->src.s_addr == INADDR_ANY)
815 {
816 struct pim_upstream *up = pim_upstream_find (sg);
817 struct pim_upstream *child;
818 struct listnode *up_node;
819
0f588989 820 for (ALL_LIST_ELEMENTS_RO (pim_upstream_list, up_node, child))
dfbbce1d
DS
821 {
822 if (child->parent == up)
823 {
824 struct channel_oil *c_oil = child->channel_oil;
7c4002dd 825 struct pim_ifchannel *chchannel = pim_ifchannel_find (ifp, &child->sg);
dfbbce1d
DS
826 struct pim_interface *pim_ifp = ifp->info;
827
828 if (PIM_DEBUG_EVENTS)
829 {
830 char buff[100];
831 strcpy (buff, pim_str_sg_dump (&up->sg));
594a78cc 832 zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s",
dfbbce1d 833 __FILE__, __PRETTY_FUNCTION__,
594a78cc 834 buff, ifp->name, pim_str_sg_dump (&child->sg));
dfbbce1d
DS
835 }
836
837 if (!pim_upstream_evaluate_join_desired (child))
838 pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
839
840 /*
841 * If the S,G has no if channel and the c_oil still
842 * has output here then the *,G was supplying the implied
843 * if channel. So remove it.
844 */
7c4002dd 845 if (!chchannel && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index])
dfbbce1d
DS
846 pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_PIM);
847 }
848 }
849 }
12e41d03
DL
850 delete_on_noinfo(ch);
851}
852
853void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch)
854{
855 int old_couldassert = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_COULD_ASSERT(ch->flags));
856 int new_couldassert = PIM_FORCE_BOOLEAN(pim_macro_ch_could_assert_eval(ch));
857
858 if (new_couldassert == old_couldassert)
859 return;
860
861 if (PIM_DEBUG_PIM_EVENTS) {
eaa54bdb
DW
862 char src_str[INET_ADDRSTRLEN];
863 char grp_str[INET_ADDRSTRLEN];
4ed0af70
DS
864 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
865 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
866 zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d",
867 __PRETTY_FUNCTION__,
868 src_str, grp_str, ch->interface->name,
869 old_couldassert, new_couldassert);
870 }
871
872 if (new_couldassert) {
873 /* CouldAssert(S,G,I) switched from FALSE to TRUE */
874 PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags);
875 }
876 else {
877 /* CouldAssert(S,G,I) switched from TRUE to FALSE */
878 PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags);
879
880 if (ch->ifassert_state == PIM_IFASSERT_I_AM_WINNER) {
881 assert_action_a4(ch);
882 }
883 }
884
885 pim_ifchannel_update_my_assert_metric(ch);
886}
887
888/*
889 my_assert_metric may be affected by:
890
891 CouldAssert(S,G)
892 pim_ifp->primary_address
893 rpf->source_nexthop.mrib_metric_preference;
894 rpf->source_nexthop.mrib_route_metric;
895 */
896void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch)
897{
898 struct pim_assert_metric my_metric_new = pim_macro_ch_my_assert_metric_eval(ch);
899
900 if (pim_assert_metric_match(&my_metric_new, &ch->ifassert_my_metric))
901 return;
902
903 if (PIM_DEBUG_PIM_EVENTS) {
eaa54bdb
DW
904 char src_str[INET_ADDRSTRLEN];
905 char grp_str[INET_ADDRSTRLEN];
906 char old_addr_str[INET_ADDRSTRLEN];
907 char new_addr_str[INET_ADDRSTRLEN];
4ed0af70
DS
908 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
909 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
910 pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str));
911 pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str));
912 zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s",
913 __PRETTY_FUNCTION__,
914 src_str, grp_str, ch->interface->name,
915 ch->ifassert_my_metric.rpt_bit_flag,
916 ch->ifassert_my_metric.metric_preference,
917 ch->ifassert_my_metric.route_metric,
918 old_addr_str,
919 my_metric_new.rpt_bit_flag,
920 my_metric_new.metric_preference,
921 my_metric_new.route_metric,
922 new_addr_str);
923 }
924
925 ch->ifassert_my_metric = my_metric_new;
926
927 if (pim_assert_metric_better(&ch->ifassert_my_metric,
928 &ch->ifassert_winner_metric)) {
929 assert_action_a5(ch);
930 }
931}
932
933void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch)
934{
935 int old_atd = PIM_FORCE_BOOLEAN(PIM_IF_FLAG_TEST_ASSERT_TRACKING_DESIRED(ch->flags));
936 int new_atd = PIM_FORCE_BOOLEAN(pim_macro_assert_tracking_desired_eval(ch));
937
938 if (new_atd == old_atd)
939 return;
940
941 if (PIM_DEBUG_PIM_EVENTS) {
eaa54bdb
DW
942 char src_str[INET_ADDRSTRLEN];
943 char grp_str[INET_ADDRSTRLEN];
4ed0af70
DS
944 pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str));
945 pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str));
12e41d03
DL
946 zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d",
947 __PRETTY_FUNCTION__,
948 src_str, grp_str, ch->interface->name,
949 old_atd, new_atd);
950 }
951
952 if (new_atd) {
953 /* AssertTrackingDesired(S,G,I) switched from FALSE to TRUE */
954 PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags);
955 }
956 else {
957 /* AssertTrackingDesired(S,G,I) switched from TRUE to FALSE */
958 PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags);
959
960 if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) {
961 assert_action_a5(ch);
962 }
963 }
964}
c8507a16
DS
965
966/*
967 * If we have a new pim interface, check to
968 * see if any of the pre-existing channels have
969 * their upstream out that way and turn on forwarding
970 * for that ifchannel then.
971 */
972void
973pim_ifchannel_scan_forward_start (struct interface *new_ifp)
974{
975 struct listnode *ifnode;
976 struct interface *ifp;
977 struct pim_interface *new_pim_ifp = new_ifp->info;
978
979 for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp))
980 {
981 struct pim_interface *loop_pim_ifp = ifp->info;
982 struct listnode *ch_node;
983 struct pim_ifchannel *ch;
984
985 if (!loop_pim_ifp)
986 continue;
987
988 if (new_pim_ifp == loop_pim_ifp)
989 continue;
990
991 for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp->pim_ifchannel_list, ch_node, ch))
992 {
993 if (ch->ifjoin_state == PIM_IFJOIN_JOIN)
994 {
995 struct pim_upstream *up = ch->upstream;
996 if ((!up->channel_oil) &&
997 (up->rpf.source_nexthop.interface == new_ifp))
998 pim_forward_start (ch);
999 }
1000 }
1001 }
1002}