diff options
Diffstat (limited to 'pimd/pim_ifchannel.c')
| -rw-r--r-- | pimd/pim_ifchannel.c | 926 |
1 files changed, 631 insertions, 295 deletions
diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 7afb7a5bdf..f0e4a3a68a 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -24,6 +24,7 @@ #include "thread.h" #include "memory.h" #include "if.h" +#include "vrf.h" #include "pimd.h" #include "pim_str.h" @@ -36,13 +37,100 @@ #include "pim_join.h" #include "pim_rpf.h" #include "pim_macro.h" +#include "pim_oil.h" +#include "pim_upstream.h" -void pim_ifchannel_free(struct pim_ifchannel *ch) +int +pim_ifchannel_compare (struct pim_ifchannel *ch1, struct pim_ifchannel *ch2) +{ + struct pim_interface *pim_ifp1; + struct pim_interface *pim_ifp2; + + if (ntohl(ch1->sg.grp.s_addr) < ntohl(ch2->sg.grp.s_addr)) + return -1; + + if (ntohl(ch1->sg.grp.s_addr) > ntohl(ch2->sg.grp.s_addr)) + return 1; + + if (ntohl(ch1->sg.src.s_addr) < ntohl(ch2->sg.src.s_addr)) + return -1; + + if (ntohl(ch1->sg.src.s_addr) > ntohl(ch2->sg.src.s_addr)) + return 1; + + pim_ifp1 = ch1->interface->info; + pim_ifp2 = ch2->interface->info; + if (ntohl(pim_ifp1->primary_address.s_addr) < ntohl(pim_ifp2->primary_address.s_addr)) + return -1; + + if (ntohl(pim_ifp1->primary_address.s_addr) > ntohl(pim_ifp2->primary_address.s_addr)) + return 1; + + if (pim_ifp1->mroute_vif_index < pim_ifp2->mroute_vif_index) + return -1; + + if (pim_ifp1->mroute_vif_index > pim_ifp2->mroute_vif_index) + return 1; + + return 0; +} + +/* + * A (*,G) or a (*,*) is going away + * remove the parent pointer from + * those pointing at us + */ +static void +pim_ifchannel_remove_children (struct pim_ifchannel *ch) { - zassert(!ch->t_ifjoin_expiry_timer); - zassert(!ch->t_ifjoin_prune_pending_timer); - zassert(!ch->t_ifassert_timer); + struct pim_ifchannel *child; + + if (!ch->sources) + return; + + while (!list_isempty (ch->sources)) + { + child = listnode_head (ch->sources); + child->parent = NULL; + listnode_delete (ch->sources, child); + } +} + +/* + * A (*,G) or a (*,*) is being created + * find all the children that would point + * at us. + */ +static void +pim_ifchannel_find_new_children (struct pim_ifchannel *ch) +{ + struct pim_interface *pim_ifp = ch->interface->info; + struct pim_ifchannel *child; + struct listnode *ch_node; + + // Basic Sanity that we are not being silly + if ((ch->sg.src.s_addr != INADDR_ANY) && + (ch->sg.grp.s_addr != INADDR_ANY)) + return; + + if ((ch->sg.src.s_addr == INADDR_ANY) && + (ch->sg.grp.s_addr == INADDR_ANY)) + return; + + for (ALL_LIST_ELEMENTS_RO (pim_ifp->pim_ifchannel_list, ch_node, child)) + { + if ((ch->sg.grp.s_addr != INADDR_ANY) && + (child->sg.grp.s_addr == ch->sg.grp.s_addr) && + (child != ch)) + { + child->parent = ch; + listnode_add_sort (ch->sources, child); + } + } +} +void pim_ifchannel_free(struct pim_ifchannel *ch) +{ XFREE(MTYPE_PIM_IFCHANNEL, ch); } @@ -51,48 +139,91 @@ void pim_ifchannel_delete(struct pim_ifchannel *ch) struct pim_interface *pim_ifp; pim_ifp = ch->interface->info; - zassert(pim_ifp); + + if (ch->upstream->channel_oil) + { + uint32_t mask = PIM_OIF_FLAG_PROTO_PIM; + if (ch->upstream->flags & PIM_UPSTREAM_FLAG_MASK_SRC_IGMP) + mask = PIM_OIF_FLAG_PROTO_IGMP; + + pim_channel_del_oif (ch->upstream->channel_oil, ch->interface, mask); + /* + * Do we have any S,G's that are inheriting? + * Nuke from on high too. + */ + if (ch->upstream->sources) + { + struct pim_upstream *child; + struct listnode *up_node; + + for (ALL_LIST_ELEMENTS_RO (ch->upstream->sources, up_node, child)) + pim_channel_del_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR); + } + } + + /* + * When this channel is removed + * we need to find all our children + * and make sure our pointers are fixed + */ + pim_ifchannel_remove_children (ch); + + if (ch->sources) + list_delete (ch->sources); if (ch->ifjoin_state != PIM_IFJOIN_NOINFO) { pim_upstream_update_join_desired(ch->upstream); } - pim_upstream_del(ch->upstream); + pim_upstream_del(ch->upstream, __PRETTY_FUNCTION__); + ch->upstream = NULL; THREAD_OFF(ch->t_ifjoin_expiry_timer); THREAD_OFF(ch->t_ifjoin_prune_pending_timer); THREAD_OFF(ch->t_ifassert_timer); + if (ch->parent) + { + listnode_delete (ch->parent->sources, ch); + ch->parent = NULL; + } /* notice that listnode_delete() can't be moved into pim_ifchannel_free() because the later is called by list_delete_all_node() */ listnode_delete(pim_ifp->pim_ifchannel_list, ch); + listnode_delete(pim_ifchannel_list, ch); pim_ifchannel_free(ch); } -#define IFCHANNEL_NOINFO(ch) \ - ( \ - ((ch)->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO) \ - && \ - ((ch)->ifjoin_state == PIM_IFJOIN_NOINFO) \ - && \ - ((ch)->ifassert_state == PIM_IFASSERT_NOINFO) \ - ) +void +pim_ifchannel_delete_all (struct interface *ifp) +{ + struct pim_interface *pim_ifp; + struct listnode *ifchannel_node; + struct listnode *ifchannel_nextnode; + struct pim_ifchannel *ifchannel; + + pim_ifp = ifp->info; + if (!pim_ifp) + return; + + for (ALL_LIST_ELEMENTS (pim_ifp->pim_ifchannel_list, ifchannel_node, + ifchannel_nextnode, ifchannel)) + { + pim_ifchannel_delete (ifchannel); + } +} static void delete_on_noinfo(struct pim_ifchannel *ch) { - if (IFCHANNEL_NOINFO(ch)) { - - /* In NOINFO state, timers should have been cleared */ - zassert(!ch->t_ifjoin_expiry_timer); - zassert(!ch->t_ifjoin_prune_pending_timer); - zassert(!ch->t_ifassert_timer); - + if (ch->local_ifmembership == PIM_IFMEMBERSHIP_NOINFO && + ch->ifjoin_state == PIM_IFJOIN_NOINFO && + ch->t_ifjoin_expiry_timer == NULL) pim_ifchannel_delete(ch); - } + } void pim_ifchannel_ifjoin_switch(const char *caller, @@ -101,6 +232,14 @@ void pim_ifchannel_ifjoin_switch(const char *caller, { enum pim_ifjoin_state old_state = ch->ifjoin_state; + if (PIM_DEBUG_PIM_EVENTS) + zlog_debug ("PIM_IFCHANNEL(%s): %s is switching from %s to %s", + ch->interface->name, + ch->sg_str, + pim_ifchannel_ifjoin_name (ch->ifjoin_state), + pim_ifchannel_ifjoin_name (new_state)); + + if (old_state == new_state) { if (PIM_DEBUG_PIM_EVENTS) { zlog_debug("%s calledby %s: non-transition on state %d (%s)", @@ -110,25 +249,72 @@ void pim_ifchannel_ifjoin_switch(const char *caller, return; } - zassert(old_state != new_state); - ch->ifjoin_state = new_state; + if (ch->sg.src.s_addr == INADDR_ANY) + { + struct pim_upstream *up = ch->upstream; + struct pim_upstream *child; + struct listnode *up_node; + + if (up) + { + if (ch->ifjoin_state == PIM_IFJOIN_NOINFO) + { + for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child)) + { + struct channel_oil *c_oil = child->channel_oil; + struct pim_interface *pim_ifp = ch->interface->info; + + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s %s: Prune(S,G)=%s from %s", + __FILE__, __PRETTY_FUNCTION__, + child->sg_str, up->sg_str); + if (!c_oil) + continue; + + if (!pim_upstream_evaluate_join_desired (child)) + { + pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR); + pim_upstream_update_join_desired (child); + } + + /* + * If the S,G has no if channel and the c_oil still + * has output here then the *,G was supplying the implied + * if channel. So remove it. + * I think this is dead code now. is it? + */ + if (!ch && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) + pim_channel_del_oif (c_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR); + } + } + if (ch->ifjoin_state == PIM_IFJOIN_JOIN) + { + for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child)) + { + if (PIM_DEBUG_PIM_TRACE) + zlog_debug("%s %s: Join(S,G)=%s from %s", + __FILE__, __PRETTY_FUNCTION__, + child->sg_str, up->sg_str); + + if (pim_upstream_evaluate_join_desired (child)) + { + pim_channel_add_oif (child->channel_oil, ch->interface, PIM_OIF_FLAG_PROTO_STAR); + pim_upstream_update_join_desired (child); + } + } + } + } + } /* Transition to/from NOINFO ? */ - if ( - (old_state == PIM_IFJOIN_NOINFO) - || - (new_state == PIM_IFJOIN_NOINFO) - ) { + if ((old_state == PIM_IFJOIN_NOINFO) || + (new_state == PIM_IFJOIN_NOINFO)) { if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("PIM_IFCHANNEL_%s: (S,G)=(%s,%s) on interface %s", + zlog_debug("PIM_IFCHANNEL_%s: (S,G)=%s on interface %s", ((new_state == PIM_IFJOIN_NOINFO) ? "DOWN" : "UP"), - src_str, grp_str, ch->interface->name); + ch->sg_str, ch->interface->name); } /* @@ -145,9 +331,12 @@ void pim_ifchannel_ifjoin_switch(const char *caller, const char *pim_ifchannel_ifjoin_name(enum pim_ifjoin_state ifjoin_state) { switch (ifjoin_state) { - case PIM_IFJOIN_NOINFO: return "NOINFO"; - case PIM_IFJOIN_JOIN: return "JOIN"; - case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP"; + case PIM_IFJOIN_NOINFO: return "NOINFO"; + case PIM_IFJOIN_JOIN: return "JOIN"; + case PIM_IFJOIN_PRUNE: return "PRUNE"; + case PIM_IFJOIN_PRUNE_PENDING: return "PRUNEP"; + case PIM_IFJOIN_PRUNE_TMP: return "PRUNET"; + case PIM_IFJOIN_PRUNE_PENDING_TMP: return "PRUNEPT"; } return "ifjoin_bad_state"; @@ -180,77 +369,8 @@ void reset_ifassert_state(struct pim_ifchannel *ch) qpim_infinite_assert_metric); } -static struct pim_ifchannel *pim_ifchannel_new(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) -{ - struct pim_ifchannel *ch; - struct pim_interface *pim_ifp; - struct pim_upstream *up; - - pim_ifp = ifp->info; - zassert(pim_ifp); - - up = pim_upstream_add(source_addr, group_addr, NULL); - if (!up) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); - zlog_err("%s: could not attach upstream (S,G)=(%s,%s) on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); - return 0; - } - - ch = XMALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch)); - if (!ch) { - zlog_err("%s: PIM XMALLOC(%zu) failure", - __PRETTY_FUNCTION__, sizeof(*ch)); - return 0; - } - - ch->flags = 0; - ch->upstream = up; - ch->interface = ifp; - ch->source_addr = source_addr; - ch->group_addr = group_addr; - ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO; - - ch->ifjoin_state = PIM_IFJOIN_NOINFO; - ch->t_ifjoin_expiry_timer = 0; - ch->t_ifjoin_prune_pending_timer = 0; - ch->ifjoin_creation = 0; - - ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); - ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch); - - ch->ifassert_winner.s_addr = 0; - - /* Assert state */ - ch->t_ifassert_timer = 0; - reset_ifassert_state(ch); - if (pim_macro_ch_could_assert_eval(ch)) - PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); - else - PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); - - if (pim_macro_assert_tracking_desired_eval(ch)) - PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); - else - PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); - - /* Attach to list */ - listnode_add(pim_ifp->pim_ifchannel_list, ch); - - zassert(IFCHANNEL_NOINFO(ch)); - - return ch; -} - struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) + struct prefix_sg *sg) { struct pim_interface *pim_ifp; struct listnode *ch_node; @@ -261,21 +381,17 @@ struct pim_ifchannel *pim_ifchannel_find(struct interface *ifp, pim_ifp = ifp->info; if (!pim_ifp) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: (S,G)=(%s,%s): multicast not enabled on interface %s", + zlog_warn("%s: (S,G)=%s: multicast not enabled on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, + pim_str_sg_dump (sg), ifp->name); return 0; } for (ALL_LIST_ELEMENTS_RO(pim_ifp->pim_ifchannel_list, ch_node, ch)) { if ( - (source_addr.s_addr == ch->source_addr.s_addr) && - (group_addr.s_addr == ch->group_addr.s_addr) + (sg->src.s_addr == ch->sg.src.s_addr) && + (sg->grp.s_addr == ch->sg.grp.s_addr) ) { return ch; } @@ -291,13 +407,9 @@ static void ifmembership_set(struct pim_ifchannel *ch, return; if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str)); - zlog_debug("%s: (S,G)=(%s,%s) membership now is %s on interface %s", + zlog_debug("%s: (S,G)=%s membership now is %s on interface %s", __PRETTY_FUNCTION__, - src_str, grp_str, + ch->sg_str, membership == PIM_IFMEMBERSHIP_INCLUDE ? "INCLUDE" : "NOINFO", ch->interface->name); } @@ -339,29 +451,111 @@ void pim_ifchannel_delete_on_noinfo(struct interface *ifp) } } -struct pim_ifchannel *pim_ifchannel_add(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) +/* + * For a given Interface, if we are given a S,G + * Find the *,G (If we have it). + * If we are passed a *,G, find the *,* ifchannel + * if we have it. + */ +static struct pim_ifchannel * +pim_ifchannel_find_parent (struct pim_ifchannel *ch) { + struct prefix_sg parent_sg = ch->sg; + struct pim_ifchannel *parent = NULL; + + // (S,G) + if ((parent_sg.src.s_addr != INADDR_ANY) && + (parent_sg.grp.s_addr != INADDR_ANY)) + { + parent_sg.src.s_addr = INADDR_ANY; + parent = pim_ifchannel_find (ch->interface, &parent_sg); + + if (parent) + listnode_add (parent->sources, ch); + return parent; + } + + return NULL; +} + +struct pim_ifchannel * +pim_ifchannel_add(struct interface *ifp, + struct prefix_sg *sg, int flags) +{ + struct pim_interface *pim_ifp; struct pim_ifchannel *ch; - char src_str[100]; - char grp_str[100]; + struct pim_upstream *up; - ch = pim_ifchannel_find(ifp, source_addr, group_addr); + ch = pim_ifchannel_find(ifp, sg); if (ch) return ch; - ch = pim_ifchannel_new(ifp, source_addr, group_addr); - if (ch) - return ch; - - pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=(%s,%s) on interface %s", - __PRETTY_FUNCTION__, - src_str, grp_str, ifp->name); + pim_ifp = ifp->info; - return 0; + up = pim_upstream_add(sg, NULL, flags, __PRETTY_FUNCTION__); + if (!up) { + zlog_err("%s: could not attach upstream (S,G)=%s on interface %s", + __PRETTY_FUNCTION__, + pim_str_sg_dump (sg), ifp->name); + return NULL; + } + + ch = XCALLOC(MTYPE_PIM_IFCHANNEL, sizeof(*ch)); + if (!ch) { + zlog_warn("%s: pim_ifchannel_new() failure for (S,G)=%s on interface %s", + __PRETTY_FUNCTION__, + up->sg_str, ifp->name); + + pim_upstream_del (up, __PRETTY_FUNCTION__); + return NULL; + } + + ch->flags = 0; + ch->upstream = up; + ch->interface = ifp; + ch->sg = *sg; + pim_str_sg_set (sg, ch->sg_str); + ch->parent = pim_ifchannel_find_parent (ch); + if (ch->sg.src.s_addr == INADDR_ANY) + { + ch->sources = list_new (); + ch->sources->cmp = (int (*)(void *, void *))pim_ifchannel_compare; + } + else + ch->sources = NULL; + + pim_ifchannel_find_new_children (ch); + ch->local_ifmembership = PIM_IFMEMBERSHIP_NOINFO; + + ch->ifjoin_state = PIM_IFJOIN_NOINFO; + ch->t_ifjoin_expiry_timer = NULL; + ch->t_ifjoin_prune_pending_timer = NULL; + ch->ifjoin_creation = 0; + + ch->ifassert_my_metric = pim_macro_ch_my_assert_metric_eval(ch); + ch->ifassert_winner_metric = pim_macro_ch_my_assert_metric_eval (ch); + + ch->ifassert_winner.s_addr = 0; + + /* Assert state */ + ch->t_ifassert_timer = NULL; + ch->ifassert_state = PIM_IFASSERT_NOINFO; + reset_ifassert_state(ch); + if (pim_macro_ch_could_assert_eval(ch)) + PIM_IF_FLAG_SET_COULD_ASSERT(ch->flags); + else + PIM_IF_FLAG_UNSET_COULD_ASSERT(ch->flags); + + if (pim_macro_assert_tracking_desired_eval(ch)) + PIM_IF_FLAG_SET_ASSERT_TRACKING_DESIRED(ch->flags); + else + PIM_IF_FLAG_UNSET_ASSERT_TRACKING_DESIRED(ch->flags); + + /* Attach to list */ + listnode_add_sort(pim_ifp->pim_ifchannel_list, ch); + listnode_add_sort(pim_ifchannel_list, ch); + + return ch; } static void ifjoin_to_noinfo(struct pim_ifchannel *ch) @@ -375,13 +569,9 @@ static int on_ifjoin_expiry_timer(struct thread *t) { struct pim_ifchannel *ch; - zassert(t); ch = THREAD_ARG(t); - zassert(ch); - ch->t_ifjoin_expiry_timer = 0; - - zassert(ch->ifjoin_state == PIM_IFJOIN_JOIN); + ch->t_ifjoin_expiry_timer = NULL; ifjoin_to_noinfo(ch); /* ch may have been deleted */ @@ -389,64 +579,37 @@ static int on_ifjoin_expiry_timer(struct thread *t) return 0; } -static void prune_echo(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) -{ - struct pim_interface *pim_ifp; - struct in_addr neigh_dst_addr; - - pim_ifp = ifp->info; - zassert(pim_ifp); - - neigh_dst_addr = pim_ifp->primary_address; - - if (PIM_DEBUG_PIM_EVENTS) { - char source_str[100]; - char group_str[100]; - char neigh_dst_str[100]; - pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); - pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); - pim_inet4_dump("<neigh?>", neigh_dst_addr, neigh_dst_str, sizeof(neigh_dst_str)); - zlog_debug("%s: sending PruneEcho(S,G)=(%s,%s) to upstream=%s on interface %s", - __PRETTY_FUNCTION__, source_str, group_str, neigh_dst_str, ifp->name); - } - - pim_joinprune_send(ifp, neigh_dst_addr, source_addr, group_addr, - 0 /* boolean: send_join=false (prune) */); -} - static int on_ifjoin_prune_pending_timer(struct thread *t) { struct pim_ifchannel *ch; int send_prune_echo; /* boolean */ struct interface *ifp; struct pim_interface *pim_ifp; - struct in_addr ch_source; - struct in_addr ch_group; - zassert(t); ch = THREAD_ARG(t); - zassert(ch); - - ch->t_ifjoin_prune_pending_timer = 0; - zassert(ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING); + ch->t_ifjoin_prune_pending_timer = NULL; - /* Send PruneEcho(S,G) ? */ - ifp = ch->interface; - pim_ifp = ifp->info; - send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1); - - /* Save (S,G) */ - ch_source = ch->source_addr; - ch_group = ch->group_addr; + if (ch->ifjoin_state == PIM_IFJOIN_PRUNE_PENDING) + { + /* Send PruneEcho(S,G) ? */ + ifp = ch->interface; + pim_ifp = ifp->info; + send_prune_echo = (listcount(pim_ifp->pim_neighbor_list) > 1); - ifjoin_to_noinfo(ch); - /* from here ch may have been deleted */ + ifjoin_to_noinfo(ch); + /* from here ch may have been deleted */ - if (send_prune_echo) - prune_echo(ifp, ch_source, ch_group); + if (send_prune_echo) + pim_joinprune_send (ifp, pim_ifp->primary_address, + ch->upstream, 0); + } + else + { + zlog_warn("%s: IFCHANNEL%s Prune Pending Timer Popped while in %s state", + __PRETTY_FUNCTION__, pim_str_sg_dump (&ch->sg), + pim_ifchannel_ifjoin_name (ch->ifjoin_state)); + } return 0; } @@ -454,15 +617,14 @@ static int on_ifjoin_prune_pending_timer(struct thread *t) static void check_recv_upstream(int is_join, struct interface *recv_ifp, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, int holdtime) { struct pim_upstream *up; /* Upstream (S,G) in Joined state ? */ - up = pim_upstream_find(source_addr, group_addr); + up = pim_upstream_find(sg); if (!up) return; if (up->join_state != PIM_UPSTREAM_JOINED) @@ -470,31 +632,23 @@ static void check_recv_upstream(int is_join, /* Upstream (S,G) in Joined state */ - if (PIM_INADDR_IS_ANY(up->rpf.rpf_addr)) { + if (pim_rpf_addr_is_inaddr_any(&up->rpf)) { /* RPF'(S,G) not found */ - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s %s: RPF'(%s,%s) not found", + zlog_warn("%s %s: RPF'%s not found", __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str); + up->sg_str); return; } /* upstream directed to RPF'(S,G) ? */ - if (upstream.s_addr != up->rpf.rpf_addr.s_addr) { - char src_str[100]; - char grp_str[100]; - char up_str[100]; - char rpf_str[100]; - pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); + if (upstream.s_addr != up->rpf.rpf_addr.u.prefix4.s_addr) { + char up_str[INET_ADDRSTRLEN]; + char rpf_str[PREFIX_STRLEN]; pim_inet4_dump("<up?>", upstream, up_str, sizeof(up_str)); - pim_inet4_dump("<rpf?>", up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); - zlog_warn("%s %s: (S,G)=(%s,%s) upstream=%s not directed to RPF'(S,G)=%s on interface %s", + pim_addr_dump("<rpf?>", &up->rpf.rpf_addr, rpf_str, sizeof(rpf_str)); + zlog_warn("%s %s: (S,G)=%s upstream=%s not directed to RPF'(S,G)=%s on interface %s", __FILE__, __PRETTY_FUNCTION__, - src_str, grp_str, + up->sg_str, up_str, rpf_str, recv_ifp->name); return; } @@ -502,7 +656,7 @@ static void check_recv_upstream(int is_join, if (is_join) { /* Join(S,G) to RPF'(S,G) */ - pim_upstream_join_suppress(up, up->rpf.rpf_addr, holdtime); + pim_upstream_join_suppress(up, up->rpf.rpf_addr.u.prefix4, holdtime); return; } @@ -512,26 +666,25 @@ static void check_recv_upstream(int is_join, if (source_flags & PIM_WILDCARD_BIT_MASK) { /* Prune(*,G) to RPF'(S,G) */ pim_upstream_join_timer_decrease_to_t_override("Prune(*,G)", - up, up->rpf.rpf_addr); + up, up->rpf.rpf_addr.u.prefix4); return; } /* Prune(S,G,rpt) to RPF'(S,G) */ pim_upstream_join_timer_decrease_to_t_override("Prune(S,G,rpt)", - up, up->rpf.rpf_addr); + up, up->rpf.rpf_addr.u.prefix4); return; } /* Prune(S,G) to RPF'(S,G) */ pim_upstream_join_timer_decrease_to_t_override("Prune(S,G)", up, - up->rpf.rpf_addr); + up->rpf.rpf_addr.u.prefix4); } static int nonlocal_upstream(int is_join, struct interface *recv_ifp, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime) { @@ -543,17 +696,13 @@ static int nonlocal_upstream(int is_join, is_local = (upstream.s_addr == recv_pim_ifp->primary_address.s_addr); - if (PIM_DEBUG_PIM_TRACE) { - char up_str[100]; - char src_str[100]; - char grp_str[100]; + if (PIM_DEBUG_PIM_TRACE_DETAIL) { + char up_str[INET_ADDRSTRLEN]; pim_inet4_dump("<upstream?>", upstream, up_str, sizeof(up_str)); - pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); - zlog_warn("%s: recv %s (S,G)=(%s,%s) to %s upstream=%s on %s", + zlog_warn("%s: recv %s (S,G)=%s to %s upstream=%s on %s", __PRETTY_FUNCTION__, is_join ? "join" : "prune", - src_str, grp_str, + pim_str_sg_dump (sg), is_local ? "local" : "non-local", up_str, recv_ifp->name); } @@ -565,7 +714,7 @@ static int nonlocal_upstream(int is_join, Since recv upstream addr was not directed to our primary address, check if we should react to it in any way. */ - check_recv_upstream(is_join, recv_ifp, upstream, source_addr, group_addr, + check_recv_upstream(is_join, recv_ifp, upstream, sg, source_flags, holdtime); return 1; /* non-local */ @@ -574,8 +723,7 @@ static int nonlocal_upstream(int is_join, void pim_ifchannel_join_add(struct interface *ifp, struct in_addr neigh_addr, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime) { @@ -583,11 +731,11 @@ void pim_ifchannel_join_add(struct interface *ifp, struct pim_ifchannel *ch; if (nonlocal_upstream(1 /* join */, ifp, upstream, - source_addr, group_addr, source_flags, holdtime)) { + sg, source_flags, holdtime)) { return; } - ch = pim_ifchannel_add(ifp, source_addr, group_addr); + ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM); if (!ch) return; @@ -608,15 +756,11 @@ void pim_ifchannel_join_add(struct interface *ifp, address of the join message is our primary address. */ if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { - char src_str[100]; - char grp_str[100]; - char neigh_str[100]; - pim_inet4_dump("<src?>", source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", group_addr, grp_str, sizeof(grp_str)); + char neigh_str[INET_ADDRSTRLEN]; pim_inet4_dump("<neigh?>", neigh_addr, neigh_str, sizeof(neigh_str)); - zlog_warn("%s: Assert Loser recv Join(%s,%s) from %s on %s", + zlog_warn("%s: Assert Loser recv Join%s from %s on %s", __PRETTY_FUNCTION__, - src_str, grp_str, neigh_str, ifp->name); + ch->sg_str, neigh_str, ifp->name); assert_action_a5(ch); } @@ -628,6 +772,7 @@ void pim_ifchannel_join_add(struct interface *ifp, case PIM_IFJOIN_NOINFO: pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); if (pim_macro_chisin_oiflist(ch)) { + pim_upstream_inherited_olist (ch->upstream); pim_forward_start(ch); } break; @@ -660,16 +805,26 @@ void pim_ifchannel_join_add(struct interface *ifp, } THREAD_OFF(ch->t_ifjoin_expiry_timer); break; + case PIM_IFJOIN_PRUNE: + if (source_flags & PIM_ENCODE_RPT_BIT) + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); + break; case PIM_IFJOIN_PRUNE_PENDING: - zassert(!ch->t_ifjoin_expiry_timer); - zassert(ch->t_ifjoin_prune_pending_timer); THREAD_OFF(ch->t_ifjoin_prune_pending_timer); - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); + if (source_flags & PIM_ENCODE_RPT_BIT) + { + THREAD_OFF(ch->t_ifjoin_expiry_timer); + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_NOINFO); + } + else + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_JOIN); + break; + case PIM_IFJOIN_PRUNE_TMP: + break; + case PIM_IFJOIN_PRUNE_PENDING_TMP: break; } - zassert(!IFCHANNEL_NOINFO(ch)); - if (holdtime != 0xFFFF) { THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, on_ifjoin_expiry_timer, @@ -679,66 +834,113 @@ void pim_ifchannel_join_add(struct interface *ifp, void pim_ifchannel_prune(struct interface *ifp, struct in_addr upstream, - struct in_addr source_addr, - struct in_addr group_addr, + struct prefix_sg *sg, uint8_t source_flags, uint16_t holdtime) { struct pim_ifchannel *ch; + struct pim_interface *pim_ifp; int jp_override_interval_msec; if (nonlocal_upstream(0 /* prune */, ifp, upstream, - source_addr, group_addr, source_flags, holdtime)) { + sg, source_flags, holdtime)) { return; } - ch = pim_ifchannel_add(ifp, source_addr, group_addr); + ch = pim_ifchannel_find (ifp, sg); + if (!ch && !(source_flags & PIM_ENCODE_RPT_BIT)) + { + if (PIM_DEBUG_TRACE) + zlog_debug ("%s: Received prune with no relevant ifchannel %s(%s) state: %d", + __PRETTY_FUNCTION__, ifp->name, pim_str_sg_dump (sg), source_flags); + return; + } + + ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_PIM); if (!ch) return; + pim_ifp = ifp->info; + switch (ch->ifjoin_state) { case PIM_IFJOIN_NOINFO: + if (source_flags & PIM_ENCODE_RPT_BIT) + { + PIM_IF_FLAG_SET_S_G_RPT(ch->flags); + ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING; + if (listcount(pim_ifp->pim_neighbor_list) > 1) + jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp); + else + jp_override_interval_msec = 0; /* schedule to expire immediately */ + /* If we called ifjoin_prune() directly instead, care should + be taken not to use "ch" afterwards since it would be + deleted. */ + + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + THREAD_OFF(ch->t_ifjoin_expiry_timer); + THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer, + on_ifjoin_prune_pending_timer, + ch, jp_override_interval_msec); + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); + } + break; case PIM_IFJOIN_PRUNE_PENDING: /* nothing to do */ break; case PIM_IFJOIN_JOIN: - { - struct pim_interface *pim_ifp; - - pim_ifp = ifp->info; + THREAD_OFF(ch->t_ifjoin_expiry_timer); - zassert(ch->t_ifjoin_expiry_timer); - zassert(!ch->t_ifjoin_prune_pending_timer); + pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING); - THREAD_OFF(ch->t_ifjoin_expiry_timer); - - pim_ifchannel_ifjoin_switch(__PRETTY_FUNCTION__, ch, PIM_IFJOIN_PRUNE_PENDING); - - if (listcount(pim_ifp->pim_neighbor_list) > 1) { - jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp); + if (listcount(pim_ifp->pim_neighbor_list) > 1) + jp_override_interval_msec = pim_if_jp_override_interval_msec(ifp); + else + jp_override_interval_msec = 0; /* schedule to expire immediately */ + /* If we called ifjoin_prune() directly instead, care should + be taken not to use "ch" afterwards since it would be + deleted. */ + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer, + on_ifjoin_prune_pending_timer, + ch, jp_override_interval_msec); + break; + case PIM_IFJOIN_PRUNE: + if (source_flags & PIM_ENCODE_RPT_BIT) + { + THREAD_OFF(ch->t_ifjoin_prune_pending_timer); + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); } - else { - jp_override_interval_msec = 0; /* schedule to expire immediately */ - /* If we called ifjoin_prune() directly instead, care should - be taken not to use "ch" afterwards since it would be - deleted. */ + break; + case PIM_IFJOIN_PRUNE_TMP: + if (source_flags & PIM_ENCODE_RPT_BIT) + { + ch->ifjoin_state = PIM_IFJOIN_PRUNE; + THREAD_OFF(ch->t_ifjoin_expiry_timer); + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); + } + break; + case PIM_IFJOIN_PRUNE_PENDING_TMP: + if (source_flags & PIM_ENCODE_RPT_BIT) + { + ch->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING; + THREAD_OFF(ch->t_ifjoin_expiry_timer); + THREAD_TIMER_ON(master, ch->t_ifjoin_expiry_timer, + on_ifjoin_expiry_timer, + ch, holdtime); } - - THREAD_TIMER_MSEC_ON(master, ch->t_ifjoin_prune_pending_timer, - on_ifjoin_prune_pending_timer, - ch, jp_override_interval_msec); - - zassert(!ch->t_ifjoin_expiry_timer); - zassert(ch->t_ifjoin_prune_pending_timer); - } break; } - } -void pim_ifchannel_local_membership_add(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) +int +pim_ifchannel_local_membership_add(struct interface *ifp, + struct prefix_sg *sg) { struct pim_ifchannel *ch; struct pim_interface *pim_ifp; @@ -746,23 +948,43 @@ void pim_ifchannel_local_membership_add(struct interface *ifp, /* PIM enabled on interface? */ pim_ifp = ifp->info; if (!pim_ifp) - return; + return 0; if (!PIM_IF_TEST_PIM(pim_ifp->options)) - return; + return 0; - ch = pim_ifchannel_add(ifp, source_addr, group_addr); + ch = pim_ifchannel_add(ifp, sg, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP); if (!ch) { - return; + return 0; } ifmembership_set(ch, PIM_IFMEMBERSHIP_INCLUDE); - zassert(!IFCHANNEL_NOINFO(ch)); + if (sg->src.s_addr == INADDR_ANY) + { + struct pim_upstream *up = pim_upstream_find (sg); + struct pim_upstream *child; + struct listnode *up_node; + + for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child)) + { + if (PIM_DEBUG_EVENTS) + zlog_debug("%s %s: IGMP (S,G)=%s(%s) from %s", + __FILE__, __PRETTY_FUNCTION__, + child->sg_str, ifp->name, up->sg_str); + + if (pim_upstream_evaluate_join_desired (child)) + { + pim_channel_add_oif (child->channel_oil, ifp, PIM_OIF_FLAG_PROTO_STAR); + pim_upstream_switch (child, PIM_UPSTREAM_JOINED); + } + } + } + + return 1; } void pim_ifchannel_local_membership_del(struct interface *ifp, - struct in_addr source_addr, - struct in_addr group_addr) + struct prefix_sg *sg) { struct pim_ifchannel *ch; struct pim_interface *pim_ifp; @@ -774,12 +996,41 @@ void pim_ifchannel_local_membership_del(struct interface *ifp, if (!PIM_IF_TEST_PIM(pim_ifp->options)) return; - ch = pim_ifchannel_find(ifp, source_addr, group_addr); + ch = pim_ifchannel_find(ifp, sg); if (!ch) return; ifmembership_set(ch, PIM_IFMEMBERSHIP_NOINFO); + if (sg->src.s_addr == INADDR_ANY) + { + struct pim_upstream *up = pim_upstream_find (sg); + struct pim_upstream *child; + struct listnode *up_node; + + for (ALL_LIST_ELEMENTS_RO (up->sources, up_node, child)) + { + struct channel_oil *c_oil = child->channel_oil; + struct pim_ifchannel *chchannel = pim_ifchannel_find (ifp, &child->sg); + struct pim_interface *pim_ifp = ifp->info; + + if (PIM_DEBUG_EVENTS) + zlog_debug("%s %s: Prune(S,G)=%s(%s) from %s", + __FILE__, __PRETTY_FUNCTION__, + up->sg_str, ifp->name, child->sg_str); + + if (c_oil && !pim_upstream_evaluate_join_desired (child)) + pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_STAR); + + /* + * If the S,G has no if channel and the c_oil still + * has output here then the *,G was supplying the implied + * if channel. So remove it. + */ + if (!chchannel && c_oil && c_oil->oil.mfcc_ttls[pim_ifp->mroute_vif_index]) + pim_channel_del_oif (c_oil, ifp, PIM_OIF_FLAG_PROTO_STAR); + } + } delete_on_noinfo(ch); } @@ -792,10 +1043,10 @@ void pim_ifchannel_update_could_assert(struct pim_ifchannel *ch) return; if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str)); + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str)); zlog_debug("%s: CouldAssert(%s,%s,%s) changed from %d to %d", __PRETTY_FUNCTION__, src_str, grp_str, ch->interface->name, @@ -834,12 +1085,12 @@ void pim_ifchannel_update_my_assert_metric(struct pim_ifchannel *ch) return; if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - char old_addr_str[100]; - char new_addr_str[100]; - pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str)); + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + char old_addr_str[INET_ADDRSTRLEN]; + char new_addr_str[INET_ADDRSTRLEN]; + pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str)); pim_inet4_dump("<old_addr?>", ch->ifassert_my_metric.ip_address, old_addr_str, sizeof(old_addr_str)); pim_inet4_dump("<new_addr?>", my_metric_new.ip_address, new_addr_str, sizeof(new_addr_str)); zlog_debug("%s: my_assert_metric(%s,%s,%s) changed from %u,%u,%u,%s to %u,%u,%u,%s", @@ -872,10 +1123,10 @@ void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch) return; if (PIM_DEBUG_PIM_EVENTS) { - char src_str[100]; - char grp_str[100]; - pim_inet4_dump("<src?>", ch->source_addr, src_str, sizeof(src_str)); - pim_inet4_dump("<grp?>", ch->group_addr, grp_str, sizeof(grp_str)); + char src_str[INET_ADDRSTRLEN]; + char grp_str[INET_ADDRSTRLEN]; + pim_inet4_dump("<src?>", ch->sg.src, src_str, sizeof(src_str)); + pim_inet4_dump("<grp?>", ch->sg.grp, grp_str, sizeof(grp_str)); zlog_debug("%s: AssertTrackingDesired(%s,%s,%s) changed from %d to %d", __PRETTY_FUNCTION__, src_str, grp_str, ch->interface->name, @@ -895,3 +1146,88 @@ void pim_ifchannel_update_assert_tracking_desired(struct pim_ifchannel *ch) } } } + +/* + * If we have a new pim interface, check to + * see if any of the pre-existing channels have + * their upstream out that way and turn on forwarding + * for that ifchannel then. + */ +void +pim_ifchannel_scan_forward_start (struct interface *new_ifp) +{ + struct listnode *ifnode; + struct interface *ifp; + struct pim_interface *new_pim_ifp = new_ifp->info; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), ifnode, ifp)) + { + struct pim_interface *loop_pim_ifp = ifp->info; + struct listnode *ch_node; + struct pim_ifchannel *ch; + + if (!loop_pim_ifp) + continue; + + if (new_pim_ifp == loop_pim_ifp) + continue; + + for (ALL_LIST_ELEMENTS_RO (loop_pim_ifp->pim_ifchannel_list, ch_node, ch)) + { + if (ch->ifjoin_state == PIM_IFJOIN_JOIN) + { + struct pim_upstream *up = ch->upstream; + if ((!up->channel_oil) && + (up->rpf.source_nexthop.interface == new_ifp)) + pim_forward_start (ch); + } + } + } +} + +/* + * Downstream per-interface (S,G,rpt) state machine + * states that we need to move (S,G,rpt) items + * into different states at the start of the + * reception of a *,G join as well, when + * we get End of Message + */ +void +pim_ifchannel_set_star_g_join_state (struct pim_ifchannel *ch, int eom) +{ + struct pim_ifchannel *child; + struct listnode *ch_node; + + if (PIM_DEBUG_PIM_TRACE) + zlog_debug ("%s: %s %s eom: %d", __PRETTY_FUNCTION__, + pim_ifchannel_ifjoin_name(ch->ifjoin_state), + ch->sg_str, eom); + if (!ch->sources) + return; + + for (ALL_LIST_ELEMENTS_RO (ch->sources, ch_node, child)) + { + if (!PIM_IF_FLAG_TEST_S_G_RPT(child->flags)) + continue; + + switch (child->ifjoin_state) + { + case PIM_IFJOIN_NOINFO: + case PIM_IFJOIN_JOIN: + break; + case PIM_IFJOIN_PRUNE: + if (!eom) + child->ifjoin_state = PIM_IFJOIN_PRUNE_TMP; + break; + case PIM_IFJOIN_PRUNE_PENDING: + if (!eom) + child->ifjoin_state = PIM_IFJOIN_PRUNE_PENDING_TMP; + break; + case PIM_IFJOIN_PRUNE_TMP: + case PIM_IFJOIN_PRUNE_PENDING_TMP: + if (eom) + child->ifjoin_state = PIM_IFJOIN_NOINFO; + break; + } + } +} |
