diff options
Diffstat (limited to 'pimd/pim_iface.c')
| -rw-r--r-- | pimd/pim_iface.c | 443 |
1 files changed, 361 insertions, 82 deletions
diff --git a/pimd/pim_iface.c b/pimd/pim_iface.c index bac9692caa..cc4f4f3dce 100644 --- a/pimd/pim_iface.c +++ b/pimd/pim_iface.c @@ -25,6 +25,8 @@ #include "memory.h" #include "prefix.h" #include "vrf.h" +#include "linklist.h" +#include "plist.h" #include "pimd.h" #include "pim_iface.h" @@ -38,14 +40,26 @@ #include "pim_sock.h" #include "pim_time.h" #include "pim_ssmpingd.h" +#include "pim_rp.h" struct interface *pim_regiface = NULL; +struct list *pim_ifchannel_list = NULL; static void pim_if_igmp_join_del_all(struct interface *ifp); -void pim_if_init() +void +pim_if_init (void) { vrf_iflist_create(VRF_DEFAULT); + pim_ifchannel_list = list_new(); + pim_ifchannel_list->cmp = (int (*)(void *, void *))pim_ifchannel_compare; +} + +void +pim_if_terminate (void) +{ + if (pim_ifchannel_list) + list_free (pim_ifchannel_list); } static void *if_list_clean(struct pim_interface *pim_ifp) @@ -78,15 +92,16 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) zassert(ifp); zassert(!ifp->info); - pim_ifp = XMALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp)); + pim_ifp = XCALLOC(MTYPE_PIM_INTERFACE, sizeof(*pim_ifp)); if (!pim_ifp) { - zlog_err("PIM XMALLOC(%zu) failure", sizeof(*pim_ifp)); + zlog_err("PIM XCALLOC(%zu) failure", sizeof(*pim_ifp)); return 0; } pim_ifp->options = 0; pim_ifp->mroute_vif_index = -1; + pim_ifp->igmp_version = IGMP_DEFAULT_VERSION; pim_ifp->igmp_default_robustness_variable = IGMP_DEFAULT_ROBUSTNESS_VARIABLE; pim_ifp->igmp_default_query_interval = IGMP_GENERAL_QUERY_INTERVAL; pim_ifp->igmp_query_max_response_time_dsec = IGMP_QUERY_MAX_RESPONSE_TIME_DSEC; @@ -104,15 +119,12 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) if (igmp) PIM_IF_DO_IGMP(pim_ifp->options); -#if 0 - /* FIXME: Should join? */ PIM_IF_DO_IGMP_LISTEN_ALLROUTERS(pim_ifp->options); -#endif - pim_ifp->igmp_join_list = 0; - pim_ifp->igmp_socket_list = 0; - pim_ifp->pim_neighbor_list = 0; - pim_ifp->pim_ifchannel_list = 0; + pim_ifp->igmp_join_list = NULL; + pim_ifp->igmp_socket_list = NULL; + pim_ifp->pim_neighbor_list = NULL; + pim_ifp->pim_ifchannel_list = NULL; pim_ifp->pim_generation_id = 0; /* list of struct igmp_sock */ @@ -141,6 +153,7 @@ struct pim_interface *pim_if_new(struct interface *ifp, int igmp, int pim) return if_list_clean(pim_ifp); } pim_ifp->pim_ifchannel_list->del = (void (*)(void *)) pim_ifchannel_free; + pim_ifp->pim_ifchannel_list->cmp = (int (*)(void *, void *)) pim_ifchannel_compare; ifp->info = pim_ifp; @@ -164,16 +177,11 @@ void pim_if_delete(struct interface *ifp) if (pim_ifp->igmp_join_list) { pim_if_igmp_join_del_all(ifp); } - zassert(!pim_ifp->igmp_join_list); - - zassert(pim_ifp->igmp_socket_list); - zassert(!listcount(pim_ifp->igmp_socket_list)); - zassert(pim_ifp->pim_neighbor_list); - zassert(!listcount(pim_ifp->pim_neighbor_list)); + pim_ifchannel_delete_all (ifp); + igmp_sock_delete_all (ifp); - zassert(pim_ifp->pim_ifchannel_list); - zassert(!listcount(pim_ifp->pim_ifchannel_list)); + pim_neighbor_delete_all (ifp, "Interface removed from configuration"); if (PIM_MROUTE_IS_ENABLED) { pim_if_del_vif(ifp); @@ -185,7 +193,7 @@ void pim_if_delete(struct interface *ifp) XFREE(MTYPE_PIM_INTERFACE, pim_ifp); - ifp->info = 0; + ifp->info = NULL; } void pim_if_update_could_assert(struct interface *ifp) @@ -258,14 +266,10 @@ static int detect_primary_address_change(struct interface *ifp, int force_prim_as_any, const char *caller) { - struct pim_interface *pim_ifp; + struct pim_interface *pim_ifp = ifp->info; struct in_addr new_prim_addr; int changed; - pim_ifp = ifp->info; - if (!pim_ifp) - return 0; - if (force_prim_as_any) new_prim_addr = qpim_inaddr_any; else @@ -274,8 +278,8 @@ static int detect_primary_address_change(struct interface *ifp, changed = new_prim_addr.s_addr != pim_ifp->primary_address.s_addr; if (PIM_DEBUG_ZEBRA) { - char new_prim_str[100]; - char old_prim_str[100]; + char new_prim_str[INET_ADDRSTRLEN]; + char old_prim_str[INET_ADDRSTRLEN]; pim_inet4_dump("<new?>", new_prim_addr, new_prim_str, sizeof(new_prim_str)); pim_inet4_dump("<old?>", pim_ifp->primary_address, old_prim_str, sizeof(old_prim_str)); zlog_debug("%s: old=%s new=%s on interface %s: %s", @@ -286,57 +290,230 @@ static int detect_primary_address_change(struct interface *ifp, if (changed) { pim_ifp->primary_address = new_prim_addr; + } - if (!PIM_IF_TEST_PIM(pim_ifp->options)) { - return changed; + return changed; +} + +static int pim_sec_addr_comp(const void *p1, const void *p2) +{ + const struct pim_secondary_addr *sec1 = p1; + const struct pim_secondary_addr *sec2 = p2; + + if (ntohl(sec1->addr.s_addr) < ntohl(sec2->addr.s_addr)) + return -1; + + if (ntohl(sec1->addr.s_addr) > ntohl(sec2->addr.s_addr)) + return 1; + + return 0; +} + +static void pim_sec_addr_free(struct pim_secondary_addr *sec_addr) +{ + XFREE(MTYPE_PIM_SEC_ADDR, sec_addr); +} + +static struct pim_secondary_addr * +pim_sec_addr_find(struct pim_interface *pim_ifp, struct in_addr addr) +{ + struct pim_secondary_addr *sec_addr; + struct listnode *node; + + if (!pim_ifp->sec_addr_list) { + return NULL; + } + + for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { + if (sec_addr->addr.s_addr == addr.s_addr) { + return sec_addr; } + } - pim_addr_change(ifp); + return NULL; +} + +static void pim_sec_addr_del(struct pim_interface *pim_ifp, + struct pim_secondary_addr *sec_addr) +{ + listnode_delete(pim_ifp->sec_addr_list, sec_addr); + pim_sec_addr_free(sec_addr); +} + +static int pim_sec_addr_add(struct pim_interface *pim_ifp, struct in_addr addr) +{ + int changed = 0; + struct pim_secondary_addr *sec_addr; + + sec_addr = pim_sec_addr_find(pim_ifp, addr); + if (sec_addr) { + sec_addr->flags &= ~PIM_SEC_ADDRF_STALE; + return changed; + } + + if (!pim_ifp->sec_addr_list) { + pim_ifp->sec_addr_list = list_new(); + pim_ifp->sec_addr_list->del = (void (*)(void *))pim_sec_addr_free; + pim_ifp->sec_addr_list->cmp = (int (*)(void *, void *))pim_sec_addr_comp; + } + + sec_addr = XCALLOC(MTYPE_PIM_SEC_ADDR, sizeof(*sec_addr)); + if (!sec_addr) { + if (list_isempty(pim_ifp->sec_addr_list)) { + list_free(pim_ifp->sec_addr_list); + pim_ifp->sec_addr_list = NULL; + } + return changed; + } + + changed = 1; + sec_addr->addr = addr; + listnode_add_sort(pim_ifp->sec_addr_list, sec_addr); + + return changed; +} + +static int pim_sec_addr_del_all(struct pim_interface *pim_ifp) +{ + int changed = 0; + + if (!pim_ifp->sec_addr_list) { + return changed; + } + if (!list_isempty(pim_ifp->sec_addr_list)) { + changed = 1; + /* remove all nodes and free up the list itself */ + list_delete_all_node(pim_ifp->sec_addr_list); + list_free(pim_ifp->sec_addr_list); + pim_ifp->sec_addr_list = NULL; + } + + return changed; +} + +static int pim_sec_addr_update(struct interface *ifp) +{ + struct pim_interface *pim_ifp = ifp->info; + struct connected *ifc; + struct listnode *node; + struct listnode *nextnode; + struct pim_secondary_addr *sec_addr; + int changed = 0; + + if (pim_ifp->sec_addr_list) { + for (ALL_LIST_ELEMENTS_RO(pim_ifp->sec_addr_list, node, sec_addr)) { + sec_addr->flags |= PIM_SEC_ADDRF_STALE; + } + } + + for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { + struct prefix *p = ifc->address; + + if (p->family != AF_INET) { + continue; + } + + if (PIM_INADDR_IS_ANY(p->u.prefix4)) { + continue; + } + + if (pim_ifp->primary_address.s_addr == p->u.prefix4.s_addr) { + /* don't add the primary address into the secondary address list */ + continue; + } + + if (pim_sec_addr_add(pim_ifp, p->u.prefix4)) { + changed = 1; + } + } + + if (pim_ifp->sec_addr_list) { + /* Drop stale entries */ + for (ALL_LIST_ELEMENTS(pim_ifp->sec_addr_list, node, nextnode, sec_addr)) { + if (sec_addr->flags & PIM_SEC_ADDRF_STALE) { + pim_sec_addr_del(pim_ifp, sec_addr); + changed = 1; + } + } + + /* If the list went empty free it up */ + if (list_isempty(pim_ifp->sec_addr_list)) { + list_free(pim_ifp->sec_addr_list); + pim_ifp->sec_addr_list = NULL; + } } return changed; } -static void detect_secondary_address_change(struct interface *ifp, +static int detect_secondary_address_change(struct interface *ifp, + int force_prim_as_any, const char *caller) { + struct pim_interface *pim_ifp = ifp->info; + int changed = 0; + + if (force_prim_as_any) { + /* if primary address is being forced to zero just flush the + * secondary address list */ + changed = pim_sec_addr_del_all(pim_ifp); + } else { + /* re-evaluate the secondary address list */ + changed = pim_sec_addr_update(ifp); + } + + return changed; +} + +static void detect_address_change(struct interface *ifp, + int force_prim_as_any, + const char *caller) +{ + int changed = 0; struct pim_interface *pim_ifp; - int changed; pim_ifp = ifp->info; if (!pim_ifp) return; - changed = 1; /* true */ - if (PIM_DEBUG_ZEBRA) - zlog_debug("FIXME T31 C15 %s: on interface %s: acting on any addr change", - __PRETTY_FUNCTION__, ifp->name); + if (detect_primary_address_change(ifp, force_prim_as_any, caller)) { + changed = 1; + } - if (!changed) { - return; + if (detect_secondary_address_change(ifp, force_prim_as_any, caller)) { + changed = 1; } - if (!PIM_IF_TEST_PIM(pim_ifp->options)) { - return; + + if (changed) { + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + return; + } + + pim_addr_change(ifp); } - pim_addr_change(ifp); + /* XXX: if we have unnumbered interfaces we need to run detect address + * address change on all of them when the lo address changes */ } -static void detect_address_change(struct interface *ifp, - int force_prim_as_any, - const char *caller) +int pim_update_source_set(struct interface *ifp, struct in_addr source) { - int prim_changed; + struct pim_interface *pim_ifp = ifp->info; - prim_changed = detect_primary_address_change(ifp, force_prim_as_any, caller); - if (prim_changed) { - /* no need to detect secondary change because - the reaction would be the same */ - return; + if (!pim_ifp) { + return PIM_IFACE_NOT_FOUND; } - detect_secondary_address_change(ifp, caller); + if (pim_ifp->update_source.s_addr == source.s_addr) { + return PIM_UPDATE_SOURCE_DUP; + } + + pim_ifp->update_source = source; + detect_address_change(ifp, 0 /* force_prim_as_any */, + __PRETTY_FUNCTION__); + + return PIM_SUCCESS; } void pim_if_addr_add(struct connected *ifc) @@ -406,6 +583,7 @@ void pim_if_addr_add(struct connected *ifc) if (pim_ifp->mroute_vif_index < 0) { pim_if_add_vif(ifp); } + pim_ifchannel_scan_forward_start (ifp); } } @@ -496,19 +674,59 @@ void pim_if_addr_add_all(struct interface *ifp) struct connected *ifc; struct listnode *node; struct listnode *nextnode; + int v4_addrs = 0; + int v6_addrs = 0; + struct pim_interface *pim_ifp = ifp->info; + /* PIM/IGMP enabled ? */ - if (!ifp->info) + if (!pim_ifp) return; for (ALL_LIST_ELEMENTS(ifp->connected, node, nextnode, ifc)) { struct prefix *p = ifc->address; if (p->family != AF_INET) - continue; + { + v6_addrs++; + continue; + } + v4_addrs++; pim_if_addr_add(ifc); } + + if (!v4_addrs && v6_addrs && !if_is_loopback (ifp)) + { + if (PIM_IF_TEST_PIM(pim_ifp->options)) { + + /* Interface has a valid primary address ? */ + if (PIM_INADDR_ISNOT_ANY(pim_ifp->primary_address)) { + + /* Interface has a valid socket ? */ + if (pim_ifp->pim_sock_fd < 0) { + if (pim_sock_add(ifp)) { + zlog_warn("Failure creating PIM socket for interface %s", + ifp->name); + } + } + + } + } /* pim */ + } + if (PIM_MROUTE_IS_ENABLED) { + /* + * PIM or IGMP is enabled on interface, and there is at least one + * address assigned, then try to create a vif_index. + */ + if (pim_ifp->mroute_vif_index < 0) { + pim_if_add_vif(ifp); + } + pim_ifchannel_scan_forward_start (ifp); + } + + pim_rp_setup(); + pim_rp_check_on_if_add(pim_ifp); } void pim_if_addr_del_all(struct interface *ifp) @@ -529,6 +747,9 @@ void pim_if_addr_del_all(struct interface *ifp) pim_if_addr_del(ifc, 1 /* force_prim_as_any=true */); } + + pim_rp_setup(); + pim_i_am_rp_re_evaluate(); } void pim_if_addr_del_all_igmp(struct interface *ifp) @@ -571,17 +792,28 @@ void pim_if_addr_del_all_pim(struct interface *ifp) } } -static struct in_addr find_first_nonsec_addr(struct interface *ifp) +struct in_addr +pim_find_primary_addr (struct interface *ifp) { struct connected *ifc; struct listnode *node; struct in_addr addr; + int v4_addrs = 0; + int v6_addrs = 0; + struct pim_interface *pim_ifp = ifp->info; + + if (pim_ifp && PIM_INADDR_ISNOT_ANY(pim_ifp->update_source)) { + return pim_ifp->update_source; + } for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) { struct prefix *p = ifc->address; - + if (p->family != AF_INET) - continue; + { + v6_addrs++; + continue; + } if (PIM_INADDR_IS_ANY(p->u.prefix4)) { zlog_warn("%s: null IPv4 address connected to interface %s", @@ -589,22 +821,33 @@ static struct in_addr find_first_nonsec_addr(struct interface *ifp) continue; } + v4_addrs++; + if (CHECK_FLAG(ifc->flags, ZEBRA_IFA_SECONDARY)) continue; return p->u.prefix4; } + /* + * If we have no v4_addrs and v6 is configured + * We probably are using unnumbered + * So let's grab the loopbacks v4 address + * and use that as the primary address + */ + if (!v4_addrs && v6_addrs && !if_is_loopback (ifp)) + { + struct interface *lo_ifp; + lo_ifp = if_lookup_by_name_vrf ("lo", VRF_DEFAULT); + if (lo_ifp) + return pim_find_primary_addr (lo_ifp); + } + addr.s_addr = PIM_NET_INADDR_ANY; return addr; } -struct in_addr pim_find_primary_addr(struct interface *ifp) -{ - return find_first_nonsec_addr(ifp); -} - static int pim_iface_vif_index = 0; static int @@ -800,9 +1043,9 @@ int pim_if_find_vifindex_by_ifindex(ifindex_t ifindex) struct interface *ifp; ifp = if_lookup_by_index_vrf (ifindex, VRF_DEFAULT); - pim_ifp = ifp->info; - if (!pim_ifp) + if (!ifp || !ifp->info) return -1; + pim_ifp = ifp->info; return pim_ifp->mroute_vif_index; } @@ -899,14 +1142,14 @@ struct pim_neighbor *pim_if_find_neighbor(struct interface *ifp, } if (PIM_DEBUG_PIM_TRACE) { - char addr_str[100]; + char addr_str[INET_ADDRSTRLEN]; pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str)); zlog_debug("%s: neighbor not found for address %s on interface %s", __PRETTY_FUNCTION__, addr_str, ifp->name); } - return 0; + return NULL; } long pim_if_t_suppressed_msec(struct interface *ifp) @@ -985,8 +1228,8 @@ static struct igmp_join *igmp_join_new(struct interface *ifp, join_fd = igmp_join_sock(ifp->name, ifp->ifindex, group_addr, source_addr); if (join_fd < 0) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: igmp_join_sock() failure for IGMP group %s source %s on interface %s", @@ -995,13 +1238,13 @@ static struct igmp_join *igmp_join_new(struct interface *ifp, return 0; } - ij = XMALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij)); + ij = XCALLOC(MTYPE_PIM_IGMP_JOIN, sizeof(*ij)); if (!ij) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); - zlog_err("%s: XMALLOC(%zu) failure for IGMP group %s source %s on interface %s", + zlog_err("%s: XCALLOC(%zu) failure for IGMP group %s source %s on interface %s", __PRETTY_FUNCTION__, sizeof(*ij), group_str, source_str, ifp->name); close(join_fd); @@ -1045,8 +1288,8 @@ int pim_if_igmp_join_add(struct interface *ifp, ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); if (ij) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: can't re-join existing IGMP group %s source %s on interface %s", @@ -1057,8 +1300,8 @@ int pim_if_igmp_join_add(struct interface *ifp, ij = igmp_join_new(ifp, group_addr, source_addr); if (!ij) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: igmp_join_new() failure for IGMP group %s source %s on interface %s", @@ -1068,8 +1311,8 @@ int pim_if_igmp_join_add(struct interface *ifp, } if (PIM_DEBUG_IGMP_EVENTS) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); zlog_debug("%s: issued static igmp join for channel (S,G)=(%s,%s) on interface %s", @@ -1106,8 +1349,8 @@ int pim_if_igmp_join_del(struct interface *ifp, ij = igmp_join_find(pim_ifp->igmp_join_list, group_addr, source_addr); if (!ij) { - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: could not find IGMP group %s source %s on interface %s", @@ -1117,14 +1360,13 @@ int pim_if_igmp_join_del(struct interface *ifp, } if (close(ij->sock_fd)) { - int e = errno; - char group_str[100]; - char source_str[100]; + char group_str[INET_ADDRSTRLEN]; + char source_str[INET_ADDRSTRLEN]; pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str)); pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); zlog_warn("%s: failure closing sock_fd=%d for IGMP group %s source %s on interface %s: errno=%d: %s", __PRETTY_FUNCTION__, - ij->sock_fd, group_str, source_str, ifp->name, e, safe_strerror(e)); + ij->sock_fd, group_str, source_str, ifp->name, errno, safe_strerror(errno)); /* warning only */ } listnode_delete(pim_ifp->igmp_join_list, ij); @@ -1249,3 +1491,40 @@ void pim_if_create_pimreg (void) pim_if_new(pim_regiface, 0, 0); } } + +int +pim_if_connected_to_source (struct interface *ifp, struct in_addr src) +{ + struct listnode *cnode; + struct connected *c; + struct prefix p; + + p.family = AF_INET; + p.u.prefix4 = src; + p.prefixlen = IPV4_MAX_BITLEN; + + for (ALL_LIST_ELEMENTS_RO (ifp->connected, cnode, c)) + { + if ((c->address->family == AF_INET) && + prefix_match (CONNECTED_PREFIX (c), &p)) + { + return 1; + } + } + + return 0; +} + +struct interface * +pim_if_lookup_address_vrf (struct in_addr src, vrf_id_t vrf_id) +{ + struct listnode *ifnode; + struct interface *ifp; + + for (ALL_LIST_ELEMENTS_RO (vrf_iflist(vrf_id), ifnode, ifp)) + { + if (pim_if_connected_to_source (ifp, src) && ifp->info) + return ifp; + } + return NULL; +} |
