From e782cca726674008ccb209ff7dd18451ace7f17c Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 22 Aug 2019 11:29:37 -0300 Subject: [PATCH] isisd: add support for IPv6 BFD peers Make isisd create BFD sessions over IPv6 when IS-IS is configured for IPv6 operation only. When IS-IS is enabled for both IPv4 and IPv6 on a given interface, prefer creating a BFD session over IPv6 to avoid having two BFD sessions protecting the same IS-IS adjacency. Signed-off-by: Renato Westphal --- isisd/isis_bfd.c | 129 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 98 insertions(+), 31 deletions(-) diff --git a/isisd/isis_bfd.c b/isisd/isis_bfd.c index fa89c80049..8fc7997d79 100644 --- a/isisd/isis_bfd.c +++ b/isisd/isis_bfd.c @@ -19,7 +19,9 @@ #include #include "zclient.h" +#include "nexthop.h" #include "bfd.h" +#include "lib_errors.h" #include "isisd/isis_bfd.h" #include "isisd/isis_zebra.h" @@ -33,17 +35,19 @@ DEFINE_MTYPE_STATIC(ISISD, BFD_SESSION, "ISIS BFD Session") struct bfd_session { - struct in_addr dst_ip; - struct in_addr src_ip; + int family; + union g_addr dst_ip; + union g_addr src_ip; int status; }; -static struct bfd_session *bfd_session_new(struct in_addr *dst_ip, - struct in_addr *src_ip) +static struct bfd_session *bfd_session_new(int family, union g_addr *dst_ip, + union g_addr *src_ip) { struct bfd_session *rv; rv = XCALLOC(MTYPE_BFD_SESSION, sizeof(*rv)); + rv->family = family; rv->dst_ip = *dst_ip; rv->src_ip = *src_ip; return rv; @@ -58,15 +62,60 @@ static void bfd_session_free(struct bfd_session **session) *session = NULL; } +static bool bfd_session_same(const struct bfd_session *session, int family, + const union g_addr *src, const union g_addr *dst) +{ + if (session->family != family) + return false; + + switch (session->family) { + case AF_INET: + if (!IPV4_ADDR_SAME(&session->dst_ip.ipv4, &dst->ipv4)) + return false; + if (!IPV4_ADDR_SAME(&session->src_ip.ipv4, &src->ipv4)) + return false; + break; + case AF_INET6: + if (!IPV6_ADDR_SAME(&session->dst_ip.ipv6, &dst->ipv6)) + return false; + if (!IPV6_ADDR_SAME(&session->src_ip.ipv6, &src->ipv6)) + return false; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u", + __func__, session->family); + exit(1); + } + + return true; +} + static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, int new_status) { if (!adj->bfd_session) return; - if (adj->bfd_session->dst_ip.s_addr != dst->u.prefix4.s_addr) + if (adj->bfd_session->family != dst->family) return; + switch (adj->bfd_session->family) { + case AF_INET: + if (!IPV4_ADDR_SAME(&adj->bfd_session->dst_ip.ipv4, + &dst->u.prefix4)) + return; + break; + case AF_INET6: + if (!IPV6_ADDR_SAME(&adj->bfd_session->dst_ip.ipv6, + &dst->u.prefix6)) + return; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, "%s: unknown address-family: %u", + __func__, adj->bfd_session->family); + exit(1); + } + int old_status = adj->bfd_session->status; adj->bfd_session->status = new_status; @@ -76,7 +125,7 @@ static void bfd_adj_event(struct isis_adjacency *adj, struct prefix *dst, if (isis->debugs & DEBUG_BFD) { char dst_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET, &adj->bfd_session->dst_ip, + inet_ntop(adj->bfd_session->family, &adj->bfd_session->dst_ip, dst_str, sizeof(dst_str)); zlog_debug("ISIS-BFD: Peer %s on %s changed from %s to %s", dst_str, adj->circuit->interface->name, @@ -100,14 +149,14 @@ static int isis_bfd_interface_dest_update(ZAPI_CALLBACK_ARGS) ifp = bfd_get_peer_info(zclient->ibuf, &dst_ip, NULL, &status, NULL, vrf_id); - if (!ifp || dst_ip.family != AF_INET) + if (!ifp || (dst_ip.family != AF_INET && dst_ip.family != AF_INET6)) return 0; if (isis->debugs & DEBUG_BFD) { char dst_buf[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET, &dst_ip.u.prefix4, - dst_buf, sizeof(dst_buf)); + inet_ntop(dst_ip.family, &dst_ip.u.prefix, dst_buf, + sizeof(dst_buf)); zlog_debug("ISIS-BFD: Received update for %s on %s: Changed state to %s", dst_buf, ifp->name, bfd_get_status_str(status)); @@ -171,7 +220,7 @@ static void isis_bfd_zebra_connected(struct zclient *zclient) bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); } -static void bfd_debug(struct in_addr *dst, struct in_addr *src, +static void bfd_debug(int family, union g_addr *dst, union g_addr *src, const char *interface, int command) { if (!(isis->debugs & DEBUG_BFD)) @@ -180,8 +229,8 @@ static void bfd_debug(struct in_addr *dst, struct in_addr *src, char dst_str[INET6_ADDRSTRLEN]; char src_str[INET6_ADDRSTRLEN]; - inet_ntop(AF_INET, dst, dst_str, sizeof(dst_str)); - inet_ntop(AF_INET, src, src_str, sizeof(src_str)); + inet_ntop(family, dst, dst_str, sizeof(dst_str)); + inet_ntop(family, src, src_str, sizeof(src_str)); const char *command_str; @@ -209,10 +258,11 @@ static void bfd_handle_adj_down(struct isis_adjacency *adj) if (!adj->bfd_session) return; - bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, - adj->circuit->interface->name, ZEBRA_BFD_DEST_DEREGISTER); + bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip, + &adj->bfd_session->src_ip, adj->circuit->interface->name, + ZEBRA_BFD_DEST_DEREGISTER); - bfd_peer_sendmsg(zclient, NULL, AF_INET, + bfd_peer_sendmsg(zclient, NULL, adj->bfd_session->family, &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, adj->circuit->interface->name, @@ -228,33 +278,50 @@ static void bfd_handle_adj_down(struct isis_adjacency *adj) static void bfd_handle_adj_up(struct isis_adjacency *adj, int command) { struct isis_circuit *circuit = adj->circuit; + int family; + union g_addr dst_ip; + union g_addr src_ip; + struct list *local_ips; + struct prefix *local_ip; - if (!circuit->bfd_info - || !circuit->ip_router - || !adj->ipv4_address_count) + if (!circuit->bfd_info) goto out; - struct list *local_ips = fabricd_ip_addrs(adj->circuit); - - if (!local_ips) + /* + * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer + * creating a BFD session over IPv6. + */ + if (circuit->ipv6_router && adj->ipv6_address_count) { + family = AF_INET6; + dst_ip.ipv6 = adj->ipv6_addresses[0]; + local_ips = circuit->ipv6_link; + if (!local_ips || list_isempty(local_ips)) + goto out; + local_ip = listgetdata(listhead(local_ips)); + src_ip.ipv6 = local_ip->u.prefix6; + } else if (circuit->ip_router && adj->ipv4_address_count) { + family = AF_INET; + dst_ip.ipv4 = adj->ipv4_addresses[0]; + local_ips = fabricd_ip_addrs(adj->circuit); + if (!local_ips || list_isempty(local_ips)) + goto out; + local_ip = listgetdata(listhead(local_ips)); + src_ip.ipv4 = local_ip->u.prefix4; + } else goto out; - struct in_addr *dst_ip = &adj->ipv4_addresses[0]; - struct prefix_ipv4 *local_ip = listgetdata(listhead(local_ips)); - struct in_addr *src_ip = &local_ip->prefix; - if (adj->bfd_session) { - if (adj->bfd_session->dst_ip.s_addr != dst_ip->s_addr - || adj->bfd_session->src_ip.s_addr != src_ip->s_addr) + if (bfd_session_same(adj->bfd_session, family, &src_ip, + &dst_ip)) bfd_handle_adj_down(adj); } if (!adj->bfd_session) - adj->bfd_session = bfd_session_new(dst_ip, src_ip); + adj->bfd_session = bfd_session_new(family, &dst_ip, &src_ip); - bfd_debug(&adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, - circuit->interface->name, command); - bfd_peer_sendmsg(zclient, circuit->bfd_info, AF_INET, + bfd_debug(adj->bfd_session->family, &adj->bfd_session->dst_ip, + &adj->bfd_session->src_ip, circuit->interface->name, command); + bfd_peer_sendmsg(zclient, circuit->bfd_info, adj->bfd_session->family, &adj->bfd_session->dst_ip, &adj->bfd_session->src_ip, circuit->interface->name, -- 2.39.5