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