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