diff options
Diffstat (limited to 'pimd/pim_rp.c')
| -rw-r--r-- | pimd/pim_rp.c | 786 |
1 files changed, 757 insertions, 29 deletions
diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 26d108bcaa..dc19002a42 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -20,48 +20,598 @@ */ #include <zebra.h> +#include "lib/json.h" #include "log.h" #include "network.h" #include "if.h" +#include "linklist.h" +#include "prefix.h" +#include "memory.h" +#include "vty.h" +#include "vrf.h" +#include "plist.h" #include "pimd.h" +#include "pim_vty.h" #include "pim_str.h" +#include "pim_iface.h" #include "pim_rp.h" #include "pim_str.h" #include "pim_rpf.h" +#include "pim_sock.h" +#include "pim_memory.h" +#include "pim_iface.h" +#include "pim_msdp.h" -static int i_am_rp = 0; +struct rp_info +{ + struct prefix group; + struct pim_rpf rp; + int i_am_rp; + char *plist; +}; + +static struct list *qpim_rp_list = NULL; +static struct rp_info *tail = NULL; + +static void +pim_rp_info_free (struct rp_info *rp_info) +{ + XFREE (MTYPE_PIM_RP, rp_info); +} + +static int +pim_rp_list_cmp (void *v1, void *v2) +{ + struct rp_info *rp1 = (struct rp_info *)v1; + struct rp_info *rp2 = (struct rp_info *)v2; + + if (rp1 == rp2) + return 0; + + if (!rp1 && rp2) + return -1; + + if (rp1 && !rp2) + return 1; + + /* + * Sort by RP IP address + */ + if (rp1->rp.rpf_addr.u.prefix4.s_addr < rp2->rp.rpf_addr.u.prefix4.s_addr) + return -1; + + if (rp1->rp.rpf_addr.u.prefix4.s_addr > rp2->rp.rpf_addr.u.prefix4.s_addr) + return 1; + + /* + * Sort by group IP address + */ + if (rp1->group.u.prefix4.s_addr < rp2->group.u.prefix4.s_addr) + return -1; + + if (rp1->group.u.prefix4.s_addr > rp2->group.u.prefix4.s_addr) + return 1; + + if (rp1 == tail) + return 1; + + return -1; +} + +void +pim_rp_init (void) +{ + struct rp_info *rp_info; + + qpim_rp_list = list_new (); + qpim_rp_list->del = (void (*)(void *))pim_rp_info_free; + qpim_rp_list->cmp = pim_rp_list_cmp; + + rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info)); + + if (!rp_info) + return; + + str2prefix ("224.0.0.0/4", &rp_info->group); + rp_info->group.family = AF_INET; + rp_info->rp.rpf_addr.family = AF_INET; + rp_info->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE; + tail = rp_info; + + listnode_add (qpim_rp_list, rp_info); +} + +void +pim_rp_free (void) +{ + if (qpim_rp_list) + list_delete (qpim_rp_list); + qpim_rp_list = NULL; +} + +/* + * Given an RP's prefix-list, return the RP's rp_info for that prefix-list + */ +static struct rp_info * +pim_rp_find_prefix_list (struct in_addr rp, const char *plist) +{ + struct listnode *node; + struct rp_info *rp_info; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr && + rp_info->plist && strcmp(rp_info->plist, plist) == 0) + { + return rp_info; + } + } + + return NULL; +} + +/* + * Return true if plist is used by any rp_info + */ +static int +pim_rp_prefix_list_used (const char *plist) +{ + struct listnode *node; + struct rp_info *rp_info; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp_info->plist && strcmp(rp_info->plist, plist) == 0) + { + return 1; + } + } + + return 0; +} /* - * Checks to see if we should elect ourself the actual RP + * Given an RP's address, return the RP's rp_info that is an exact match for 'group' */ +static struct rp_info * +pim_rp_find_exact (struct in_addr rp, struct prefix *group) +{ + struct listnode *node; + struct rp_info *rp_info; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr && + prefix_same (&rp_info->group, group)) + return rp_info; + } + + return NULL; +} + +/* + * Given a group, return the rp_info for that group + */ +static struct rp_info * +pim_rp_find_match_group (struct prefix *group) +{ + struct listnode *node; + struct rp_info *rp_info; + struct prefix_list *plist; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp_info->plist) + { + plist = prefix_list_lookup (AFI_IP, rp_info->plist); + + if (plist && prefix_list_apply (plist, group) == PREFIX_PERMIT) + return rp_info; + } + else + { + if (prefix_match (&rp_info->group, group)) + return rp_info; + } + } + + return NULL; +} + +/* + * When the user makes "ip pim rp" configuration changes or if they change the + * prefix-list(s) used by these statements we must tickle the upstream state + * for each group to make them re-lookup who their RP should be. + * + * This is a placeholder function for now. + */ +static void +pim_rp_refresh_group_to_rp_mapping() +{ + pim_msdp_i_am_rp_changed(); +} + void -pim_rp_check_rp (struct in_addr old, struct in_addr new) -{ - if (PIM_DEBUG_ZEBRA) { - char sold[100]; - char snew[100]; - char rp[100]; - pim_inet4_dump("<rp?>", qpim_rp.rpf_addr, rp, sizeof(rp)); - pim_inet4_dump("<old?>", old, sold, sizeof(sold)); - pim_inet4_dump("<new?>", new, snew, sizeof(snew)); - zlog_debug("%s: %s for old %s new %s", __func__, rp, sold, snew ); +pim_rp_prefix_list_update (struct prefix_list *plist) +{ + struct listnode *node; + struct rp_info *rp_info; + int refresh_needed = 0; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp_info->plist && strcmp(rp_info->plist, prefix_list_name (plist)) == 0) + { + refresh_needed = 1; + break; + } + } + + if (refresh_needed) + pim_rp_refresh_group_to_rp_mapping(); +} + +static int +pim_rp_check_interface_addrs(struct rp_info *rp_info, + struct pim_interface *pim_ifp) +{ + struct listnode *node; + struct pim_secondary_addr *sec_addr; + + if (pim_ifp->primary_address.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) + return 1; + + if (!pim_ifp->sec_addr_list) { + return 0; } - if (qpim_rp.rpf_addr.s_addr == INADDR_NONE) - return; + for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { + if (sec_addr->addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) { + return 1; + } + } + + return 0; +} + +static void +pim_rp_check_interfaces (struct rp_info *rp_info) +{ + struct listnode *node; + struct interface *ifp; + + rp_info->i_am_rp = 0; + for (ALL_LIST_ELEMENTS_RO (vrf_iflist (VRF_DEFAULT), node, ifp)) + { + struct pim_interface *pim_ifp = ifp->info; + + if (!pim_ifp) + continue; + + if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) { + rp_info->i_am_rp = 1; + } + } +} + +int +pim_rp_new (const char *rp, const char *group_range, const char *plist) +{ + int result; + struct rp_info *rp_info; + struct rp_info *rp_all; + struct prefix group_all; + struct listnode *node, *nnode; + struct rp_info *tmp_rp_info; + char buffer[BUFSIZ]; + + rp_info = XCALLOC (MTYPE_PIM_RP, sizeof (*rp_info)); + if (!rp_info) + return PIM_MALLOC_FAIL; + + if (group_range == NULL) + result = str2prefix ("224.0.0.0/4", &rp_info->group); + else + result = str2prefix (group_range, &rp_info->group); + + if (!result) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_GROUP_BAD_ADDRESS; + } + + rp_info->rp.rpf_addr.family = AF_INET; + result = inet_pton (rp_info->rp.rpf_addr.family, rp, &rp_info->rp.rpf_addr.u.prefix4); + + if (result <= 0) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_RP_BAD_ADDRESS; + } + + if (plist) + { + /* + * Return if the prefix-list is already configured for this RP + */ + if (pim_rp_find_prefix_list (rp_info->rp.rpf_addr.u.prefix4, plist)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_SUCCESS; + } + + /* + * Barf if the prefix-list is already configured for an RP + */ + if (pim_rp_prefix_list_used (plist)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_RP_PFXLIST_IN_USE; + } + + /* + * Free any existing rp_info entries for this RP + */ + for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info)) + { + if (rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr) + { + if (tmp_rp_info->plist) + pim_rp_del (rp, NULL, tmp_rp_info->plist); + else + pim_rp_del (rp, prefix2str(&tmp_rp_info->group, buffer, BUFSIZ), NULL); + } + } + + rp_info->plist = XSTRDUP(MTYPE_PIM_FILTER_NAME, plist); + } + else + { + str2prefix ("224.0.0.0/4", &group_all); + rp_all = pim_rp_find_match_group(&group_all); + + /* + * Barf if group is a non-multicast subnet + */ + if (! prefix_match (&rp_all->group, &rp_info->group)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_GROUP_BAD_ADDRESS; + } - if (new.s_addr == qpim_rp.rpf_addr.s_addr) + /* + * Remove any prefix-list rp_info entries for this RP + */ + for (ALL_LIST_ELEMENTS (qpim_rp_list, node, nnode, tmp_rp_info)) + { + if (tmp_rp_info->plist && + rp_info->rp.rpf_addr.u.prefix4.s_addr == tmp_rp_info->rp.rpf_addr.u.prefix4.s_addr) + { + pim_rp_del (rp, NULL, tmp_rp_info->plist); + } + } + + /* + * Take over the 224.0.0.0/4 group if the rp is INADDR_NONE + */ + if (prefix_same (&rp_all->group, &rp_info->group) && + pim_rpf_addr_is_inaddr_none (&rp_all->rp)) + { + rp_all->rp.rpf_addr = rp_info->rp.rpf_addr; + XFREE (MTYPE_PIM_RP, rp_info); + + if (pim_nexthop_lookup (&rp_all->rp.source_nexthop, rp_all->rp.rpf_addr.u.prefix4, 1) != 0) + return PIM_RP_NO_PATH; + + pim_rp_check_interfaces (rp_all); + pim_rp_refresh_group_to_rp_mapping(); + return PIM_SUCCESS; + } + + /* + * Return if the group is already configured for this RP + */ + if (pim_rp_find_exact (rp_info->rp.rpf_addr.u.prefix4, &rp_info->group)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_SUCCESS; + } + + /* + * Barf if this group is already covered by some other RP + */ + tmp_rp_info = pim_rp_find_match_group (&rp_info->group); + + if (tmp_rp_info) + { + if (tmp_rp_info->plist) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_GROUP_PFXLIST_OVERLAP; + } + else + { + /* + * If the only RP that covers this group is an RP configured for + * 224.0.0.0/4 that is fine, ignore that one. For all others + * though we must return PIM_GROUP_OVERLAP + */ + if (! prefix_same (&group_all, &tmp_rp_info->group)) + { + XFREE (MTYPE_PIM_RP, rp_info); + return PIM_GROUP_OVERLAP; + } + } + } + } + + listnode_add_sort (qpim_rp_list, rp_info); + + if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0) + return PIM_RP_NO_PATH; + + pim_rp_check_interfaces (rp_info); + pim_rp_refresh_group_to_rp_mapping(); + return PIM_SUCCESS; +} + +int +pim_rp_del (const char *rp, const char *group_range, const char *plist) +{ + struct prefix group; + struct in_addr rp_addr; + struct prefix g_all; + struct rp_info *rp_info; + struct rp_info *rp_all; + int result; + + if (group_range == NULL) + result = str2prefix ("224.0.0.0/4", &group); + else + result = str2prefix (group_range, &group); + + if (!result) + return PIM_GROUP_BAD_ADDRESS; + + result = inet_pton (AF_INET, rp, &rp_addr); + if (result <= 0) + return PIM_RP_BAD_ADDRESS; + + if (plist) + rp_info = pim_rp_find_prefix_list (rp_addr, plist); + else + rp_info = pim_rp_find_exact (rp_addr, &group); + + if (!rp_info) + return PIM_RP_NOT_FOUND; + + if (rp_info->plist) { - i_am_rp = 1; - return; + XFREE(MTYPE_PIM_FILTER_NAME, rp_info->plist); + rp_info->plist = NULL; } - if (old.s_addr == qpim_rp.rpf_addr.s_addr) + str2prefix ("224.0.0.0/4", &g_all); + rp_all = pim_rp_find_match_group (&g_all); + + if (rp_all == rp_info) { - i_am_rp = 0; - return; + rp_all->rp.rpf_addr.family = AF_INET; + rp_all->rp.rpf_addr.u.prefix4.s_addr = INADDR_NONE; + rp_all->i_am_rp = 0; + return PIM_SUCCESS; + } + + listnode_delete (qpim_rp_list, rp_info); + pim_rp_refresh_group_to_rp_mapping(); + return PIM_SUCCESS; +} + +int +pim_rp_setup (void) +{ + struct listnode *node; + struct rp_info *rp_info; + int ret = 0; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (rp_info->rp.rpf_addr.u.prefix4.s_addr == INADDR_NONE) + continue; + + if (pim_nexthop_lookup (&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1) != 0) + { + if (PIM_DEBUG_PIM_TRACE) + zlog_debug ("Unable to lookup nexthop for rp specified"); + ret++; + } } + + if (ret) + return 0; + + return 1; +} + +/* + * Checks to see if we should elect ourself the actual RP when new if + * addresses are added against an interface. + */ +void +pim_rp_check_on_if_add(struct pim_interface *pim_ifp) +{ + struct listnode *node; + struct rp_info *rp_info; + bool i_am_rp_changed = false; + + if (qpim_rp_list == NULL) + return; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) { + if (pim_rpf_addr_is_inaddr_none (&rp_info->rp)) + continue; + + /* if i_am_rp is already set nothing to be done (adding new addresses + * is not going to make a difference). */ + if (rp_info->i_am_rp) { + continue; + } + + if (pim_rp_check_interface_addrs(rp_info, pim_ifp)) { + i_am_rp_changed = true; + rp_info->i_am_rp = 1; + if (PIM_DEBUG_ZEBRA) { + char rp[PREFIX_STRLEN]; + pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp)); + zlog_debug("%s: %s: i am rp", __func__, rp); + } + } + } + + if (i_am_rp_changed) { + pim_msdp_i_am_rp_changed(); + } +} + +/* up-optimized re-evaluation of "i_am_rp". this is used when ifaddresses + * are removed. Removing numbers is an uncommon event in an active network + * so I have made no attempt to optimize it. */ +void +pim_i_am_rp_re_evaluate(void) +{ + struct listnode *node; + struct rp_info *rp_info; + bool i_am_rp_changed = false; + int old_i_am_rp; + + if (qpim_rp_list == NULL) + return; + + for (ALL_LIST_ELEMENTS_RO(qpim_rp_list, node, rp_info)) { + if (pim_rpf_addr_is_inaddr_none(&rp_info->rp)) + continue; + + old_i_am_rp = rp_info->i_am_rp; + pim_rp_check_interfaces(rp_info); + + if (old_i_am_rp != rp_info->i_am_rp) { + i_am_rp_changed = true; + if (PIM_DEBUG_ZEBRA) { + char rp[PREFIX_STRLEN]; + pim_addr_dump("<rp?>", &rp_info->rp.rpf_addr, rp, sizeof(rp)); + if (rp_info->i_am_rp) { + zlog_debug("%s: %s: i am rp", __func__, rp); + } else { + zlog_debug("%s: %s: i am no longer rp", __func__, rp); + } + } + } + } + + if (i_am_rp_changed) { + pim_msdp_i_am_rp_changed(); + } } /* @@ -73,7 +623,20 @@ pim_rp_check_rp (struct in_addr old, struct in_addr new) int pim_rp_i_am_rp (struct in_addr group) { - return i_am_rp; + struct prefix g; + struct rp_info *rp_info; + + memset (&g, 0, sizeof (g)); + g.family = AF_INET; + g.prefixlen = 32; + g.u.prefix4 = group; + + rp_info = pim_rp_find_match_group (&g); + + if (rp_info) + return rp_info->i_am_rp; + + return 0; } /* @@ -84,11 +647,24 @@ pim_rp_i_am_rp (struct in_addr group) struct pim_rpf * pim_rp_g (struct in_addr group) { - /* - * For staticly configured RP, it is always the qpim_rp - */ - pim_nexthop_lookup(&qpim_rp.source_nexthop, qpim_rp.rpf_addr, NULL); - return(&qpim_rp); + struct prefix g; + struct rp_info *rp_info; + + memset (&g, 0, sizeof (g)); + g.family = AF_INET; + g.prefixlen = 32; + g.u.prefix4 = group; + + rp_info = pim_rp_find_match_group (&g); + + if (rp_info) + { + pim_nexthop_lookup(&rp_info->rp.source_nexthop, rp_info->rp.rpf_addr.u.prefix4, 1); + return (&rp_info->rp); + } + + // About to Go Down + return NULL; } /* @@ -100,16 +676,168 @@ pim_rp_g (struct in_addr group) * */ int -pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source) +pim_rp_set_upstream_addr (struct in_addr *up, struct in_addr source, struct in_addr group) { - if ((qpim_rp.rpf_addr.s_addr == INADDR_NONE) && (source.s_addr == INADDR_ANY)) + struct rp_info *rp_info; + struct prefix g; + + memset (&g, 0, sizeof (g)); + g.family = AF_INET; + g.prefixlen = 32; + g.u.prefix4 = group; + + rp_info = pim_rp_find_match_group (&g); + + if ((pim_rpf_addr_is_inaddr_none (&rp_info->rp)) && (source.s_addr == INADDR_ANY)) { if (PIM_DEBUG_PIM_TRACE) zlog_debug("%s: Received a (*,G) with no RP configured", __PRETTY_FUNCTION__); return 0; } - *up = (source.s_addr == INADDR_ANY) ? qpim_rp.rpf_addr : source; + *up = (source.s_addr == INADDR_ANY) ? rp_info->rp.rpf_addr.u.prefix4 : source; return 1; } + +int +pim_rp_config_write (struct vty *vty) +{ + struct listnode *node; + struct rp_info *rp_info; + char rp_buffer[32]; + char group_buffer[32]; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (pim_rpf_addr_is_inaddr_none (&rp_info->rp)) + continue; + + if (rp_info->plist) + vty_out(vty, "ip pim rp %s prefix-list %s%s", + inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp_buffer, 32), + rp_info->plist, VTY_NEWLINE); + else + vty_out(vty, "ip pim rp %s %s%s", + inet_ntop(AF_INET, &rp_info->rp.rpf_addr.u.prefix4, rp_buffer, 32), + prefix2str(&rp_info->group, group_buffer, 32), VTY_NEWLINE); + count++; + } + + return count; +} + +int +pim_rp_check_is_my_ip_address (struct in_addr group, struct in_addr dest_addr) +{ + struct rp_info *rp_info; + struct prefix g; + + memset (&g, 0, sizeof (g)); + g.family = AF_INET; + g.prefixlen = 32; + g.u.prefix4 = group; + + rp_info = pim_rp_find_match_group (&g); + /* + * See if we can short-cut some? + * This might not make sense if we ever leave a static RP + * type of configuration. + * Note - Premature optimization might bite our patooeys' here. + */ + if (I_am_RP(group)) + { + if (dest_addr.s_addr == rp_info->rp.rpf_addr.u.prefix4.s_addr) + return 1; + } + + if (if_lookup_exact_address (&dest_addr, AF_INET)) + return 1; + + return 0; +} + +void +pim_rp_show_information (struct vty *vty, u_char uj) +{ + struct rp_info *rp_info; + struct rp_info *prev_rp_info = NULL; + struct listnode *node; + + json_object *json = NULL; + json_object *json_rp_rows = NULL; + json_object *json_row = NULL; + + if (uj) + json = json_object_new_object(); + else + vty_out (vty, "RP address group/prefix-list OIF I am RP%s", VTY_NEWLINE); + + for (ALL_LIST_ELEMENTS_RO (qpim_rp_list, node, rp_info)) + { + if (!pim_rpf_addr_is_inaddr_none (&rp_info->rp)) + { + char buf[48]; + + if (uj) + { + /* + * If we have moved on to a new RP then add the entry for the previous RP + */ + if (prev_rp_info && + prev_rp_info->rp.rpf_addr.u.prefix4.s_addr != rp_info->rp.rpf_addr.u.prefix4.s_addr) + { + json_object_object_add(json, inet_ntoa (prev_rp_info->rp.rpf_addr.u.prefix4), json_rp_rows); + json_rp_rows = NULL; + } + + if (!json_rp_rows) + json_rp_rows = json_object_new_array(); + + json_row = json_object_new_object(); + if (rp_info->rp.source_nexthop.interface) + json_object_string_add(json_row, "outboundInterface", rp_info->rp.source_nexthop.interface->name); + + if (rp_info->i_am_rp) + json_object_boolean_true_add(json_row, "iAmRP"); + + if (rp_info->plist) + json_object_string_add(json_row, "prefixList", rp_info->plist); + else + json_object_string_add(json_row, "group", prefix2str(&rp_info->group, buf, 48)); + + json_object_array_add(json_rp_rows, json_row); + } + else + { + vty_out (vty, "%-15s ", inet_ntoa (rp_info->rp.rpf_addr.u.prefix4)); + + if (rp_info->plist) + vty_out (vty, "%-18s ", rp_info->plist); + else + vty_out (vty, "%-18s ", prefix2str(&rp_info->group, buf, 48)); + + if (rp_info->rp.source_nexthop.interface) + vty_out (vty, "%-10s ", rp_info->rp.source_nexthop.interface->name); + else + vty_out (vty, "%-10s ", "(Unknown)"); + + if (rp_info->i_am_rp) + vty_out (vty, "yes%s", VTY_NEWLINE); + else + vty_out (vty, "no%s", VTY_NEWLINE); + } + + prev_rp_info = rp_info; + } + } + + if (uj) { + if (prev_rp_info && json_rp_rows) + json_object_object_add(json, inet_ntoa (prev_rp_info->rp.rpf_addr.u.prefix4), json_rp_rows); + + vty_out (vty, "%s%s", json_object_to_json_string_ext(json, JSON_C_TO_STRING_PRETTY), VTY_NEWLINE); + json_object_free(json); + } +} |
