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