diff options
Diffstat (limited to 'zebra')
80 files changed, 14191 insertions, 6966 deletions
diff --git a/zebra/connected.c b/zebra/connected.c index a982ac9b46..3b9ebe14a4 100644 --- a/zebra/connected.c +++ b/zebra/connected.c @@ -312,13 +312,11 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr, if (IPV4_ADDR_SAME(addr, dest)) flog_warn( EC_ZEBRA_IFACE_SAME_LOCAL_AS_PEER, - "warning: interface %s has same local and peer " - "address %s, routing protocols may malfunction", + "warning: interface %s has same local and peer address %s, routing protocols may malfunction", ifp->name, inet_ntoa(*addr)); } else { zlog_debug( - "warning: %s called for interface %s " - "with peer flag set, but no peer address supplied", + "warning: %s called for interface %s with peer flag set, but no peer address supplied", __func__, ifp->name); UNSET_FLAG(ifc->flags, ZEBRA_IFA_PEER); } @@ -328,8 +326,7 @@ void connected_add_ipv4(struct interface *ifp, int flags, struct in_addr *addr, if (!dest && (prefixlen == IPV4_MAX_PREFIXLEN) && if_is_pointopoint(ifp)) zlog_debug( - "warning: PtP interface %s with addr %s/%d needs a " - "peer address", + "warning: PtP interface %s with addr %s/%d needs a peer address", ifp->name, inet_ntoa(*addr), prefixlen); /* Label of this address. */ diff --git a/zebra/debug.c b/zebra/debug.c index c920fca5ff..8c53ab73e4 100644 --- a/zebra/debug.c +++ b/zebra/debug.c @@ -40,6 +40,7 @@ unsigned long zebra_debug_pw; unsigned long zebra_debug_dplane; unsigned long zebra_debug_mlag; unsigned long zebra_debug_nexthop; +unsigned long zebra_debug_evpn_mh; DEFINE_HOOK(zebra_debug_show_debugging, (struct vty *vty), (vty)); @@ -109,6 +110,18 @@ DEFUN_NOSH (show_debugging_zebra, else if (IS_ZEBRA_DEBUG_NHG) vty_out(vty, " Zebra nexthop debugging is on\n"); + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + vty_out(vty, " Zebra EVPN-MH ethernet segment debugging is on\n"); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) + vty_out(vty, " Zebra EVPN-MH nexthop debugging is on\n"); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + vty_out(vty, " Zebra EVPN-MH MAC debugging is on\n"); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + vty_out(vty, " Zebra EVPN-MH Neigh debugging is on\n"); + hook_call(zebra_debug_show_debugging, vty); return CMD_SUCCESS; } @@ -320,6 +333,53 @@ DEFPY (debug_zebra_mlag, return CMD_SUCCESS; } +DEFPY (debug_zebra_evpn_mh, + debug_zebra_evpn_mh_cmd, + "[no$no] debug zebra evpn mh <es$es|mac$mac|neigh$neigh|nh$nh>", + NO_STR + DEBUG_STR + "Zebra configuration\n" + "EVPN\n" + "Multihoming\n" + "Ethernet Segment Debugging\n" + "MAC Debugging\n" + "Neigh Debugging\n" + "Nexthop Debugging\n") +{ + if (es) { + if (no) + UNSET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_ES); + else + SET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_ES); + } + + if (mac) { + if (no) + UNSET_FLAG(zebra_debug_evpn_mh, + ZEBRA_DEBUG_EVPN_MH_MAC); + else + SET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_MAC); + } + + if (neigh) { + if (no) + UNSET_FLAG(zebra_debug_evpn_mh, + ZEBRA_DEBUG_EVPN_MH_NEIGH); + else + SET_FLAG(zebra_debug_evpn_mh, + ZEBRA_DEBUG_EVPN_MH_NEIGH); + } + + if (nh) { + if (no) + UNSET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_NH); + else + SET_FLAG(zebra_debug_evpn_mh, ZEBRA_DEBUG_EVPN_MH_NH); + } + + return CMD_SUCCESS; +} + DEFUN (no_debug_zebra_events, no_debug_zebra_events_cmd, "no debug zebra events", @@ -553,6 +613,22 @@ static int config_write_debug(struct vty *vty) vty_out(vty, "debug zebra mlag\n"); write++; } + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) { + vty_out(vty, "debug zebra evpn mh es\n"); + write++; + } + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) { + vty_out(vty, "debug zebra evpn mh nh\n"); + write++; + } + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) { + vty_out(vty, "debug zebra evpn mh mac\n"); + write++; + } + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) { + vty_out(vty, "debug zebra evpn mh neigh\n"); + write++; + } if (IS_ZEBRA_DEBUG_PW) { vty_out(vty, "debug zebra pseudowires\n"); write++; @@ -589,6 +665,7 @@ void zebra_debug_init(void) zebra_debug_pw = 0; zebra_debug_dplane = 0; zebra_debug_mlag = 0; + zebra_debug_evpn_mh = 0; zebra_debug_nht = 0; zebra_debug_nexthop = 0; @@ -619,6 +696,7 @@ void zebra_debug_init(void) install_element(ENABLE_NODE, &no_debug_zebra_rib_cmd); install_element(ENABLE_NODE, &no_debug_zebra_fpm_cmd); install_element(ENABLE_NODE, &no_debug_zebra_dplane_cmd); + install_element(ENABLE_NODE, &debug_zebra_evpn_mh_cmd); install_element(CONFIG_NODE, &debug_zebra_events_cmd); install_element(CONFIG_NODE, &debug_zebra_nht_cmd); @@ -643,4 +721,5 @@ void zebra_debug_init(void) install_element(CONFIG_NODE, &no_debug_zebra_fpm_cmd); install_element(CONFIG_NODE, &no_debug_zebra_dplane_cmd); install_element(CONFIG_NODE, &debug_zebra_mlag_cmd); + install_element(CONFIG_NODE, &debug_zebra_evpn_mh_cmd); } diff --git a/zebra/debug.h b/zebra/debug.h index e513f8865d..8402224f19 100644 --- a/zebra/debug.h +++ b/zebra/debug.h @@ -62,6 +62,11 @@ extern "C" { #define ZEBRA_DEBUG_NHG 0x01 #define ZEBRA_DEBUG_NHG_DETAILED 0x02 +#define ZEBRA_DEBUG_EVPN_MH_ES 0x01 +#define ZEBRA_DEBUG_EVPN_MH_NH 0x02 +#define ZEBRA_DEBUG_EVPN_MH_MAC 0x04 +#define ZEBRA_DEBUG_EVPN_MH_NEIGH 0x08 + /* Debug related macro. */ #define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT) @@ -100,6 +105,15 @@ extern "C" { #define IS_ZEBRA_DEBUG_NHG_DETAIL \ (zebra_debug_nexthop & ZEBRA_DEBUG_NHG_DETAILED) +#define IS_ZEBRA_DEBUG_EVPN_MH_ES \ + (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_ES) +#define IS_ZEBRA_DEBUG_EVPN_MH_NH \ + (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_NH) +#define IS_ZEBRA_DEBUG_EVPN_MH_MAC \ + (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_MAC) +#define IS_ZEBRA_DEBUG_EVPN_MH_NEIGH \ + (zebra_debug_evpn_mh & ZEBRA_DEBUG_EVPN_MH_NEIGH) + extern unsigned long zebra_debug_event; extern unsigned long zebra_debug_packet; extern unsigned long zebra_debug_kernel; @@ -112,6 +126,7 @@ extern unsigned long zebra_debug_pw; extern unsigned long zebra_debug_dplane; extern unsigned long zebra_debug_mlag; extern unsigned long zebra_debug_nexthop; +extern unsigned long zebra_debug_evpn_mh; extern void zebra_debug_init(void); diff --git a/zebra/dplane_fpm_nl.c b/zebra/dplane_fpm_nl.c index ef208bdc83..5bf47580a8 100644 --- a/zebra/dplane_fpm_nl.c +++ b/zebra/dplane_fpm_nl.c @@ -44,6 +44,8 @@ #include "zebra/interface.h" #include "zebra/zebra_dplane.h" #include "zebra/zebra_router.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" #include "zebra/zebra_vxlan_private.h" #include "zebra/kernel_netlink.h" #include "zebra/rt_netlink.h" @@ -72,6 +74,7 @@ struct fpm_nl_ctx { int socket; bool disabled; bool connecting; + bool nhg_complete; bool rib_complete; bool rmac_complete; bool use_nhg; @@ -149,8 +152,25 @@ enum fpm_nl_events { FNE_RESET_COUNTERS, /* Toggle next hop group feature. */ FNE_TOGGLE_NHG, + /* Reconnect request by our own code to avoid races. */ + FNE_INTERNAL_RECONNECT, + + /* Next hop groups walk finished. */ + FNE_NHG_FINISHED, + /* RIB walk finished. */ + FNE_RIB_FINISHED, + /* RMAC walk finished. */ + FNE_RMAC_FINISHED, }; +#define FPM_RECONNECT(fnc) \ + thread_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \ + FNE_INTERNAL_RECONNECT, &(fnc)->t_event) + +#define WALK_FINISH(fnc, ev) \ + thread_add_event((fnc)->fthread->master, fpm_process_event, (fnc), \ + (ev), NULL) + /* * Prototypes. */ @@ -428,7 +448,18 @@ static int fpm_connect(struct thread *t); static void fpm_reconnect(struct fpm_nl_ctx *fnc) { - /* Grab the lock to empty the stream and stop the zebra thread. */ + /* Cancel all zebra threads first. */ + thread_cancel_async(zrouter.master, &fnc->t_nhgreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_nhgwalk, NULL); + thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL); + thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL); + thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL); + + /* + * Grab the lock to empty the streams (data plane might try to + * enqueue updates while we are closing). + */ frr_mutex_lock_autounlock(&fnc->obuf_mutex); /* Avoid calling close on `-1`. */ @@ -442,13 +473,6 @@ static void fpm_reconnect(struct fpm_nl_ctx *fnc) THREAD_OFF(fnc->t_read); THREAD_OFF(fnc->t_write); - thread_cancel_async(zrouter.master, &fnc->t_nhgreset, NULL); - thread_cancel_async(zrouter.master, &fnc->t_nhgwalk, NULL); - thread_cancel_async(zrouter.master, &fnc->t_ribreset, NULL); - thread_cancel_async(zrouter.master, &fnc->t_ribwalk, NULL); - thread_cancel_async(zrouter.master, &fnc->t_rmacreset, NULL); - thread_cancel_async(zrouter.master, &fnc->t_rmacwalk, NULL); - /* FPM is disabled, don't attempt to connect. */ if (fnc->disabled) return; @@ -465,6 +489,13 @@ static int fpm_read(struct thread *t) /* Let's ignore the input at the moment. */ rv = stream_read_try(fnc->ibuf, fnc->socket, STREAM_WRITEABLE(fnc->ibuf)); + /* We've got an interruption. */ + if (rv == -2) { + /* Schedule next read. */ + thread_add_read(fnc->fthread->master, fpm_read, fnc, + fnc->socket, &fnc->t_read); + return 0; + } if (rv == 0) { atomic_fetch_add_explicit(&fnc->counters.connection_closes, 1, memory_order_relaxed); @@ -472,19 +503,15 @@ static int fpm_read(struct thread *t) if (IS_ZEBRA_DEBUG_FPM) zlog_debug("%s: connection closed", __func__); - fpm_reconnect(fnc); + FPM_RECONNECT(fnc); return 0; } if (rv == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK - || errno == EINTR) - return 0; - atomic_fetch_add_explicit(&fnc->counters.connection_errors, 1, memory_order_relaxed); zlog_warn("%s: connection failure: %s", __func__, strerror(errno)); - fpm_reconnect(fnc); + FPM_RECONNECT(fnc); return 0; } stream_reset(fnc->ibuf); @@ -525,33 +552,15 @@ static int fpm_write(struct thread *t) &fnc->counters.connection_errors, 1, memory_order_relaxed); - fpm_reconnect(fnc); + FPM_RECONNECT(fnc); return 0; } fnc->connecting = false; - /* - * Walk the route tables to send old information before starting - * to send updated information. - * - * NOTE 1: - * RIB table walk is called after the next group table walk - * ends. - * - * NOTE 2: - * Don't attempt to go through next hop group table if we were - * explictly told to not use it. - */ - if (fnc->use_nhg) - thread_add_timer(zrouter.master, fpm_nhg_send, fnc, 0, - &fnc->t_nhgwalk); - else - thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0, - &fnc->t_ribwalk); - - thread_add_timer(zrouter.master, fpm_rmac_send, fnc, 0, - &fnc->t_rmacwalk); + /* Permit receiving messages now. */ + thread_add_read(fnc->fthread->master, fpm_read, fnc, + fnc->socket, &fnc->t_read); } frr_mutex_lock_autounlock(&fnc->obuf_mutex); @@ -589,8 +598,9 @@ static int fpm_write(struct thread *t) memory_order_relaxed); zlog_warn("%s: connection failure: %s", __func__, strerror(errno)); - fpm_reconnect(fnc); - break; + + FPM_RECONNECT(fnc); + return 0; } /* Account all bytes sent. */ @@ -661,18 +671,19 @@ static int fpm_connect(struct thread *t) fnc->connecting = (errno == EINPROGRESS); fnc->socket = sock; - thread_add_read(fnc->fthread->master, fpm_read, fnc, sock, - &fnc->t_read); + if (!fnc->connecting) + thread_add_read(fnc->fthread->master, fpm_read, fnc, sock, + &fnc->t_read); thread_add_write(fnc->fthread->master, fpm_write, fnc, sock, &fnc->t_write); /* Mark all routes as unsent. */ - thread_add_timer(zrouter.master, fpm_nhg_reset, fnc, 0, - &fnc->t_nhgreset); - thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0, - &fnc->t_ribreset); - thread_add_timer(zrouter.master, fpm_rmac_reset, fnc, 0, - &fnc->t_rmacreset); + if (fnc->use_nhg) + thread_add_timer(zrouter.master, fpm_nhg_reset, fnc, 0, + &fnc->t_nhgreset); + else + thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0, + &fnc->t_ribreset); return 0; } @@ -904,10 +915,11 @@ static int fpm_nhg_send(struct thread *t) dplane_ctx_fini(&fna.ctx); /* We are done sending next hops, lets install the routes now. */ - if (fna.complete) - thread_add_timer(zrouter.master, fpm_rib_send, fnc, 0, - &fnc->t_ribwalk); - else /* Otherwise reschedule next hop group again. */ + if (fna.complete) { + WALK_FINISH(fnc, FNE_NHG_FINISHED); + thread_add_timer(zrouter.master, fpm_rib_reset, fnc, 0, + &fnc->t_ribreset); + } else /* Otherwise reschedule next hop group again. */ thread_add_timer(zrouter.master, fpm_nhg_send, fnc, 0, &fnc->t_nhgwalk); @@ -963,7 +975,11 @@ static int fpm_rib_send(struct thread *t) dplane_ctx_fini(&ctx); /* All RIB routes sent! */ - fnc->rib_complete = true; + WALK_FINISH(fnc, FNE_RIB_FINISHED); + + /* Schedule next event: RMAC reset. */ + thread_add_event(zrouter.master, fpm_rmac_reset, fnc, 0, + &fnc->t_rmacreset); return 0; } @@ -975,6 +991,7 @@ struct fpm_rmac_arg { struct zebra_dplane_ctx *ctx; struct fpm_nl_ctx *fnc; zebra_l3vni_t *zl3vni; + bool complete; }; static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg) @@ -988,7 +1005,7 @@ static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg) bool sticky; /* Entry already sent. */ - if (CHECK_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT)) + if (CHECK_FLAG(zrmac->flags, ZEBRA_MAC_FPM_SENT) || !fra->complete) return; sticky = !!CHECK_FLAG(zrmac->flags, @@ -1000,10 +1017,12 @@ static void fpm_enqueue_rmac_table(struct hash_bucket *backet, void *arg) dplane_ctx_set_op(fra->ctx, DPLANE_OP_MAC_INSTALL); dplane_mac_init(fra->ctx, fra->zl3vni->vxlan_if, zif->brslave_info.br_if, vid, - &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, sticky); + &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, sticky, + 0 /*nhg*/, 0 /*update_flags*/); if (fpm_nl_enqueue(fra->fnc, fra->ctx) == -1) { thread_add_timer(zrouter.master, fpm_rmac_send, fra->fnc, 1, &fra->fnc->t_rmacwalk); + fra->complete = false; } } @@ -1022,9 +1041,14 @@ static int fpm_rmac_send(struct thread *t) fra.fnc = THREAD_ARG(t); fra.ctx = dplane_ctx_alloc(); + fra.complete = true; hash_iterate(zrouter.l3vni_table, fpm_enqueue_l3vni_table, &fra); dplane_ctx_fini(&fra.ctx); + /* RMAC walk completed. */ + if (fra.complete) + WALK_FINISH(fra.fnc, FNE_RMAC_FINISHED); + return 0; } @@ -1041,7 +1065,14 @@ static void fpm_nhg_reset_cb(struct hash_bucket *bucket, void *arg) static int fpm_nhg_reset(struct thread *t) { + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + + fnc->nhg_complete = false; hash_iterate(zrouter.nhgs_id, fpm_nhg_reset_cb, NULL); + + /* Schedule next step: send next hop groups. */ + thread_add_event(zrouter.master, fpm_nhg_send, fnc, 0, &fnc->t_nhgwalk); + return 0; } @@ -1070,6 +1101,9 @@ static int fpm_rib_reset(struct thread *t) } } + /* Schedule next step: send RIB routes. */ + thread_add_event(zrouter.master, fpm_rib_send, fnc, 0, &fnc->t_ribwalk); + return 0; } @@ -1092,8 +1126,15 @@ static void fpm_unset_l3vni_table(struct hash_bucket *backet, void *arg) static int fpm_rmac_reset(struct thread *t) { + struct fpm_nl_ctx *fnc = THREAD_ARG(t); + + fnc->rmac_complete = false; hash_iterate(zrouter.l3vni_table, fpm_unset_l3vni_table, NULL); + /* Schedule next event: send RMAC entries. */ + thread_add_event(zrouter.master, fpm_rmac_send, fnc, 0, + &fnc->t_rmacwalk); + return 0; } @@ -1174,6 +1215,30 @@ static int fpm_process_event(struct thread *t) fpm_reconnect(fnc); break; + case FNE_INTERNAL_RECONNECT: + fpm_reconnect(fnc); + break; + + case FNE_NHG_FINISHED: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: next hop groups walk finished", + __func__); + + fnc->nhg_complete = true; + break; + case FNE_RIB_FINISHED: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: RIB walk finished", __func__); + + fnc->rib_complete = true; + break; + case FNE_RMAC_FINISHED: + if (IS_ZEBRA_DEBUG_FPM) + zlog_debug("%s: RMAC walk finished", __func__); + + fnc->rmac_complete = true; + break; + default: if (IS_ZEBRA_DEBUG_FPM) zlog_debug("%s: unhandled event %d", __func__, event); diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index a29a810902..5cd3e69299 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -70,6 +70,7 @@ #include "zebra/if_netlink.h" #include "zebra/zebra_errors.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" extern struct zebra_privs_t zserv_privs; @@ -245,6 +246,26 @@ static enum zebra_link_type netlink_to_zebra_link_type(unsigned int hwt) } } +static inline void zebra_if_set_ziftype(struct interface *ifp, + zebra_iftype_t zif_type, + zebra_slave_iftype_t zif_slave_type) +{ + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + zif->zif_slave_type = zif_slave_type; + + if (zif->zif_type != zif_type) { + zif->zif_type = zif_type; + /* If the if_type has been set to bond initialize ES info + * against it. XXX - note that we don't handle the case where + * a zif changes from bond to non-bond; it is really + * an unexpected/error condition. + */ + zebra_evpn_if_init(zif); + } +} + static void netlink_determine_zebra_iftype(const char *kind, zebra_iftype_t *zif_type) { @@ -513,8 +534,7 @@ static int netlink_extract_vxlan_info(struct rtattr *link_data, if (!attr[IFLA_VXLAN_LINK]) { if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("IFLA_VXLAN_LINK missing " - "from VXLAN IF message"); + zlog_debug("IFLA_VXLAN_LINK missing from VXLAN IF message"); } else { ifindex_link = *(ifindex_t *)RTA_DATA(attr[IFLA_VXLAN_LINK]); @@ -558,6 +578,74 @@ static void netlink_interface_update_l2info(struct interface *ifp, } } +static int netlink_bridge_vxlan_update(struct interface *ifp, + struct rtattr *af_spec) +{ + struct rtattr *aftb[IFLA_BRIDGE_MAX + 1]; + struct bridge_vlan_info *vinfo; + vlanid_t access_vlan; + + /* There is a 1-to-1 mapping of VLAN to VxLAN - hence + * only 1 access VLAN is accepted. + */ + memset(aftb, 0, sizeof(aftb)); + parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, af_spec); + if (!aftb[IFLA_BRIDGE_VLAN_INFO]) + return 0; + + vinfo = RTA_DATA(aftb[IFLA_BRIDGE_VLAN_INFO]); + if (!(vinfo->flags & BRIDGE_VLAN_INFO_PVID)) + return 0; + + access_vlan = (vlanid_t)vinfo->vid; + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("Access VLAN %u for VxLAN IF %s(%u)", access_vlan, + ifp->name, ifp->ifindex); + zebra_l2_vxlanif_update_access_vlan(ifp, access_vlan); + return 0; +} + +static void netlink_bridge_vlan_update(struct interface *ifp, + struct rtattr *af_spec) +{ + struct rtattr *i; + int rem; + uint16_t vid_range_start = 0; + struct zebra_if *zif; + bitfield_t old_vlan_bitmap; + struct bridge_vlan_info *vinfo; + + zif = (struct zebra_if *)ifp->info; + + /* cache the old bitmap addrs */ + old_vlan_bitmap = zif->vlan_bitmap; + /* create a new bitmap space for re-eval */ + bf_init(zif->vlan_bitmap, IF_VLAN_BITMAP_MAX); + + for (i = RTA_DATA(af_spec), rem = RTA_PAYLOAD(af_spec); + RTA_OK(i, rem); i = RTA_NEXT(i, rem)) { + + if (i->rta_type != IFLA_BRIDGE_VLAN_INFO) + continue; + + vinfo = RTA_DATA(i); + + if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { + vid_range_start = vinfo->vid; + continue; + } + + if (!(vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END)) + vid_range_start = vinfo->vid; + + zebra_vlan_bitmap_compute(ifp, vid_range_start, vinfo->vid); + } + + zebra_vlan_mbr_re_eval(ifp, old_vlan_bitmap); + + bf_free(old_vlan_bitmap); +} + static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, int startup) { @@ -565,12 +653,8 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; struct interface *ifp; - struct rtattr *aftb[IFLA_BRIDGE_MAX + 1]; - struct { - uint16_t flags; - uint16_t vid; - } * vinfo; - vlanid_t access_vlan; + struct zebra_if *zif; + struct rtattr *af_spec; /* Fetch name and ifindex */ ifi = NLMSG_DATA(h); @@ -588,30 +672,22 @@ static int netlink_bridge_interface(struct nlmsghdr *h, int len, ns_id_t ns_id, ifi->ifi_index); return 0; } - if (!IS_ZEBRA_IF_VXLAN(ifp)) - return 0; /* We are only interested in the access VLAN i.e., AF_SPEC */ - if (!tb[IFLA_AF_SPEC]) - return 0; + af_spec = tb[IFLA_AF_SPEC]; + if (!af_spec) + return 0; - /* There is a 1-to-1 mapping of VLAN to VxLAN - hence - * only 1 access VLAN is accepted. - */ - memset(aftb, 0, sizeof(aftb)); - parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, tb[IFLA_AF_SPEC]); - if (!aftb[IFLA_BRIDGE_VLAN_INFO]) - return 0; + if (IS_ZEBRA_IF_VXLAN(ifp)) + return netlink_bridge_vxlan_update(ifp, af_spec); - vinfo = RTA_DATA(aftb[IFLA_BRIDGE_VLAN_INFO]); - if (!(vinfo->flags & BRIDGE_VLAN_INFO_PVID)) - return 0; + /* build vlan bitmap associated with this interface if that + * device type is interested in the vlans + */ + zif = (struct zebra_if *)ifp->info; + if (bf_is_inited(zif->vlan_bitmap)) + netlink_bridge_vlan_update(ifp, af_spec); - access_vlan = (vlanid_t)vinfo->vid; - if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Access VLAN %u for VxLAN IF %s(%u)", access_vlan, - name, ifi->ifi_index); - zebra_l2_vxlanif_update_access_vlan(ifp, access_vlan); return 0; } @@ -722,10 +798,8 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); - if (tb[IFLA_LINK_NETNSID]) { + if (tb[IFLA_LINK_NETNSID]) link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); - link_nsid = ns_id_get_absolute(ns_id, link_nsid); - } /* Add interface. * We add by index first because in some cases such as the master @@ -733,9 +807,11 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) * back references on the slave interfaces is painful if not done * this way, i.e. by creating by ifindex. */ - ifp = if_get_by_ifindex(ifi->ifi_index, vrf_id, name); + ifp = if_get_by_ifindex(ifi->ifi_index, vrf_id); set_ifindex(ifp, ifi->ifi_index, zns); /* add it to ns struct */ + if_set_name(ifp, name); + ifp->flags = ifi->ifi_flags & 0x0000fffff; ifp->mtu6 = ifp->mtu = *(uint32_t *)RTA_DATA(tb[IFLA_MTU]); ifp->metric = 0; @@ -772,7 +848,7 @@ static int netlink_interface(struct nlmsghdr *h, ns_id_t ns_id, int startup) netlink_interface_update_l2info(ifp, linkinfo[IFLA_INFO_DATA], 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) - zebra_l2if_update_bridge_slave(ifp, bridge_ifindex, ns_id); + zebra_l2if_update_bridge_slave(ifp, bridge_ifindex); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); @@ -911,7 +987,8 @@ int kernel_interface_set_master(struct interface *master, } /* Interface address modification. */ -static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx) +static ssize_t netlink_address_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { int bytelen; const struct prefix *p; @@ -921,64 +998,72 @@ static int netlink_address_ctx(const struct zebra_dplane_ctx *ctx) struct { struct nlmsghdr n; struct ifaddrmsg ifa; - char buf[NL_PKT_BUF_SIZE]; - } req; + char buf[0]; + } *req = buf; + + if (buflen < sizeof(*req)) + return 0; p = dplane_ctx_get_intf_addr(ctx); - memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); + memset(req, 0, sizeof(*req)); bytelen = (p->family == AF_INET ? 4 : 16); - req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); - req.n.nlmsg_flags = NLM_F_REQUEST; + req->n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg)); + req->n.nlmsg_flags = NLM_F_REQUEST; if (dplane_ctx_get_op(ctx) == DPLANE_OP_ADDR_INSTALL) cmd = RTM_NEWADDR; else cmd = RTM_DELADDR; - req.n.nlmsg_type = cmd; - req.ifa.ifa_family = p->family; + req->n.nlmsg_type = cmd; + req->ifa.ifa_family = p->family; - req.ifa.ifa_index = dplane_ctx_get_ifindex(ctx); + req->ifa.ifa_index = dplane_ctx_get_ifindex(ctx); - nl_attr_put(&req.n, sizeof(req), IFA_LOCAL, &p->u.prefix, bytelen); + if (!nl_attr_put(&req->n, buflen, IFA_LOCAL, &p->u.prefix, bytelen)) + return 0; if (p->family == AF_INET) { if (dplane_ctx_intf_is_connected(ctx)) { p = dplane_ctx_get_intf_dest(ctx); - nl_attr_put(&req.n, sizeof(req), IFA_ADDRESS, - &p->u.prefix, bytelen); + if (!nl_attr_put(&req->n, buflen, IFA_ADDRESS, + &p->u.prefix, bytelen)) + return 0; } else if (cmd == RTM_NEWADDR) { struct in_addr broad = { .s_addr = ipv4_broadcast_addr(p->u.prefix4.s_addr, p->prefixlen) }; - nl_attr_put(&req.n, sizeof(req), IFA_BROADCAST, &broad, - bytelen); + if (!nl_attr_put(&req->n, buflen, IFA_BROADCAST, &broad, + bytelen)) + return 0; } } /* p is now either address or destination/bcast addr */ - req.ifa.ifa_prefixlen = p->prefixlen; + req->ifa.ifa_prefixlen = p->prefixlen; if (dplane_ctx_intf_is_secondary(ctx)) - SET_FLAG(req.ifa.ifa_flags, IFA_F_SECONDARY); + SET_FLAG(req->ifa.ifa_flags, IFA_F_SECONDARY); if (dplane_ctx_intf_has_label(ctx)) { label = dplane_ctx_get_intf_label(ctx); - nl_attr_put(&req.n, sizeof(req), IFA_LABEL, label, - strlen(label) + 1); + if (!nl_attr_put(&req->n, buflen, IFA_LABEL, label, + strlen(label) + 1)) + return 0; } - return netlink_talk_info(netlink_talk_filter, &req.n, - dplane_ctx_get_ns(ctx), 0); + return NLMSG_ALIGN(req->n.nlmsg_len); } -enum zebra_dplane_result kernel_address_update_ctx(struct zebra_dplane_ctx *ctx) +enum netlink_msg_status +netlink_put_address_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) { - return (netlink_address_ctx(ctx) == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); + return netlink_batch_add_msg(bth, ctx, netlink_address_msg_encoder, + false); } int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, int startup) @@ -1264,10 +1349,9 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) if (tb[IFLA_LINK]) link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]); - if (tb[IFLA_LINK_NETNSID]) { + if (tb[IFLA_LINK_NETNSID]) link_nsid = *(ns_id_t *)RTA_DATA(tb[IFLA_LINK_NETNSID]); - link_nsid = ns_id_get_absolute(ns_id, link_nsid); - } + if (tb[IFLA_IFALIAS]) { desc = (char *)RTA_DATA(tb[IFLA_IFALIAS]); } @@ -1307,8 +1391,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Add interface notification from kernel */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d " - "sl_type %d master %u flags 0x%x", + "RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d sl_type %d master %u flags 0x%x", name, ifi->ifi_index, vrf_id, zif_type, zif_slave_type, bridge_ifindex, ifi->ifi_flags); @@ -1356,16 +1439,14 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) 1, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp)) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex, - ns_id); + bridge_ifindex); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp)) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } else if (ifp->vrf_id != vrf_id) { /* VRF change for an interface. */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "RTM_NEWLINK vrf-change for %s(%u) " - "vrf_id %u -> %u flags 0x%x", + "RTM_NEWLINK vrf-change for %s(%u) vrf_id %u -> %u flags 0x%x", name, ifp->ifindex, ifp->vrf_id, vrf_id, ifi->ifi_flags); @@ -1376,8 +1457,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* Interface update. */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( - "RTM_NEWLINK update for %s(%u) " - "sl_type %d master %u flags 0x%x", + "RTM_NEWLINK update for %s(%u) sl_type %d master %u flags 0x%x", name, ifp->ifindex, zif_slave_type, bridge_ifindex, ifi->ifi_flags); @@ -1460,8 +1540,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) 0, link_nsid); if (IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) || was_bridge_slave) zebra_l2if_update_bridge_slave(ifp, - bridge_ifindex, - ns_id); + bridge_ifindex); else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) zebra_l2if_update_bond_slave(ifp, bond_ifindex); } diff --git a/zebra/if_netlink.h b/zebra/if_netlink.h index 29fd2aca35..0bbba81ca6 100644 --- a/zebra/if_netlink.h +++ b/zebra/if_netlink.h @@ -32,6 +32,10 @@ extern int netlink_interface_addr(struct nlmsghdr *h, ns_id_t ns_id, extern int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int interface_lookup_netlink(struct zebra_ns *zns); +extern enum netlink_msg_status +netlink_put_address_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); + /* * Set protodown status of interface. * diff --git a/zebra/interface.c b/zebra/interface.c index 982a63a022..b824e313ec 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -51,6 +51,7 @@ #include "zebra/interface.h" #include "zebra/zebra_vxlan.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_evpn_mh.h" DEFINE_MTYPE_STATIC(ZEBRA, ZINFO, "Zebra Interface Information") @@ -127,6 +128,7 @@ static int if_zebra_new_hook(struct interface *ifp) struct zebra_if *zebra_if; zebra_if = XCALLOC(MTYPE_ZINFO, sizeof(struct zebra_if)); + zebra_if->ifp = ifp; zebra_if->multicast = IF_ZEBRA_MULTICAST_UNSPEC; zebra_if->shutdown = IF_ZEBRA_SHUTDOWN_OFF; @@ -238,6 +240,8 @@ static int if_zebra_delete_hook(struct interface *ifp) list_delete(&rtadv->AdvDNSSLList); #endif /* HAVE_RTADV */ + zebra_evpn_if_cleanup(zebra_if); + if_nhg_dependents_release(ifp); zebra_if_nhg_dependents_free(zebra_if); @@ -385,8 +389,7 @@ int if_subnet_delete(struct interface *ifp, struct connected *ifc) rn = route_node_lookup(zebra_if->ipv4_subnets, &cp); if (!(rn && rn->info)) { flog_warn(EC_ZEBRA_REMOVE_ADDR_UNKNOWN_SUBNET, - "Trying to remove an address from an unknown subnet." - " (please report this bug)"); + "Trying to remove an address from an unknown subnet. (please report this bug)"); return -1; } route_unlock_node(rn); @@ -400,8 +403,7 @@ int if_subnet_delete(struct interface *ifp, struct connected *ifc) if (!listnode_lookup(addr_list, ifc)) { flog_warn( EC_ZEBRA_REMOVE_UNREGISTERED_ADDR, - "Trying to remove an address from a subnet where it is not" - " currently registered. (please report this bug)"); + "Trying to remove an address from a subnet where it is not currently registered. (please report this bug)"); return -1; } @@ -618,8 +620,7 @@ void if_add_update(struct interface *ifp) if (if_data->shutdown == IF_ZEBRA_SHUTDOWN_ON) { if (IS_ZEBRA_DEBUG_KERNEL) { zlog_debug( - "interface %s vrf %s(%u) index %d is shutdown. " - "Won't wake it up.", + "interface %s vrf %s(%u) index %d is shutdown. Won't wake it up.", ifp->name, VRF_LOGNAME(zvrf->vrf), ifp->vrf_id, ifp->ifindex); } @@ -834,6 +835,7 @@ void if_delete_update(struct interface *ifp) memset(&zif->l2info, 0, sizeof(union zebra_l2if_info)); memset(&zif->brslave_info, 0, sizeof(struct zebra_l2info_brslave)); + zebra_evpn_if_cleanup(zif); } if (!ifp->configured) { @@ -1075,6 +1077,8 @@ void if_up(struct interface *ifp) } else if (IS_ZEBRA_IF_MACVLAN(ifp)) zebra_vxlan_macvlan_up(ifp); + if (zif->es_info.es) + zebra_evpn_es_if_oper_state_change(zif, true /*up*/); } /* Interface goes down. We have to manage different behavior of based @@ -1109,6 +1113,8 @@ void if_down(struct interface *ifp) } else if (IS_ZEBRA_IF_MACVLAN(ifp)) zebra_vxlan_macvlan_down(ifp); + if (zif->es_info.es) + zebra_evpn_es_if_oper_state_change(zif, false /*up*/); /* Notify to the protocol daemons. */ zebra_interface_down_update(ifp); @@ -1236,23 +1242,6 @@ static void nbr_connected_dump_vty(struct vty *vty, vty_out(vty, "\n"); } -static const char *zebra_zifslavetype_2str(zebra_slave_iftype_t zif_slave_type) -{ - switch (zif_slave_type) { - case ZEBRA_IF_SLAVE_BRIDGE: - return "Bridge"; - case ZEBRA_IF_SLAVE_VRF: - return "Vrf"; - case ZEBRA_IF_SLAVE_BOND: - return "Bond"; - case ZEBRA_IF_SLAVE_OTHER: - return "Other"; - case ZEBRA_IF_SLAVE_NONE: - return "None"; - } - return "None"; -} - static const char *zebra_ziftype_2str(zebra_iftype_t zif_type) { switch (zif_type) { @@ -1480,9 +1469,6 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " Interface Type %s\n", zebra_ziftype_2str(zebra_if->zif_type)); - vty_out(vty, " Interface Slave Type %s\n", - zebra_zifslavetype_2str(zebra_if->zif_slave_type)); - if (IS_ZEBRA_IF_BRIDGE(ifp)) { struct zebra_l2info_bridge *bridge_info; @@ -1550,6 +1536,8 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) } } + zebra_evpn_if_es_print(vty, zebra_if); + if (zebra_if->link_ifindex != IFINDEX_INTERNAL) { if (zebra_if->link) vty_out(vty, " Parent interface: %s\n", zebra_if->link->name); @@ -1625,14 +1613,12 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) #ifdef HAVE_PROC_NET_DEV /* Statistics print out using proc file system. */ vty_out(vty, - " %lu input packets (%lu multicast), %lu bytes, " - "%lu dropped\n", + " %lu input packets (%lu multicast), %lu bytes, %lu dropped\n", ifp->stats.rx_packets, ifp->stats.rx_multicast, ifp->stats.rx_bytes, ifp->stats.rx_dropped); vty_out(vty, - " %lu input errors, %lu length, %lu overrun," - " %lu CRC, %lu frame\n", + " %lu input errors, %lu length, %lu overrun, %lu CRC, %lu frame\n", ifp->stats.rx_errors, ifp->stats.rx_length_errors, ifp->stats.rx_over_errors, ifp->stats.rx_crc_errors, ifp->stats.rx_frame_errors); @@ -1645,8 +1631,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) ifp->stats.tx_dropped); vty_out(vty, - " %lu output errors, %lu aborted, %lu carrier," - " %lu fifo, %lu heartbeat\n", + " %lu output errors, %lu aborted, %lu carrier, %lu fifo, %lu heartbeat\n", ifp->stats.tx_errors, ifp->stats.tx_aborted_errors, ifp->stats.tx_carrier_errors, ifp->stats.tx_fifo_errors, ifp->stats.tx_heartbeat_errors); @@ -1658,8 +1643,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) #ifdef HAVE_NET_RT_IFLIST /* Statistics print out using sysctl (). */ vty_out(vty, - " input packets %llu, bytes %llu, dropped %llu," - " multicast packets %llu\n", + " input packets %llu, bytes %llu, dropped %llu, multicast packets %llu\n", (unsigned long long)ifp->stats.ifi_ipackets, (unsigned long long)ifp->stats.ifi_ibytes, (unsigned long long)ifp->stats.ifi_iqdrops, @@ -1669,8 +1653,7 @@ static void if_dump_vty(struct vty *vty, struct interface *ifp) (unsigned long long)ifp->stats.ifi_ierrors); vty_out(vty, - " output packets %llu, bytes %llu," - " multicast packets %llu\n", + " output packets %llu, bytes %llu, multicast packets %llu\n", (unsigned long long)ifp->stats.ifi_opackets, (unsigned long long)ifp->stats.ifi_obytes, (unsigned long long)ifp->stats.ifi_omcasts); @@ -3596,7 +3579,7 @@ static int if_config_write(struct vty *vty) } hook_call(zebra_if_config_wr, vty, ifp); - + zebra_evpn_mh_if_write(vty, ifp); link_params_config_write(vty, ifp); vty_endframe(vty, "!\n"); @@ -3672,4 +3655,7 @@ void zebra_if_init(void) install_element(LINK_PARAMS_NODE, &link_params_use_bw_cmd); install_element(LINK_PARAMS_NODE, &no_link_params_use_bw_cmd); install_element(LINK_PARAMS_NODE, &exit_link_params_cmd); + + /* setup EVPN MH elements */ + zebra_evpn_interface_init(); } diff --git a/zebra/interface.h b/zebra/interface.h index 2dad0c3bb2..1a8e3caed5 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -25,6 +25,7 @@ #include "redistribute.h" #include "vrf.h" #include "hook.h" +#include "bitfield.h" #include "zebra/zebra_l2.h" #include "zebra/zebra_nhg_private.h" @@ -42,6 +43,8 @@ extern "C" { #define IF_ZEBRA_SHUTDOWN_OFF 0 #define IF_ZEBRA_SHUTDOWN_ON 1 +#define IF_VLAN_BITMAP_MAX 4096 + #if defined(HAVE_RTADV) /* Router advertisement parameter. From RFC4861, RFC6275 and RFC4191. */ struct rtadvconf { @@ -272,8 +275,19 @@ typedef enum { struct irdp_interface; +/* Ethernet segment info used for setting up EVPN multihoming */ +struct zebra_evpn_es; +struct zebra_es_if_info { + struct ethaddr sysmac; + uint32_t lid; /* local-id; has to be unique per-ES-sysmac */ + struct zebra_evpn_es *es; /* local ES */ +}; + /* `zebra' daemon local interface structure. */ struct zebra_if { + /* back pointer to the interface */ + struct interface *ifp; + /* Shutdown configuration. */ uint8_t shutdown; @@ -347,6 +361,12 @@ struct zebra_if { struct zebra_l2info_bondslave bondslave_info; + /* ethernet segment */ + struct zebra_es_if_info es_info; + + /* bitmap of vlans associated with this interface */ + bitfield_t vlan_bitmap; + /* Link fields - for sub-interfaces. */ ifindex_t link_ifindex; struct interface *link; @@ -370,17 +390,6 @@ DECLARE_HOOK(zebra_if_extra_info, (struct vty * vty, struct interface *ifp), DECLARE_HOOK(zebra_if_config_wr, (struct vty * vty, struct interface *ifp), (vty, ifp)) -static inline void zebra_if_set_ziftype(struct interface *ifp, - zebra_iftype_t zif_type, - zebra_slave_iftype_t zif_slave_type) -{ - struct zebra_if *zif; - - zif = (struct zebra_if *)ifp->info; - zif->zif_type = zif_type; - zif->zif_slave_type = zif_slave_type; -} - #define IS_ZEBRA_IF_VRF(ifp) \ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VRF) diff --git a/zebra/ipforward_solaris.c b/zebra/ipforward_solaris.c index 1a45328248..3d6a29bec6 100644 --- a/zebra/ipforward_solaris.c +++ b/zebra/ipforward_solaris.c @@ -72,8 +72,7 @@ static int solaris_nd(const int cmd, const char *parameter, const int value) snprintf(nd_buf, ND_BUFFER_SIZE, "%s", parameter); else { flog_err_sys(EC_LIB_SYSTEM_CALL, - "internal error - inappropriate command given to " - "solaris_nd()%s:%d", + "internal error - inappropriate command given to solaris_nd()%s:%d", __FILE__, __LINE__); return -1; } diff --git a/zebra/irdp_interface.c b/zebra/irdp_interface.c index 87a1f5fdc7..2ab5fd3a4c 100644 --- a/zebra/irdp_interface.c +++ b/zebra/irdp_interface.c @@ -224,8 +224,7 @@ static void irdp_if_start(struct interface *ifp, int multicast, } if ((irdp_sock < 0) && ((irdp_sock = irdp_sock_init()) < 0)) { flog_warn(EC_ZEBRA_IRDP_CANNOT_ACTIVATE_IFACE, - "IRDP: Cannot activate interface %s (cannot create " - "IRDP socket)", + "IRDP: Cannot activate interface %s (cannot create IRDP socket)", ifp->name); return; } @@ -503,8 +502,7 @@ DEFUN (ip_irdp_minadvertinterval, return CMD_SUCCESS; } else { vty_out(vty, - "%% MinAdvertInterval must be less than or equal to " - "MaxAdvertInterval\n"); + "%% MinAdvertInterval must be less than or equal to MaxAdvertInterval\n"); return CMD_WARNING_CONFIG_FAILED; } } @@ -528,8 +526,7 @@ DEFUN (ip_irdp_maxadvertinterval, return CMD_SUCCESS; } else { vty_out(vty, - "%% MaxAdvertInterval must be greater than or equal to " - "MinAdvertInterval\n"); + "%% MaxAdvertInterval must be greater than or equal to MinAdvertInterval\n"); return CMD_WARNING_CONFIG_FAILED; } } diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index a4d22c12a4..d0c1bc812d 100644 --- a/zebra/kernel_netlink.c +++ b/zebra/kernel_netlink.c @@ -84,6 +84,27 @@ #define RTPROT_MROUTED 17 #endif +#define NL_DEFAULT_BATCH_BUFSIZE (16 * NL_PKT_BUF_SIZE) + +/* + * We limit the batch's size to a number smaller than the length of the + * underlying buffer since the last message that wouldn't fit the batch would go + * over the upper boundary and then it would have to be encoded again into a new + * buffer. If the difference between the limit and the length of the buffer is + * big enough (bigger than the biggest Netlink message) then this situation + * won't occur. + */ +#define NL_DEFAULT_BATCH_SEND_THRESHOLD (15 * NL_PKT_BUF_SIZE) + +/* + * For every request sent to the kernel that has failed we get an error message, + * which contains a standard netlink message header and the payload consisting + * of an error code and the original netlink mesage. So the receiving buffer + * must be at least as big as the transmitting buffer increased by some space + * for headers. + */ +#define NL_BATCH_RX_BUFSIZE (NL_DEFAULT_BATCH_BUFSIZE + NL_PKT_BUF_SIZE) + static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_DELROUTE, "RTM_DELROUTE"}, {RTM_GETROUTE, "RTM_GETROUTE"}, @@ -151,6 +172,62 @@ extern uint32_t nl_rcvbufsize; extern struct zebra_privs_t zserv_privs; +DEFINE_MTYPE_STATIC(ZEBRA, NL_BUF, "Zebra Netlink buffers") + +size_t nl_batch_tx_bufsize; +char *nl_batch_tx_buf; + +char nl_batch_rx_buf[NL_BATCH_RX_BUFSIZE]; + +_Atomic uint32_t nl_batch_bufsize = NL_DEFAULT_BATCH_BUFSIZE; +_Atomic uint32_t nl_batch_send_threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD; + +struct nl_batch { + void *buf; + size_t bufsiz; + size_t limit; + + void *buf_head; + size_t curlen; + size_t msgcnt; + + const struct zebra_dplane_info *zns; + + struct dplane_ctx_q ctx_list; + + /* + * Pointer to the queue of completed contexts outbound back + * towards the dataplane module. + */ + struct dplane_ctx_q *ctx_out_q; +}; + +int netlink_config_write_helper(struct vty *vty) +{ + uint32_t size = + atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed); + uint32_t threshold = atomic_load_explicit(&nl_batch_send_threshold, + memory_order_relaxed); + + if (size != NL_DEFAULT_BATCH_BUFSIZE + || threshold != NL_DEFAULT_BATCH_SEND_THRESHOLD) + vty_out(vty, "zebra kernel netlink batch-tx-buf %u %u\n", size, + threshold); + + return 0; +} + +void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, bool set) +{ + if (!set) { + size = NL_DEFAULT_BATCH_BUFSIZE; + threshold = NL_DEFAULT_BATCH_SEND_THRESHOLD; + } + + atomic_store_explicit(&nl_batch_bufsize, size, memory_order_relaxed); + atomic_store_explicit(&nl_batch_send_threshold, threshold, + memory_order_relaxed); +} int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns_id, int startup) { @@ -1008,9 +1085,10 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int), * startup -> Are we reading in under startup conditions * This is passed through eventually to filter. */ -int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), - struct nlmsghdr *n, - const struct zebra_dplane_info *dp_info, int startup) +static int +netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), + struct nlmsghdr *n, const struct zebra_dplane_info *dp_info, + int startup) { const struct nlsock *nl; @@ -1080,6 +1158,328 @@ int netlink_request(struct nlsock *nl, void *req) return 0; } +static int nl_batch_read_resp(struct nl_batch *bth) +{ + struct nlmsghdr *h; + struct sockaddr_nl snl; + struct msghdr msg; + int status, seq; + const struct nlsock *nl; + struct zebra_dplane_ctx *ctx; + bool ignore_msg; + + nl = &(bth->zns->nls); + + msg.msg_name = (void *)&snl; + msg.msg_namelen = sizeof(snl); + + status = netlink_recv_msg(nl, msg, nl_batch_rx_buf, + sizeof(nl_batch_rx_buf)); + if (status == -1 || status == 0) + return status; + + for (h = (struct nlmsghdr *)nl_batch_rx_buf; + (status >= 0 && NLMSG_OK(h, (unsigned int)status)); + h = NLMSG_NEXT(h, status)) { + ignore_msg = false; + seq = h->nlmsg_seq; + /* + * Find the corresponding context object. Received responses are + * in the same order as requests we sent, so we can simply + * iterate over the context list and match responses with + * requests at same time. + */ + while (true) { + ctx = dplane_ctx_dequeue(&(bth->ctx_list)); + if (ctx == NULL) + break; + + dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx); + + /* We have found corresponding context object. */ + if (dplane_ctx_get_ns(ctx)->nls.seq == seq) + break; + + /* + * 'update' context objects take two consecutive + * sequence numbers. + */ + if (dplane_ctx_is_update(ctx) + && dplane_ctx_get_ns(ctx)->nls.seq + 1 == seq) { + /* + * This is the situation where we get a response + * to a message that should be ignored. + */ + ignore_msg = true; + break; + } + } + + if (ignore_msg) + continue; + + /* + * We received a message with the sequence number that isn't + * associated with any dplane context object. + */ + if (ctx == NULL) { + zlog_debug( + "%s: skipping unassociated response, seq number %d NS %u", + __func__, h->nlmsg_seq, bth->zns->ns_id); + continue; + } + + if (h->nlmsg_type == NLMSG_ERROR) { + int err = netlink_parse_error(nl, h, bth->zns, 0); + + if (err == -1) + dplane_ctx_set_status( + ctx, ZEBRA_DPLANE_REQUEST_FAILURE); + + zlog_debug("%s: netlink error message seq=%d ", + __func__, h->nlmsg_seq); + continue; + } + + /* + * If we get here then we did not receive neither the ack nor + * the error and instead received some other message in an + * unexpected way. + */ + zlog_debug("%s: ignoring message type 0x%04x(%s) NS %u", + __func__, h->nlmsg_type, + nl_msg_type_to_str(h->nlmsg_type), bth->zns->ns_id); + } + + return 0; +} + +static void nl_batch_reset(struct nl_batch *bth) +{ + bth->buf_head = bth->buf; + bth->curlen = 0; + bth->msgcnt = 0; + bth->zns = NULL; + + TAILQ_INIT(&(bth->ctx_list)); +} + +static void nl_batch_init(struct nl_batch *bth, struct dplane_ctx_q *ctx_out_q) +{ + /* + * If the size of the buffer has changed, free and then allocate a new + * one. + */ + size_t bufsize = + atomic_load_explicit(&nl_batch_bufsize, memory_order_relaxed); + if (bufsize != nl_batch_tx_bufsize) { + if (nl_batch_tx_buf) + XFREE(MTYPE_NL_BUF, nl_batch_tx_buf); + + nl_batch_tx_buf = XCALLOC(MTYPE_NL_BUF, bufsize); + nl_batch_tx_bufsize = bufsize; + } + + bth->buf = nl_batch_tx_buf; + bth->bufsiz = bufsize; + bth->limit = atomic_load_explicit(&nl_batch_send_threshold, + memory_order_relaxed); + + bth->ctx_out_q = ctx_out_q; + + nl_batch_reset(bth); +} + +static void nl_batch_send(struct nl_batch *bth) +{ + struct zebra_dplane_ctx *ctx; + bool err = false; + + if (bth->curlen != 0 && bth->zns != NULL) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug("%s: %s, batch size=%zu, msg cnt=%zu", + __func__, bth->zns->nls.name, bth->curlen, + bth->msgcnt); + + if (netlink_send_msg(&(bth->zns->nls), bth->buf, bth->curlen) + == -1) + err = true; + + if (!err) { + if (nl_batch_read_resp(bth) == -1) + err = true; + } + } + + /* Move remaining contexts to the outbound queue. */ + while (true) { + ctx = dplane_ctx_dequeue(&(bth->ctx_list)); + if (ctx == NULL) + break; + + if (err) + dplane_ctx_set_status(ctx, + ZEBRA_DPLANE_REQUEST_FAILURE); + + dplane_ctx_enqueue_tail(bth->ctx_out_q, ctx); + } + + nl_batch_reset(bth); +} + +enum netlink_msg_status netlink_batch_add_msg( + struct nl_batch *bth, struct zebra_dplane_ctx *ctx, + ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t), + bool ignore_res) +{ + int seq; + ssize_t size; + struct nlmsghdr *msgh; + + size = (*msg_encoder)(ctx, bth->buf_head, bth->bufsiz - bth->curlen); + + /* + * If there was an error while encoding the message (other than buffer + * overflow) then return an error. + */ + if (size < 0) + return FRR_NETLINK_ERROR; + + /* + * If the message doesn't fit entirely in the buffer then send the batch + * and retry. + */ + if (size == 0) { + nl_batch_send(bth); + size = (*msg_encoder)(ctx, bth->buf_head, + bth->bufsiz - bth->curlen); + /* + * If the message doesn't fit in the empty buffer then just + * return an error. + */ + if (size <= 0) + return FRR_NETLINK_ERROR; + } + + seq = dplane_ctx_get_ns(ctx)->nls.seq; + if (ignore_res) + seq++; + + msgh = (struct nlmsghdr *)bth->buf_head; + msgh->nlmsg_seq = seq; + msgh->nlmsg_pid = dplane_ctx_get_ns(ctx)->nls.snl.nl_pid; + + bth->zns = dplane_ctx_get_ns(ctx); + bth->buf_head = ((char *)bth->buf_head) + size; + bth->curlen += size; + bth->msgcnt++; + + return FRR_NETLINK_QUEUED; +} + +static enum netlink_msg_status nl_put_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + if (dplane_ctx_is_skip_kernel(ctx)) + return FRR_NETLINK_SUCCESS; + + switch (dplane_ctx_get_op(ctx)) { + + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + return netlink_put_route_update_msg(bth, ctx); + + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + return netlink_put_nexthop_update_msg(bth, ctx); + + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + return netlink_put_lsp_update_msg(bth, ctx); + + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + return netlink_put_pw_update_msg(bth, ctx); + + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + return netlink_put_address_update_msg(bth, ctx); + + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + return netlink_put_mac_update_msg(bth, ctx); + + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + return netlink_put_neigh_update_msg(bth, ctx); + + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + return netlink_put_rule_update_msg(bth, ctx); + + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + return FRR_NETLINK_SUCCESS; + + case DPLANE_OP_NONE: + return FRR_NETLINK_ERROR; + } + + return FRR_NETLINK_ERROR; +} + +void kernel_update_multi(struct dplane_ctx_q *ctx_list) +{ + struct nl_batch batch; + struct zebra_dplane_ctx *ctx; + struct dplane_ctx_q handled_list; + enum netlink_msg_status res; + + TAILQ_INIT(&handled_list); + nl_batch_init(&batch, &handled_list); + + while (true) { + ctx = dplane_ctx_dequeue(ctx_list); + if (ctx == NULL) + break; + + if (batch.zns != NULL + && batch.zns->ns_id != dplane_ctx_get_ns(ctx)->ns_id) + nl_batch_send(&batch); + + /* + * Assume all messages will succeed and then mark only the ones + * that failed. + */ + dplane_ctx_set_status(ctx, ZEBRA_DPLANE_REQUEST_SUCCESS); + + res = nl_put_msg(&batch, ctx); + + dplane_ctx_enqueue_tail(&(batch.ctx_list), ctx); + if (res == FRR_NETLINK_ERROR) + dplane_ctx_set_status(ctx, + ZEBRA_DPLANE_REQUEST_FAILURE); + + if (batch.curlen > batch.limit) + nl_batch_send(&batch); + } + + nl_batch_send(&batch); + + TAILQ_INIT(ctx_list); + dplane_ctx_list_append(ctx_list, &handled_list); +} + /* Exported interface function. This function simply calls netlink_socket (). */ void kernel_init(struct zebra_ns *zns) diff --git a/zebra/kernel_netlink.h b/zebra/kernel_netlink.h index bd8159faf3..c02e16480b 100644 --- a/zebra/kernel_netlink.h +++ b/zebra/kernel_netlink.h @@ -98,13 +98,49 @@ extern int netlink_talk_filter(struct nlmsghdr *h, ns_id_t ns, int startup); extern int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), struct nlmsghdr *n, struct nlsock *nl, struct zebra_ns *zns, int startup); -/* Version with 'info' struct only */ -int netlink_talk_info(int (*filter)(struct nlmsghdr *, ns_id_t, int startup), - struct nlmsghdr *n, - const struct zebra_dplane_info *dp_info, int startup); - extern int netlink_request(struct nlsock *nl, void *req); +enum netlink_msg_status { + FRR_NETLINK_SUCCESS, + FRR_NETLINK_ERROR, + FRR_NETLINK_QUEUED, +}; + +struct nl_batch; + +/* + * netlink_batch_add_msg - add message to the netlink batch using dplane + * context object. + * + * @ctx: Dataplane context + * @msg_encoder: A function that encodes dplane context object into + * netlink message. Should take dplane context object, + * pointer to a buffer and buffer's length as parameters + * and should return -1 on error, 0 on buffer overflow or + * size of the encoded message. + * @ignore_res: Whether the result of this message should be ignored. + * This should be used in some 'update' cases where we + * need to send two messages for one context object. + * + * Return: Status of the message. + */ +extern enum netlink_msg_status netlink_batch_add_msg( + struct nl_batch *bth, struct zebra_dplane_ctx *ctx, + ssize_t (*msg_encoder)(struct zebra_dplane_ctx *, void *, size_t), + bool ignore_res); + +/* + * Vty/cli apis + */ +extern int netlink_config_write_helper(struct vty *vty); + +/* + * Configure size of the batch buffer and sending threshold. If 'unset', reset + * to default value. + */ +extern void netlink_set_batch_buffer_size(uint32_t size, uint32_t threshold, + bool set); + #endif /* HAVE_NETLINK */ #ifdef __cplusplus diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 950690b943..4c29b999f0 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -712,8 +712,7 @@ int ifm_read(struct if_msghdr *ifm) { if (ifp->ifindex != ifm->ifm_index) { zlog_debug( - "%s: index mismatch, ifname %s, ifp index %d, " - "ifm index %d", + "%s: index mismatch, ifname %s, ifp index %d, ifm index %d", __func__, ifp->name, ifp->ifindex, ifm->ifm_index); return -1; @@ -833,9 +832,7 @@ static void ifam_read_mesg(struct ifa_msghdr *ifm, union sockunion *addr, ? ip_masklen(mask->sin.sin_addr) : ip6_masklen(mask->sin6.sin6_addr); zlog_debug( - "%s: ifindex %d, ifname %s, ifam_addrs {%s}, " - "ifam_flags 0x%x, addr %s/%d broad %s dst %s " - "gateway %s", + "%s: ifindex %d, ifname %s, ifam_addrs {%s}, ifam_flags 0x%x, addr %s/%d broad %s dst %s gateway %s", __func__, ifm->ifam_index, (ifnlen ? ifname : "(nil)"), rtatostr(ifm->ifam_addrs, fbuf, sizeof(fbuf)), @@ -978,8 +975,7 @@ static int rtm_read_mesg(struct rt_msghdr *rtm, union sockunion *dest, /* rt_msghdr version check. */ if (rtm->rtm_version != RTM_VERSION) flog_warn(EC_ZEBRA_RTM_VERSION_MISMATCH, - "Routing message version different %d should be %d." - "This may cause problem\n", + "Routing message version different %d should be %d.This may cause problem\n", rtm->rtm_version, RTM_VERSION); /* Be sure structure is cleared */ @@ -1468,4 +1464,99 @@ void kernel_terminate(struct zebra_ns *zns, bool complete) return; } +void kernel_update_multi(struct dplane_ctx_q *ctx_list) +{ + struct zebra_dplane_ctx *ctx; + struct dplane_ctx_q handled_list; + enum zebra_dplane_result res; + + TAILQ_INIT(&handled_list); + + while (true) { + ctx = dplane_ctx_dequeue(ctx_list); + if (ctx == NULL) + break; + + /* + * A previous provider plugin may have asked to skip the + * kernel update. + */ + if (dplane_ctx_is_skip_kernel(ctx)) { + res = ZEBRA_DPLANE_REQUEST_SUCCESS; + goto skip_one; + } + + switch (dplane_ctx_get_op(ctx)) { + + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + res = kernel_route_update(ctx); + break; + + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: + res = kernel_nexthop_update(ctx); + break; + + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + res = kernel_lsp_update(ctx); + break; + + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + res = kernel_pw_update(ctx); + break; + + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + res = kernel_address_update_ctx(ctx); + break; + + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: + res = kernel_mac_update_ctx(ctx); + break; + + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: + res = kernel_neigh_update_ctx(ctx); + break; + + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: + res = kernel_pbr_rule_update(ctx); + break; + + /* Ignore 'notifications' - no-op */ + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: + res = ZEBRA_DPLANE_REQUEST_SUCCESS; + break; + + default: + res = ZEBRA_DPLANE_REQUEST_FAILURE; + break; + } + + skip_one: + dplane_ctx_set_status(ctx, res); + + dplane_ctx_enqueue_tail(&handled_list, ctx); + } + + TAILQ_INIT(ctx_list); + dplane_ctx_list_append(ctx_list, &handled_list); +} + #endif /* !HAVE_NETLINK */ diff --git a/zebra/label_manager.c b/zebra/label_manager.c index 93736e672a..d312a661f3 100644 --- a/zebra/label_manager.c +++ b/zebra/label_manager.c @@ -240,7 +240,7 @@ assign_specific_label_chunk(uint8_t proto, unsigned short instance, if (lmc->proto != NO_PROTO) return NULL; - if (end < lmc->end) { + if (end <= lmc->end) { last_node = node; break; } diff --git a/zebra/main.c b/zebra/main.c index 71c7ebb62f..64746f7166 100644 --- a/zebra/main.c +++ b/zebra/main.c @@ -35,6 +35,7 @@ #include "vrf.h" #include "libfrr.h" #include "routemap.h" +#include "routing_nb.h" #include "zebra/zebra_router.h" #include "zebra/zebra_errors.h" @@ -56,6 +57,7 @@ #include "zebra/zebra_routemap.h" #include "zebra/zebra_nb.h" #include "zebra/zebra_opaque.h" +#include "zebra/zebra_srte.h" #if defined(HANDLE_NETLINK_FUZZING) #include "zebra/kernel_netlink.h" @@ -185,7 +187,7 @@ static void sigint(void) vrf_terminate(); rtadv_terminate(); - ns_walk_func(zebra_ns_early_shutdown, NULL, NULL); + ns_walk_func(zebra_ns_early_shutdown); zebra_ns_notify_close(); access_list_reset(); @@ -216,7 +218,7 @@ int zebra_finalize(struct thread *dummy) zlog_info("Zebra final shutdown"); /* Final shutdown of ns resources */ - ns_walk_func(zebra_ns_final_shutdown, NULL, NULL); + ns_walk_func(zebra_ns_final_shutdown); /* Stop dplane thread and finish any cleanup */ zebra_dplane_shutdown(); @@ -258,14 +260,14 @@ static const struct frr_yang_module_info *const zebra_yang_modules[] = { &frr_route_map_info, &frr_zebra_info, &frr_vrf_info, + &frr_routing_info, }; FRR_DAEMON_INFO( zebra, ZEBRA, .vty_port = ZEBRA_VTY_PORT, .flags = FRR_NO_ZCLIENT, .proghelp = - "Daemon which manages kernel routing table management " - "and\nredistribution between different routing protocols.", + "Daemon which manages kernel routing table management and\nredistribution between different routing protocols.", .signals = zebra_signals, .n_signals = array_size(zebra_signals), @@ -415,12 +417,12 @@ int main(int argc, char **argv) rib_init(); zebra_if_init(); zebra_debug_init(); - router_id_cmd_init(); /* * Initialize NS( and implicitly the VRF module), and make kernel * routing socket. */ zebra_ns_init((const char *)vrf_default_name_configured); + router_id_cmd_init(); zebra_vty_init(); access_list_init(); prefix_list_init(); @@ -437,9 +439,10 @@ int main(int argc, char **argv) zebra_pw_vty_init(); zebra_pbr_init(); zebra_opaque_init(); + zebra_srte_init(); -/* For debug purpose. */ -/* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ + /* For debug purpose. */ + /* SET_FLAG (zebra_debug_event, ZEBRA_DEBUG_EVENT); */ /* Process the configuration file. Among other configuration * directives we can meet those installing static routes. Such diff --git a/zebra/rib.h b/zebra/rib.h index ec992974fa..b9f4e56905 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -144,6 +144,10 @@ struct route_entry { #define ROUTE_ENTRY_INSTALLED 0x10 /* Route has Failed installation into the Data Plane in some manner */ #define ROUTE_ENTRY_FAILED 0x20 +/* Route has a 'fib' set of nexthops, probably because the installed set + * differs from the rib/normal set of nexthops. + */ +#define ROUTE_ENTRY_USE_FIB_NHG 0x40 /* Sequence value incremented for each dataplane operation */ uint32_t dplane_sequence; @@ -296,6 +300,7 @@ struct rib_table_info { struct zebra_vrf *zvrf; afi_t afi; safi_t safi; + uint32_t table_id; }; enum rib_tables_iter_state { @@ -526,26 +531,28 @@ DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason), (rn, reason)) /* - * Access active nexthop-group, either RIB or FIB version + * Access installed/fib nexthops, which may be a subset of the + * rib nexthops. */ static inline struct nexthop_group *rib_get_fib_nhg(struct route_entry *re) { - if (re->fib_ng.nexthop) + /* If the fib set is a subset of the active rib set, + * use the dedicated fib list. + */ + if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG)) return &(re->fib_ng); else return &(re->nhe->nhg); } /* - * Access active nexthop-group, either RIB or FIB version + * Access backup nexthop-group that represents the installed backup nexthops; + * any installed backup will be on the fib list. */ static inline struct nexthop_group *rib_get_fib_backup_nhg( struct route_entry *re) { - if (re->fib_backup_ng.nexthop) - return &(re->fib_backup_ng); - else - return zebra_nhg_get_backup_nhg(re->nhe); + return &(re->fib_backup_ng); } extern void zebra_vty_init(void); diff --git a/zebra/router-id.c b/zebra/router-id.c index 710f2f6c27..7e81f29827 100644 --- a/zebra/router-id.c +++ b/zebra/router-id.c @@ -59,9 +59,6 @@ static struct connected *router_id_find_node(struct list *l, static int router_id_bad_address(struct connected *ifc) { - if (ifc->address->family != AF_INET) - return 1; - /* non-redistributable addresses shouldn't be used for RIDs either */ if (!zebra_check_addr(ifc->address)) return 1; @@ -69,50 +66,82 @@ static int router_id_bad_address(struct connected *ifc) return 0; } -void router_id_get(struct prefix *p, vrf_id_t vrf_id) +static bool router_id_v6_is_any(struct prefix *p) +{ + return memcmp(&p->u.prefix6, &in6addr_any, sizeof(struct in6_addr)) + == 0; +} + +int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) { struct listnode *node; struct connected *c; - struct zebra_vrf *zvrf = vrf_info_get(vrf_id); - - p->u.prefix4.s_addr = INADDR_ANY; - p->family = AF_INET; - p->prefixlen = 32; - - if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) - p->u.prefix4.s_addr = zvrf->rid_user_assigned.u.prefix4.s_addr; - else if (!list_isempty(zvrf->rid_lo_sorted_list)) { - node = listtail(zvrf->rid_lo_sorted_list); - c = listgetdata(node); - p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; - } else if (!list_isempty(zvrf->rid_all_sorted_list)) { - node = listtail(zvrf->rid_all_sorted_list); - c = listgetdata(node); - p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + struct in6_addr *addr = NULL; + + switch (afi) { + case AFI_IP: + p->u.prefix4.s_addr = INADDR_ANY; + p->family = AF_INET; + p->prefixlen = 32; + if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) + p->u.prefix4.s_addr = + zvrf->rid_user_assigned.u.prefix4.s_addr; + else if (!list_isempty(zvrf->rid_lo_sorted_list)) { + node = listtail(zvrf->rid_lo_sorted_list); + c = listgetdata(node); + p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + } else if (!list_isempty(zvrf->rid_all_sorted_list)) { + node = listtail(zvrf->rid_all_sorted_list); + c = listgetdata(node); + p->u.prefix4.s_addr = c->address->u.prefix4.s_addr; + } + return 0; + case AFI_IP6: + p->u.prefix6 = in6addr_any; + p->family = AF_INET6; + p->prefixlen = 128; + if (!router_id_v6_is_any(&zvrf->rid6_user_assigned)) + addr = &zvrf->rid6_user_assigned.u.prefix6; + else if (!list_isempty(zvrf->rid6_lo_sorted_list)) { + node = listtail(zvrf->rid6_lo_sorted_list); + c = listgetdata(node); + addr = &c->address->u.prefix6; + } else if (!list_isempty(zvrf->rid6_all_sorted_list)) { + node = listtail(zvrf->rid6_all_sorted_list); + c = listgetdata(node); + addr = &c->address->u.prefix6; + } + if (addr) + memcpy(&p->u.prefix6, addr, sizeof(struct in6_addr)); + return 0; + default: + return -1; } } -static void router_id_set(struct prefix *p, vrf_id_t vrf_id) +static int router_id_set(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf) { struct prefix p2; struct listnode *node; struct zserv *client; - struct zebra_vrf *zvrf; - if (p->u.prefix4.s_addr == 0) /* unset */ - { - zvrf = vrf_info_lookup(vrf_id); - if (!zvrf) - return; - } else /* set */ - zvrf = vrf_info_get(vrf_id); - - zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; + switch (afi) { + case AFI_IP: + zvrf->rid_user_assigned.u.prefix4.s_addr = p->u.prefix4.s_addr; + break; + case AFI_IP6: + zvrf->rid6_user_assigned.u.prefix6 = p->u.prefix6; + break; + default: + return -1; + } - router_id_get(&p2, vrf_id); + router_id_get(afi, &p2, zvrf); for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, &p2, vrf_id); + zsend_router_id_update(client, afi, &p2, zvrf->vrf->vrf_id); + + return 0; } void router_id_add_address(struct connected *ifc) @@ -123,27 +152,42 @@ void router_id_add_address(struct connected *ifc) struct prefix after; struct zserv *client; struct zebra_vrf *zvrf = vrf_info_get(ifc->ifp->vrf_id); + afi_t afi; + struct list *rid_lo; + struct list *rid_all; if (router_id_bad_address(ifc)) return; - router_id_get(&before, zvrf_id(zvrf)); + switch (ifc->address->family) { + case AF_INET: + afi = AFI_IP; + rid_lo = zvrf->rid_lo_sorted_list; + rid_all = zvrf->rid_all_sorted_list; + break; + case AF_INET6: + afi = AFI_IP6; + rid_lo = zvrf->rid6_lo_sorted_list; + rid_all = zvrf->rid6_all_sorted_list; + break; + default: + return; + } + + router_id_get(afi, &before, zvrf); - if (if_is_loopback(ifc->ifp)) - l = zvrf->rid_lo_sorted_list; - else - l = zvrf->rid_all_sorted_list; + l = if_is_loopback(ifc->ifp) ? rid_lo : rid_all; if (!router_id_find_node(l, ifc)) listnode_add_sort(l, ifc); - router_id_get(&after, zvrf_id(zvrf)); + router_id_get(afi, &after, zvrf); if (prefix_same(&before, &after)) return; for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, &after, zvrf_id(zvrf)); + zsend_router_id_update(client, afi, &after, zvrf_id(zvrf)); } void router_id_del_address(struct connected *ifc) @@ -155,134 +199,340 @@ void router_id_del_address(struct connected *ifc) struct listnode *node; struct zserv *client; struct zebra_vrf *zvrf = vrf_info_get(ifc->ifp->vrf_id); + afi_t afi; + struct list *rid_lo; + struct list *rid_all; if (router_id_bad_address(ifc)) return; - router_id_get(&before, zvrf_id(zvrf)); + switch (ifc->address->family) { + case AF_INET: + afi = AFI_IP; + rid_lo = zvrf->rid_lo_sorted_list; + rid_all = zvrf->rid_all_sorted_list; + break; + case AF_INET6: + afi = AFI_IP6; + rid_lo = zvrf->rid6_lo_sorted_list; + rid_all = zvrf->rid6_all_sorted_list; + break; + default: + return; + } + + router_id_get(afi, &before, zvrf); if (if_is_loopback(ifc->ifp)) - l = zvrf->rid_lo_sorted_list; + l = rid_lo; else - l = zvrf->rid_all_sorted_list; + l = rid_all; if ((c = router_id_find_node(l, ifc))) listnode_delete(l, c); - router_id_get(&after, zvrf_id(zvrf)); + router_id_get(afi, &after, zvrf); if (prefix_same(&before, &after)) return; for (ALL_LIST_ELEMENTS_RO(zrouter.client_list, node, client)) - zsend_router_id_update(client, &after, zvrf_id(zvrf)); + zsend_router_id_update(client, afi, &after, zvrf_id(zvrf)); +} + +void router_id_write(struct vty *vty, struct zebra_vrf *zvrf) +{ + char space[2]; + + memset(space, 0, sizeof(space)); + + if (zvrf_id(zvrf) != VRF_DEFAULT) + snprintf(space, sizeof(space), "%s", " "); + + if (zvrf->rid_user_assigned.u.prefix4.s_addr != INADDR_ANY) { + vty_out(vty, "%sip router-id %pI4\n", space, + &zvrf->rid_user_assigned.u.prefix4); + } + if (!router_id_v6_is_any(&zvrf->rid6_user_assigned)) { + vty_out(vty, "%sipv6 router-id %pI6\n", space, + &zvrf->rid_user_assigned.u.prefix6); + } } -void router_id_write(struct vty *vty) +DEFUN (ip_router_id, + ip_router_id_cmd, + "ip router-id A.B.C.D vrf NAME", + IP_STR + "Manually set the router-id\n" + "IP address to use for router-id\n" + VRF_CMD_HELP_STR) { - struct vrf *vrf; + int idx = 0; + struct prefix rid; + vrf_id_t vrf_id; struct zebra_vrf *zvrf; - RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) - if ((zvrf = vrf->info) != NULL) - if (zvrf->rid_user_assigned.u.prefix4.s_addr - != INADDR_ANY) { - if (zvrf_id(zvrf) == VRF_DEFAULT) - vty_out(vty, "router-id %s\n", - inet_ntoa( - zvrf->rid_user_assigned - .u.prefix4)); - else - vty_out(vty, "router-id %s vrf %s\n", - inet_ntoa( - zvrf->rid_user_assigned - .u.prefix4), - zvrf_name(zvrf)); - } + argv_find(argv, argc, "A.B.C.D", &idx); + + if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4)) + return CMD_WARNING_CONFIG_FAILED; + + rid.prefixlen = 32; + rid.family = AF_INET; + + argv_find(argv, argc, "NAME", &idx); + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP, &rid, zvrf); + + return CMD_SUCCESS; } -DEFUN (router_id, +ALIAS (ip_router_id, router_id_cmd, - "router-id A.B.C.D [vrf NAME]", + "router-id A.B.C.D vrf NAME", "Manually set the router-id\n" "IP address to use for router-id\n" + VRF_CMD_HELP_STR); + +DEFUN (ipv6_router_id, + ipv6_router_id_cmd, + "ipv6 router-id X:X::X:X vrf NAME", + IPV6_STR + "Manually set the router-id\n" + "IPv6 address to use for router-id\n" VRF_CMD_HELP_STR) { - int idx_ipv4 = 1; - int idx_name = 3; + int idx = 0; + struct prefix rid; + vrf_id_t vrf_id; + struct zebra_vrf *zvrf; + + argv_find(argv, argc, "X:X::X:X", &idx); + + if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6)) + return CMD_WARNING_CONFIG_FAILED; + + rid.prefixlen = 128; + rid.family = AF_INET6; + + argv_find(argv, argc, "NAME", &idx); + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP6, &rid, zvrf); + + return CMD_SUCCESS; +} + + +DEFUN (ip_router_id_in_vrf, + ip_router_id_in_vrf_cmd, + "ip router-id A.B.C.D", + IP_STR + "Manuall set the router-id\n" + "IP address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + int idx = 0; struct prefix rid; - vrf_id_t vrf_id = VRF_DEFAULT; - rid.u.prefix4.s_addr = inet_addr(argv[idx_ipv4]->arg); - if (!rid.u.prefix4.s_addr) + argv_find(argv, argc, "A.B.C.D", &idx); + + if (!inet_pton(AF_INET, argv[idx]->arg, &rid.u.prefix4)) return CMD_WARNING_CONFIG_FAILED; rid.prefixlen = 32; rid.family = AF_INET; - if (argc > 2) - VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + router_id_set(AFI_IP, &rid, zvrf); - router_id_set(&rid, vrf_id); + return CMD_SUCCESS; +} + +ALIAS (ip_router_id_in_vrf, + router_id_in_vrf_cmd, + "router-id A.B.C.D", + "Manually set the router-id\n" + "IP address to use for router-id\n"); + +DEFUN (ipv6_router_id_in_vrf, + ipv6_router_id_in_vrf_cmd, + "ipv6 router-id X:X::X:X", + IP6_STR + "Manuall set the IPv6 router-id\n" + "IPV6 address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + int idx = 0; + struct prefix rid; + + argv_find(argv, argc, "X:X::X:X", &idx); + + if (!inet_pton(AF_INET6, argv[idx]->arg, &rid.u.prefix6)) + return CMD_WARNING_CONFIG_FAILED; + + rid.prefixlen = 128; + rid.family = AF_INET6; + + router_id_set(AFI_IP6, &rid, zvrf); return CMD_SUCCESS; } -DEFUN (no_router_id, - no_router_id_cmd, - "no router-id [A.B.C.D [vrf NAME]]", +DEFUN (no_ip_router_id, + no_ip_router_id_cmd, + "no ip router-id [A.B.C.D vrf NAME]", NO_STR + IP_STR "Remove the manually configured router-id\n" "IP address to use for router-id\n" VRF_CMD_HELP_STR) { - int idx_name = 4; + int idx = 0; + struct prefix rid; + vrf_id_t vrf_id = VRF_DEFAULT; + struct zebra_vrf *zvrf; + + rid.u.prefix4.s_addr = 0; + rid.prefixlen = 0; + rid.family = AF_INET; + + if (argv_find(argv, argc, "NAME", &idx)) + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP, &rid, zvrf); + + return CMD_SUCCESS; +} +ALIAS (no_ip_router_id, + no_router_id_cmd, + "no router-id [A.B.C.D vrf NAME]", + NO_STR + "Remove the manually configured router-id\n" + "IP address to use for router-id\n" + VRF_CMD_HELP_STR); + +DEFUN (no_ipv6_router_id, + no_ipv6_router_id_cmd, + "no ipv6 router-id [X:X::X:X vrf NAME]", + NO_STR + IPV6_STR + "Remove the manually configured IPv6 router-id\n" + "IPv6 address to use for router-id\n" + VRF_CMD_HELP_STR) +{ + int idx = 0; struct prefix rid; vrf_id_t vrf_id = VRF_DEFAULT; + struct zebra_vrf *zvrf; + + memset(&rid, 0, sizeof(rid)); + rid.family = AF_INET; + + if (argv_find(argv, argc, "NAME", &idx)) + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + + zvrf = vrf_info_lookup(vrf_id); + router_id_set(AFI_IP6, &rid, zvrf); + + return CMD_SUCCESS; +} + +DEFUN (no_ip_router_id_in_vrf, + no_ip_router_id_in_vrf_cmd, + "no ip router-id [A.B.C.D]", + NO_STR + IP_STR + "Remove the manually configured router-id\n" + "IP address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + + struct prefix rid; rid.u.prefix4.s_addr = 0; rid.prefixlen = 0; rid.family = AF_INET; - if (argc > 3) - VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + router_id_set(AFI_IP, &rid, zvrf); + + return CMD_SUCCESS; +} + +ALIAS (no_ip_router_id_in_vrf, + no_router_id_in_vrf_cmd, + "no router-id [A.B.C.D]", + NO_STR + "Remove the manually configured router-id\n" + "IP address to use for router-id\n"); + +DEFUN (no_ipv6_router_id_in_vrf, + no_ipv6_router_id_in_vrf_cmd, + "no ipv6 router-id [X:X::X:X]", + NO_STR + IP6_STR + "Remove the manually configured IPv6 router-id\n" + "IPv6 address to use for router-id\n") +{ + ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); - router_id_set(&rid, vrf_id); + struct prefix rid; + + memset(&rid, 0, sizeof(rid)); + rid.family = AF_INET; + + router_id_set(AFI_IP6, &rid, zvrf); return CMD_SUCCESS; } -DEFUN (show_router_id, - show_router_id_cmd, - "show router-id [vrf NAME]", +DEFUN (show_ip_router_id, + show_ip_router_id_cmd, + "show [ip|ipv6] router-id [vrf NAME]", SHOW_STR + IP_STR + IPV6_STR "Show the configured router-id\n" VRF_CMD_HELP_STR) { - int idx_name = 3; - - vrf_id_t vrf_id = VRF_DEFAULT; - struct zebra_vrf *zvrf; + int idx = 0; + vrf_id_t vrf_id = VRF_DEFAULT; + struct zebra_vrf *zvrf; + const char *vrf_name = "default"; + char addr_name[INET6_ADDRSTRLEN]; + int is_ipv6 = 0; - if (argc > 2) - VRF_GET_ID(vrf_id, argv[idx_name]->arg, false); + is_ipv6 = argv_find(argv, argc, "ipv6", &idx); - zvrf = vrf_info_get(vrf_id); + if (argv_find(argv, argc, "NAME", &idx)) { + VRF_GET_ID(vrf_id, argv[idx]->arg, false); + vrf_name = argv[idx]->arg; + } - if ((zvrf != NULL) && (zvrf->rid_user_assigned.u.prefix4.s_addr)) { - vty_out(vty, "zebra:\n"); - if (vrf_id == VRF_DEFAULT) - vty_out(vty, " router-id %s vrf default\n", - inet_ntoa(zvrf->rid_user_assigned.u.prefix4)); - else - vty_out(vty, " router-id %s vrf %s\n", - inet_ntoa(zvrf->rid_user_assigned.u.prefix4), - argv[idx_name]->arg); - } + zvrf = vrf_info_get(vrf_id); + + if (zvrf != NULL) { + if (is_ipv6) { + if (router_id_v6_is_any(&zvrf->rid6_user_assigned)) + return CMD_SUCCESS; + inet_ntop(AF_INET6, &zvrf->rid6_user_assigned.u.prefix6, + addr_name, sizeof(addr_name)); + } else { + if (zvrf->rid_user_assigned.u.prefix4.s_addr == 0) + return CMD_SUCCESS; + inet_ntop(AF_INET, &zvrf->rid_user_assigned.u.prefix4, + addr_name, sizeof(addr_name)); + } + + vty_out(vty, "zebra:\n"); + vty_out(vty, " router-id %s vrf %s\n", addr_name, vrf_name); + } - return CMD_SUCCESS; + return CMD_SUCCESS; } static int router_id_cmp(void *a, void *b) @@ -294,26 +544,62 @@ static int router_id_cmp(void *a, void *b) &ifb->address->u.prefix4.s_addr); } +static int router_id_v6_cmp(void *a, void *b) +{ + const struct connected *ifa = (const struct connected *)a; + const struct connected *ifb = (const struct connected *)b; + + return IPV6_ADDR_CMP(&ifa->address->u.prefix6, + &ifb->address->u.prefix6); +} + void router_id_cmd_init(void) { + install_element(CONFIG_NODE, &ip_router_id_cmd); install_element(CONFIG_NODE, &router_id_cmd); + install_element(CONFIG_NODE, &ipv6_router_id_cmd); + install_element(CONFIG_NODE, &no_ip_router_id_cmd); install_element(CONFIG_NODE, &no_router_id_cmd); - install_element(VIEW_NODE, &show_router_id_cmd); + install_element(CONFIG_NODE, &ip_router_id_in_vrf_cmd); + install_element(VRF_NODE, &ip_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &router_id_in_vrf_cmd); + install_element(VRF_NODE, &router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &ipv6_router_id_in_vrf_cmd); + install_element(VRF_NODE, &ipv6_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &no_ipv6_router_id_cmd); + install_element(CONFIG_NODE, &no_ip_router_id_in_vrf_cmd); + install_element(VRF_NODE, &no_ip_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &no_router_id_in_vrf_cmd); + install_element(VRF_NODE, &no_router_id_in_vrf_cmd); + install_element(CONFIG_NODE, &no_ipv6_router_id_in_vrf_cmd); + install_element(VRF_NODE, &no_ipv6_router_id_in_vrf_cmd); + install_element(VIEW_NODE, &show_ip_router_id_cmd); } void router_id_init(struct zebra_vrf *zvrf) { zvrf->rid_all_sorted_list = &zvrf->_rid_all_sorted_list; zvrf->rid_lo_sorted_list = &zvrf->_rid_lo_sorted_list; + zvrf->rid6_all_sorted_list = &zvrf->_rid6_all_sorted_list; + zvrf->rid6_lo_sorted_list = &zvrf->_rid6_lo_sorted_list; memset(zvrf->rid_all_sorted_list, 0, sizeof(zvrf->_rid_all_sorted_list)); memset(zvrf->rid_lo_sorted_list, 0, sizeof(zvrf->_rid_lo_sorted_list)); memset(&zvrf->rid_user_assigned, 0, sizeof(zvrf->rid_user_assigned)); + memset(zvrf->rid6_all_sorted_list, 0, + sizeof(zvrf->_rid6_all_sorted_list)); + memset(zvrf->rid6_lo_sorted_list, 0, + sizeof(zvrf->_rid6_lo_sorted_list)); + memset(&zvrf->rid6_user_assigned, 0, sizeof(zvrf->rid6_user_assigned)); zvrf->rid_all_sorted_list->cmp = router_id_cmp; zvrf->rid_lo_sorted_list->cmp = router_id_cmp; + zvrf->rid6_all_sorted_list->cmp = router_id_v6_cmp; + zvrf->rid6_lo_sorted_list->cmp = router_id_v6_cmp; zvrf->rid_user_assigned.family = AF_INET; zvrf->rid_user_assigned.prefixlen = 32; + zvrf->rid6_user_assigned.family = AF_INET6; + zvrf->rid6_user_assigned.prefixlen = 128; } diff --git a/zebra/router-id.h b/zebra/router-id.h index f7d16853f1..4a35f6605b 100644 --- a/zebra/router-id.h +++ b/zebra/router-id.h @@ -34,12 +34,12 @@ extern "C" { #endif -extern void router_id_add_address(struct connected *); -extern void router_id_del_address(struct connected *); -extern void router_id_init(struct zebra_vrf *); +extern void router_id_add_address(struct connected *c); +extern void router_id_del_address(struct connected *c); +extern void router_id_init(struct zebra_vrf *zvrf); extern void router_id_cmd_init(void); -extern void router_id_write(struct vty *); -extern void router_id_get(struct prefix *, vrf_id_t); +extern void router_id_write(struct vty *vty, struct zebra_vrf *zvrf); +extern int router_id_get(afi_t afi, struct prefix *p, struct zebra_vrf *zvrf); #ifdef __cplusplus } diff --git a/zebra/rt.h b/zebra/rt.h index 4b9a3f83fe..48f1df2868 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -40,7 +40,7 @@ extern "C" { #define RSYSTEM_ROUTE(type) \ ((RKERNEL_ROUTE(type)) || (type) == ZEBRA_ROUTE_CONNECT) - +#ifndef HAVE_NETLINK /* * Update or delete a route, nexthop, LSP, pseudowire, or vxlan MAC from the * kernel, using info from a dataplane context. @@ -63,6 +63,11 @@ enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx); enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx); +extern enum zebra_dplane_result +kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); + +#endif /* !HAVE_NETLINK */ + extern int kernel_neigh_update(int cmd, int ifindex, uint32_t addr, char *lla, int llalen, ns_id_t ns_id); extern int kernel_interface_set_master(struct interface *master, @@ -91,6 +96,16 @@ extern void neigh_read_for_vlan(struct zebra_ns *zns, struct interface *ifp); extern void neigh_read_specific_ip(struct ipaddr *ip, struct interface *vlan_if); extern void route_read(struct zebra_ns *zns); +extern int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip); +extern int kernel_del_mac_nh(uint32_t nh_id); +extern int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt, + struct nh_grp *nh_ids); +extern int kernel_del_mac_nhg(uint32_t nhg_id); + +/* + * Message batching interface. + */ +extern void kernel_update_multi(struct dplane_ctx_q *ctx_list); #ifdef __cplusplus } diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index 40a7eeba8e..4a6839d3b1 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -68,11 +68,27 @@ #include "zebra/zebra_mroute.h" #include "zebra/zebra_vxlan.h" #include "zebra/zebra_errors.h" +#include "zebra/zebra_evpn_mh.h" #ifndef AF_MPLS #define AF_MPLS 28 #endif +/* Re-defining as I am unable to include <linux/if_bridge.h> which has the + * UAPI for MAC sync. */ +#ifndef _UAPI_LINUX_IF_BRIDGE_H +/* FDB notification bits for NDA_NOTIFY: + * - BR_FDB_NFY_STATIC - notify on activity/expire even for a static entry + * - BR_FDB_NFY_INACTIVE - mark as inactive to avoid double notification, + * used with BR_FDB_NFY_STATIC (kernel controlled) + */ +enum { + BR_FDB_NFY_STATIC, + BR_FDB_NFY_INACTIVE, + BR_FDB_NFY_MAX +}; +#endif + static vlanid_t filter_vlan = 0; /* We capture whether the current kernel supports nexthop ids; by @@ -131,6 +147,8 @@ static uint8_t neigh_flags_to_netlink(uint8_t dplane_flags) flags |= NTF_EXT_LEARNED; if (dplane_flags & DPLANE_NTF_ROUTER) flags |= NTF_ROUTER; + if (dplane_flags & DPLANE_NTF_USE) + flags |= NTF_USE; return flags; } @@ -150,6 +168,8 @@ static uint16_t neigh_state_to_netlink(uint16_t dplane_state) state |= NUD_NOARP; if (dplane_state & DPLANE_NUD_PROBE) state |= NUD_PROBE; + if (dplane_state & DPLANE_NUD_INCOMPLETE) + state |= NUD_INCOMPLETE; return state; } @@ -163,7 +183,8 @@ static inline bool is_selfroute(int proto) || (proto == RTPROT_NHRP) || (proto == RTPROT_EIGRP) || (proto == RTPROT_LDP) || (proto == RTPROT_BABEL) || (proto == RTPROT_RIP) || (proto == RTPROT_SHARP) - || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC)) { + || (proto == RTPROT_PBR) || (proto == RTPROT_OPENFABRIC) + || (proto == RTPROT_SRTE)) { return true; } @@ -213,6 +234,9 @@ static inline int zebra2proto(int proto) case ZEBRA_ROUTE_OPENFABRIC: proto = RTPROT_OPENFABRIC; break; + case ZEBRA_ROUTE_SRTE: + proto = RTPROT_SRTE; + break; case ZEBRA_ROUTE_TABLE: case ZEBRA_ROUTE_NHG: proto = RTPROT_ZEBRA; @@ -278,6 +302,9 @@ static inline int proto2zebra(int proto, int family, bool is_nexthop) case RTPROT_OPENFABRIC: proto = ZEBRA_ROUTE_OPENFABRIC; break; + case RTPROT_SRTE: + proto = ZEBRA_ROUTE_SRTE; + break; case RTPROT_ZEBRA: if (is_nexthop) { proto = ZEBRA_ROUTE_NHG; @@ -1051,14 +1078,17 @@ static bool _netlink_route_add_gateway_info(uint8_t route_family, bytelen + 2)) return false; } else { - if (gw_family == AF_INET) { - if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv4, bytelen)) - return false; - } else { - if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, - &nexthop->gate.ipv6, bytelen)) - return false; + if (!(nexthop->rparent + && IS_MAPPED_IPV6(&nexthop->rparent->gate.ipv6))) { + if (gw_family == AF_INET) { + if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv4, bytelen)) + return false; + } else { + if (!nl_attr_put(nlmsg, req_size, RTA_GATEWAY, + &nexthop->gate.ipv6, bytelen)) + return false; + } } } @@ -2204,19 +2234,11 @@ nexthop_done: return NLMSG_ALIGN(req->n.nlmsg_len); } -/** - * kernel_nexthop_update() - Update/delete a nexthop from the kernel - * - * @ctx: Dataplane context - * - * Return: Dataplane result flag - */ -enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) +static ssize_t netlink_nexthop_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { enum dplane_op_e op; int cmd = 0; - int ret = 0; - char buf[NL_PKT_BUF_SIZE]; op = dplane_ctx_get_op(ctx); if (op == DPLANE_OP_NH_INSTALL || op == DPLANE_OP_NH_UPDATE) @@ -2227,33 +2249,43 @@ enum zebra_dplane_result kernel_nexthop_update(struct zebra_dplane_ctx *ctx) flog_err(EC_ZEBRA_NHG_FIB_UPDATE, "Context received for kernel nexthop update with incorrect OP code (%u)", op); - return ZEBRA_DPLANE_REQUEST_FAILURE; + return -1; } + return netlink_nexthop_msg_encode(cmd, ctx, buf, buflen); +} + +enum netlink_msg_status +netlink_put_nexthop_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ /* Nothing to do if the kernel doesn't support nexthop objects */ if (!kernel_nexthops_supported()) - return ZEBRA_DPLANE_REQUEST_SUCCESS; + return FRR_NETLINK_SUCCESS; - if (netlink_nexthop_msg_encode(cmd, ctx, buf, sizeof(buf)) > 0) - ret = netlink_talk_info(netlink_talk_filter, (void *)&buf, - dplane_ctx_get_ns(ctx), 0); - else - ret = 0; + return netlink_batch_add_msg(bth, ctx, netlink_nexthop_msg_encoder, + false); +} - return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS - : ZEBRA_DPLANE_REQUEST_FAILURE); +static ssize_t netlink_newroute_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_route_multipath_msg_encode(RTM_NEWROUTE, ctx, buf, + buflen, false, false); } -/* - * Update or delete a prefix from the kernel, - * using info from a dataplane context. - */ -enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) +static ssize_t netlink_delroute_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_route_multipath_msg_encode(RTM_DELROUTE, ctx, buf, + buflen, false, false); +} + +enum netlink_msg_status +netlink_put_route_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) { - int cmd, ret; + int cmd; const struct prefix *p = dplane_ctx_get_dest(ctx); - struct nexthop *nexthop; - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; if (dplane_ctx_get_op(ctx) == DPLANE_OP_ROUTE_DELETE) { cmd = RTM_DELROUTE; @@ -2263,7 +2295,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) if (p->family == AF_INET || v6_rr_semantics) { /* Single 'replace' operation */ - cmd = RTM_NEWROUTE; /* * With route replace semantics in place @@ -2273,17 +2304,11 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) * route should cause us to withdraw from * the kernel the old non-system route */ - if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) && - !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) { - if (netlink_route_multipath_msg_encode( - RTM_DELROUTE, ctx, nl_pkt, - sizeof(nl_pkt), false, false) - > 0) - netlink_talk_info( - netlink_talk_filter, - (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); - } + if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx)) + && !RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) + netlink_batch_add_msg( + bth, ctx, netlink_delroute_msg_encoder, + true); } else { /* * So v6 route replace semantics are not in @@ -2297,51 +2322,24 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) * of the route delete. If that happens yeah we're * screwed. */ - if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) { - if (netlink_route_multipath_msg_encode( - RTM_DELROUTE, ctx, nl_pkt, - sizeof(nl_pkt), false, false) - > 0) - netlink_talk_info( - netlink_talk_filter, - (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); - } - cmd = RTM_NEWROUTE; + if (!RSYSTEM_ROUTE(dplane_ctx_get_old_type(ctx))) + netlink_batch_add_msg( + bth, ctx, netlink_delroute_msg_encoder, + true); } - } else { - return ZEBRA_DPLANE_REQUEST_FAILURE; - } - - if (!RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) { - if (netlink_route_multipath_msg_encode( - cmd, ctx, nl_pkt, sizeof(nl_pkt), false, false) - > 0) - ret = netlink_talk_info(netlink_talk_filter, - (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); - else - ret = -1; - + cmd = RTM_NEWROUTE; } else - ret = 0; - if ((cmd == RTM_NEWROUTE) && (ret == 0)) { - /* Update installed nexthops to signal which have been - * installed. - */ - for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; + return FRR_NETLINK_ERROR; - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - } - } + if (RSYSTEM_ROUTE(dplane_ctx_get_type(ctx))) + return FRR_NETLINK_SUCCESS; - return (ret == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); + return netlink_batch_add_msg(bth, ctx, + cmd == RTM_NEWROUTE + ? netlink_newroute_msg_encoder + : netlink_delroute_msg_encoder, + false); } /** @@ -2518,6 +2516,15 @@ int netlink_nexthop_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) /* We use the ID key'd nhg table for kernel updates */ id = *((uint32_t *)RTA_DATA(tb[NHA_ID])); + if (zebra_evpn_mh_is_fdb_nh(id)) { + /* If this is a L2 NH just ignore it */ + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) { + zlog_debug("Ignore kernel update (%u) for fdb-nh 0x%x", + h->nlmsg_type, id); + } + return 0; + } + family = nhm->nh_family; afi = family2afi(family); @@ -2673,7 +2680,9 @@ int kernel_neigh_update(int add, int ifindex, uint32_t addr, char *lla, static ssize_t netlink_neigh_update_msg_encode( const struct zebra_dplane_ctx *ctx, int cmd, const struct ethaddr *mac, const struct ipaddr *ip, bool replace_obj, uint8_t family, uint8_t type, - uint8_t flags, uint16_t state, void *data, size_t datalen) + uint8_t flags, uint16_t state, uint32_t nhg_id, + bool nfy, uint8_t nfy_flags, + void *data, size_t datalen) { uint8_t protocol = RTPROT_ZEBRA; struct { @@ -2686,7 +2695,7 @@ static ssize_t netlink_neigh_update_msg_encode( if (datalen < sizeof(*req)) return 0; - memset(req, 0, datalen); + memset(req, 0, sizeof(*req)); op = dplane_ctx_get_op(ctx); @@ -2703,7 +2712,7 @@ static ssize_t netlink_neigh_update_msg_encode( req->ndm.ndm_flags = flags; req->ndm.ndm_ifindex = dplane_ctx_get_ifindex(ctx); - if (!nl_attr_put(&req->n, sizeof(req), NDA_PROTOCOL, &protocol, + if (!nl_attr_put(&req->n, datalen, NDA_PROTOCOL, &protocol, sizeof(protocol))) return 0; @@ -2712,6 +2721,16 @@ static ssize_t netlink_neigh_update_msg_encode( return 0; } + if (nhg_id) { + if (!nl_attr_put32(&req->n, datalen, NDA_NH_ID, nhg_id)) + return 0; + } + if (nfy) { + if (!nl_attr_put(&req->n, datalen, NDA_NOTIFY, + &nfy_flags, sizeof(nfy_flags))) + return 0; + } + ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN; if (!nl_attr_put(&req->n, datalen, NDA_DST, &ip->ip.addr, ipa_len)) return 0; @@ -2736,22 +2755,16 @@ static ssize_t netlink_neigh_update_msg_encode( * Add remote VTEP to the flood list for this VxLAN interface (VNI). This * is done by adding an FDB entry with a MAC of 00:00:00:00:00:00. */ -static int netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, - int cmd) +static ssize_t +netlink_vxlan_flood_update_ctx(const struct zebra_dplane_ctx *ctx, int cmd, + void *buf, size_t buflen) { struct ethaddr dst_mac = {.octet = {0}}; - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; - if (netlink_neigh_update_msg_encode( - ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false, - PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), nl_pkt, - sizeof(nl_pkt)) - <= 0) - return -1; - - return netlink_talk_info(netlink_talk_filter, - (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); + return netlink_neigh_update_msg_encode( + ctx, cmd, &dst_mac, dplane_ctx_neigh_get_ipaddr(ctx), false, + PF_BRIDGE, 0, NTF_SELF, (NUD_NOARP | NUD_PERMANENT), 0 /*nhg*/, + false /*nfy*/, 0 /*nfy_flags*/, buf, buflen); } #ifndef NDA_RTA @@ -2774,6 +2787,9 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) char vid_buf[20]; char dst_buf[30]; bool sticky; + bool local_inactive = false; + bool dp_static = false; + uint32_t nhg_id = 0; ndm = NLMSG_DATA(h); @@ -2821,13 +2837,29 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) inet_ntoa(vtep_ip)); } + if (tb[NDA_NH_ID]) + nhg_id = *(uint32_t *)RTA_DATA(tb[NDA_NH_ID]); + + if (ndm->ndm_state & NUD_STALE) + local_inactive = true; + + if (tb[NDA_NOTIFY]) { + uint8_t nfy_flags; + + dp_static = true; + nfy_flags = *(uint8_t *)RTA_DATA(tb[NDA_NOTIFY]); + /* local activity has not been detected on the entry */ + if (nfy_flags & (1 << BR_FDB_NFY_INACTIVE)) + local_inactive = true; + } + if (IS_ZEBRA_DEBUG_KERNEL) - zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %s%s", + zlog_debug("Rx %s AF_BRIDGE IF %u%s st 0x%x fl 0x%x MAC %s%s nhg %d", nl_msg_type_to_str(h->nlmsg_type), ndm->ndm_ifindex, vid_present ? vid_buf : "", ndm->ndm_state, ndm->ndm_flags, prefix_mac2str(&mac, buf, sizeof(buf)), - dst_present ? dst_buf : ""); + dst_present ? dst_buf : "", nhg_id); /* The interface should exist. */ ifp = if_lookup_by_index_per_ns(zebra_ns_lookup(ns_id), @@ -2850,7 +2882,7 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) return 0; } - sticky = !!(ndm->ndm_state & NUD_NOARP); + sticky = !!(ndm->ndm_flags & NTF_STICKY); if (filter_vlan && vid != filter_vlan) { if (IS_ZEBRA_DEBUG_KERNEL) @@ -2878,7 +2910,7 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) vid); return zebra_vxlan_local_mac_add_update(ifp, br_if, &mac, vid, - sticky); + sticky, local_inactive, dp_static); } /* This is a delete notification. @@ -2891,6 +2923,9 @@ static int netlink_macfdb_change(struct nlmsghdr *h, int len, ns_id_t ns_id) * Note: We will get notifications from both bridge driver and VxLAN * driver. */ + if (nhg_id) + return 0; + if (dst_present) { u_char zero_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; @@ -3078,9 +3113,8 @@ int netlink_macfdb_read_specific_mac(struct zebra_ns *zns, /* * Netlink-specific handler for MAC updates using dataplane context object. */ -ssize_t -netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data, - size_t datalen) +ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, void *data, + size_t datalen) { struct ipaddr vtep_ip; vlanid_t vid; @@ -3088,18 +3122,43 @@ netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data, int cmd; uint8_t flags; uint16_t state; + uint32_t nhg_id; + uint32_t update_flags; + bool nfy = false; + uint8_t nfy_flags = 0; cmd = dplane_ctx_get_op(ctx) == DPLANE_OP_MAC_INSTALL ? RTM_NEWNEIGH : RTM_DELNEIGH; - flags = (NTF_SELF | NTF_MASTER); + flags = NTF_MASTER; state = NUD_REACHABLE; - if (dplane_ctx_mac_is_sticky(ctx)) - state |= NUD_NOARP; - else - flags |= NTF_EXT_LEARNED; + update_flags = dplane_ctx_mac_get_update_flags(ctx); + if (update_flags & DPLANE_MAC_REMOTE) { + flags |= NTF_SELF; + if (dplane_ctx_mac_is_sticky(ctx)) + flags |= NTF_STICKY; + else + flags |= NTF_EXT_LEARNED; + /* if it was static-local previously we need to clear the + * notify flags on replace with remote + */ + if (update_flags & DPLANE_MAC_WAS_STATIC) + nfy = true; + } else { + /* local mac */ + if (update_flags & DPLANE_MAC_SET_STATIC) { + nfy_flags |= (1 << BR_FDB_NFY_STATIC); + state |= NUD_NOARP; + } + if (update_flags & DPLANE_MAC_SET_INACTIVE) + nfy_flags |= (1 << BR_FDB_NFY_INACTIVE); + + nfy = true; + } + + nhg_id = dplane_ctx_mac_get_nhg_id(ctx); vtep_ip.ipaddr_v4 = *(dplane_ctx_mac_get_vtep_ip(ctx)); SET_IPADDR_V4(&vtep_ip); @@ -3107,6 +3166,7 @@ netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data, char ipbuf[PREFIX_STRLEN]; char buf[ETHER_ADDR_STRLEN]; char vid_buf[20]; + const struct ethaddr *mac = dplane_ctx_mac_get_addr(ctx); vid = dplane_ctx_mac_get_vlan(ctx); if (vid > 0) @@ -3114,20 +3174,30 @@ netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, uint8_t *data, else vid_buf[0] = '\0'; - const struct ethaddr *mac = dplane_ctx_mac_get_addr(ctx); - - zlog_debug("Tx %s family %s IF %s(%u)%s %sMAC %s dst %s", + zlog_debug("Tx %s family %s IF %s(%u)%s %sMAC %s dst %s nhg %u%s%s%s%s%s", nl_msg_type_to_str(cmd), nl_family_to_str(AF_BRIDGE), dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx), vid_buf, dplane_ctx_mac_is_sticky(ctx) ? "sticky " : "", prefix_mac2str(mac, buf, sizeof(buf)), - ipaddr2str(&vtep_ip, ipbuf, sizeof(ipbuf))); + ipaddr2str(&vtep_ip, ipbuf, sizeof(ipbuf)), + nhg_id, + (update_flags & + DPLANE_MAC_REMOTE) ? " rem" : "", + (update_flags & + DPLANE_MAC_WAS_STATIC) ? " clr_sync" : "", + (update_flags & + DPLANE_MAC_SET_STATIC) ? " static" : "", + (update_flags & + DPLANE_MAC_SET_INACTIVE) ? " inactive" : "", + (nfy & + DPLANE_MAC_SET_INACTIVE) ? " nfy" : ""); } total = netlink_neigh_update_msg_encode( ctx, cmd, dplane_ctx_mac_get_addr(ctx), &vtep_ip, true, - AF_BRIDGE, 0, flags, state, data, datalen); + AF_BRIDGE, 0, flags, state, nhg_id, nfy, nfy_flags, + data, datalen); return total; } @@ -3161,6 +3231,8 @@ static void netlink_handle_5549(struct ndmsg *ndm, struct zebra_if *zif, #define NUD_VALID \ (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | NUD_PROBE | NUD_STALE \ | NUD_DELAY) +#define NUD_LOCAL_ACTIVE \ + (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE) static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) { @@ -3177,6 +3249,7 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) int mac_present = 0; bool is_ext; bool is_router; + bool local_inactive; ndm = NLMSG_DATA(h); @@ -3286,10 +3359,17 @@ static int netlink_ipneigh_change(struct nlmsghdr *h, int len, ns_id_t ns_id) * result * in re-adding the neighbor if it is a valid "remote" neighbor. */ - if (ndm->ndm_state & NUD_VALID) + if (ndm->ndm_state & NUD_VALID) { + local_inactive = !(ndm->ndm_state & NUD_LOCAL_ACTIVE); + + /* XXX - populate dp-static based on the sync flags + * in the kernel + */ return zebra_vxlan_handle_kernel_neigh_update( ifp, link_if, &ip, &mac, ndm->ndm_state, - is_ext, is_router); + is_ext, is_router, local_inactive, + false /* dp_static */); + } return zebra_vxlan_handle_kernel_neigh_del(ifp, link_if, &ip); } @@ -3510,15 +3590,14 @@ int netlink_neigh_change(struct nlmsghdr *h, ns_id_t ns_id) /* * Utility neighbor-update function, using info from dplane context. */ -static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, - int cmd) +static ssize_t netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, + int cmd, void *buf, size_t buflen) { const struct ipaddr *ip; const struct ethaddr *mac; uint8_t flags; uint16_t state; uint8_t family; - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; ip = dplane_ctx_neigh_get_ipaddr(ctx); mac = dplane_ctx_neigh_get_mac(ctx); @@ -3543,59 +3622,56 @@ static int netlink_neigh_update_ctx(const struct zebra_dplane_ctx *ctx, flags, state); } - if (netlink_neigh_update_msg_encode(ctx, cmd, mac, ip, true, family, - RTN_UNICAST, flags, state, nl_pkt, - sizeof(nl_pkt)) - <= 0) - return -1; - - return netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); + return netlink_neigh_update_msg_encode( + ctx, cmd, mac, ip, true, family, RTN_UNICAST, flags, state, + 0 /*nhg*/, false /*nfy*/, 0 /*nfy_flags*/, buf, buflen); } -/* - * Update MAC, using dataplane context object. - */ -enum zebra_dplane_result kernel_mac_update_ctx(struct zebra_dplane_ctx *ctx) +static ssize_t netlink_neigh_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) { - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; - ssize_t rv; - - rv = netlink_macfdb_update_ctx(ctx, nl_pkt, sizeof(nl_pkt)); - if (rv <= 0) - return ZEBRA_DPLANE_REQUEST_FAILURE; - - rv = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); - - return rv == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE; -} - -enum zebra_dplane_result kernel_neigh_update_ctx(struct zebra_dplane_ctx *ctx) -{ - int ret = -1; + ssize_t ret; switch (dplane_ctx_get_op(ctx)) { case DPLANE_OP_NEIGH_INSTALL: case DPLANE_OP_NEIGH_UPDATE: - ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH); + case DPLANE_OP_NEIGH_DISCOVER: + ret = netlink_neigh_update_ctx(ctx, RTM_NEWNEIGH, buf, buflen); break; case DPLANE_OP_NEIGH_DELETE: - ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH); + ret = netlink_neigh_update_ctx(ctx, RTM_DELNEIGH, buf, buflen); break; case DPLANE_OP_VTEP_ADD: - ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH); + ret = netlink_vxlan_flood_update_ctx(ctx, RTM_NEWNEIGH, buf, + buflen); break; case DPLANE_OP_VTEP_DELETE: - ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH); + ret = netlink_vxlan_flood_update_ctx(ctx, RTM_DELNEIGH, buf, + buflen); break; default: - break; + ret = -1; } - return (ret == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); + return ret; +} + +/* + * Update MAC, using dataplane context object. + */ + +enum netlink_msg_status netlink_put_mac_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + return netlink_batch_add_msg(bth, ctx, netlink_macfdb_update_ctx, + false); +} + +enum netlink_msg_status +netlink_put_neigh_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) +{ + return netlink_batch_add_msg(bth, ctx, netlink_neigh_msg_encoder, + false); } /* @@ -3754,4 +3830,172 @@ ssize_t netlink_mpls_multipath_msg_encode(int cmd, struct zebra_dplane_ctx *ctx, return NLMSG_ALIGN(req->n.nlmsg_len); } + +/**************************************************************************** +* This code was developed in a branch that didn't have dplane APIs for +* MAC updates. Hence the use of the legacy style. It will be moved to +* the new dplane style pre-merge to master. XXX +*/ +static int netlink_fdb_nh_update(uint32_t nh_id, struct in_addr vtep_ip) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[256]; + } req; + int cmd = RTM_NEWNEXTHOP; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return -1; + zns = zvrf->zns; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE); + req.n.nlmsg_type = cmd; + req.nhm.nh_family = AF_INET; + + if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nh_id)) + return -1; + if (!nl_attr_put(&req.n, sizeof(req), NHA_FDB, NULL, 0)) + return -1; + if (!nl_attr_put(&req.n, sizeof(req), NHA_GATEWAY, + &vtep_ip, IPV4_MAX_BYTELEN)) + return -1; + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) { + zlog_debug("Tx %s fdb-nh 0x%x %s", + nl_msg_type_to_str(cmd), nh_id, inet_ntoa(vtep_ip)); + } + + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, + 0); +} + +static int netlink_fdb_nh_del(uint32_t nh_id) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[256]; + } req; + int cmd = RTM_DELNEXTHOP; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return -1; + zns = zvrf->zns; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = cmd; + req.nhm.nh_family = AF_UNSPEC; + + if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nh_id)) + return -1; + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) { + zlog_debug("Tx %s fdb-nh 0x%x", + nl_msg_type_to_str(cmd), nh_id); + } + + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, + 0); +} + +static int netlink_fdb_nhg_update(uint32_t nhg_id, uint32_t nh_cnt, + struct nh_grp *nh_ids) +{ + struct { + struct nlmsghdr n; + struct nhmsg nhm; + char buf[256]; + } req; + int cmd = RTM_NEWNEXTHOP; + struct zebra_vrf *zvrf; + struct zebra_ns *zns; + struct nexthop_grp grp[nh_cnt]; + uint32_t i; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return -1; + zns = zvrf->zns; + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct nhmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE); + req.n.nlmsg_type = cmd; + req.nhm.nh_family = AF_UNSPEC; + + if (!nl_attr_put32(&req.n, sizeof(req), NHA_ID, nhg_id)) + return -1; + if (!nl_attr_put(&req.n, sizeof(req), NHA_FDB, NULL, 0)) + return -1; + memset(&grp, 0, sizeof(grp)); + for (i = 0; i < nh_cnt; ++i) { + grp[i].id = nh_ids[i].id; + grp[i].weight = nh_ids[i].weight; + } + if (!nl_attr_put(&req.n, sizeof(req), NHA_GROUP, + grp, nh_cnt * sizeof(struct nexthop_grp))) + return -1; + + + if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_EVPN_MH_NH) { + char vtep_str[ES_VTEP_LIST_STR_SZ]; + char nh_buf[16]; + + vtep_str[0] = '\0'; + for (i = 0; i < nh_cnt; ++i) { + snprintf(nh_buf, sizeof(nh_buf), "%u ", + grp[i].id); + strlcat(vtep_str, nh_buf, sizeof(vtep_str)); + } + + zlog_debug("Tx %s fdb-nhg 0x%x %s", + nl_msg_type_to_str(cmd), nhg_id, vtep_str); + } + + return netlink_talk(netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, + 0); +} + +static int netlink_fdb_nhg_del(uint32_t nhg_id) +{ + return netlink_fdb_nh_del(nhg_id); +} + +int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip) +{ + return netlink_fdb_nh_update(nh_id, vtep_ip); +} + +int kernel_del_mac_nh(uint32_t nh_id) +{ + return netlink_fdb_nh_del(nh_id); +} + +int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt, + struct nh_grp *nh_ids) +{ + return netlink_fdb_nhg_update(nhg_id, nh_cnt, nh_ids); +} + +int kernel_del_mac_nhg(uint32_t nhg_id) +{ + return netlink_fdb_nhg_del(nhg_id); +} + #endif /* HAVE_NETLINK */ diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h index 429ff0bf85..e1bb844785 100644 --- a/zebra/rt_netlink.h +++ b/zebra/rt_netlink.h @@ -60,6 +60,7 @@ extern "C" { #define RTPROT_PBR 195 #define RTPROT_ZSTATIC 196 #define RTPROT_OPENFABRIC 197 +#define RTPROT_SRTE 198 void rt_netlink_init(void); @@ -73,7 +74,7 @@ extern ssize_t netlink_route_multipath_msg_encode(int cmd, uint8_t *data, size_t datalen, bool fpm, bool force_nhg); extern ssize_t netlink_macfdb_update_ctx(struct zebra_dplane_ctx *ctx, - uint8_t *data, size_t datalen); + void *data, size_t datalen); extern int netlink_route_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); extern int netlink_route_read(struct zebra_ns *zns); @@ -100,6 +101,23 @@ extern int netlink_neigh_read_specific_ip(struct ipaddr *ip, struct interface *vlan_if); extern vrf_id_t vrf_lookup_by_table(uint32_t table_id, ns_id_t ns_id); +struct nl_batch; +extern enum netlink_msg_status +netlink_put_route_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_nexthop_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_mac_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_neigh_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_lsp_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); +extern enum netlink_msg_status +netlink_put_pw_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); + #ifdef __cplusplus } #endif diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 64fd7fa491..a2e15cbd2b 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -50,8 +50,7 @@ static int kernel_rtm_add_labels(struct mpls_label_stack *nh_label, { if (nh_label->num_labels > 1) { flog_warn(EC_ZEBRA_MAX_LABELS_PUSH, - "%s: can't push %u labels at " - "once (maximum is 1)", + "%s: can't push %u labels at once (maximum is 1)", __func__, nh_label->num_labels); return -1; } @@ -359,20 +358,6 @@ enum zebra_dplane_result kernel_route_update(struct zebra_dplane_ctx *ctx) } } /* Elevated privs */ - if (RSYSTEM_ROUTE(type) - && dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) { - struct nexthop *nexthop; - - for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) - continue; - - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - } - } - } - return res; } @@ -418,4 +403,25 @@ uint32_t kernel_get_speed(struct interface *ifp, int *error) return ifp->speed; } +int kernel_upd_mac_nh(uint32_t nh_id, struct in_addr vtep_ip) +{ + return 0; +} + +int kernel_del_mac_nh(uint32_t nh_id) +{ + return 0; +} + +int kernel_upd_mac_nhg(uint32_t nhg_id, uint32_t nh_cnt, + struct nh_grp *nh_ids) +{ + return 0; +} + +int kernel_del_mac_nhg(uint32_t nhg_id) +{ + return 0; +} + #endif /* !HAVE_NETLINK */ diff --git a/zebra/rtadv.c b/zebra/rtadv.c index fa0a682d07..13b7c150a3 100644 --- a/zebra/rtadv.c +++ b/zebra/rtadv.c @@ -2282,13 +2282,11 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp) interval = rtadv->MaxRtrAdvInterval; if (interval % 1000) vty_out(vty, - " ND router advertisements are sent every " - "%d milliseconds\n", + " ND router advertisements are sent every %d milliseconds\n", interval); else vty_out(vty, - " ND router advertisements are sent every " - "%d seconds\n", + " ND router advertisements are sent every %d seconds\n", interval / 1000); if (!rtadv->UseFastRexmit) vty_out(vty, @@ -2302,8 +2300,7 @@ static int nd_dump_vty(struct vty *vty, struct interface *ifp) vty_out(vty, " ND router advertisements lifetime tracks ra-interval\n"); vty_out(vty, - " ND router advertisement default router preference is " - "%s\n", + " ND router advertisement default router preference is %s\n", rtadv_pref_strs[rtadv->DefaultPreference]); if (rtadv->AdvManagedFlag) vty_out(vty, diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c index 28529cc138..0a1fb82d95 100644 --- a/zebra/rtread_getmsg.c +++ b/zebra/rtread_getmsg.c @@ -230,8 +230,7 @@ void route_read(struct zebra_ns *zns) if (msgdata.len % sizeof(mib2_ipRouteEntry_t) != 0) { zlog_debug( - "getmsg(data) returned " - "msgdata.len = %d (%% sizeof(mib2_ipRouteEntry_t) != 0)", + "getmsg(data) returned msgdata.len = %d (%% sizeof(mib2_ipRouteEntry_t) != 0)", msgdata.len); goto exit; } diff --git a/zebra/rule_netlink.c b/zebra/rule_netlink.c index b7be398506..3a3baab4ca 100644 --- a/zebra/rule_netlink.c +++ b/zebra/rule_netlink.c @@ -58,10 +58,12 @@ * Returns -1 on failure, 0 when the msg doesn't fit entirely in the buffer * or the number of bytes written to buf. */ -static ssize_t netlink_rule_msg_encode( - int cmd, const struct zebra_dplane_ctx *ctx, uint32_t filter_bm, - uint32_t priority, uint32_t table, const struct prefix *src_ip, - const struct prefix *dst_ip, uint32_t fwmark, void *buf, size_t buflen) +static ssize_t +netlink_rule_msg_encode(int cmd, const struct zebra_dplane_ctx *ctx, + uint32_t filter_bm, uint32_t priority, uint32_t table, + const struct prefix *src_ip, + const struct prefix *dst_ip, uint32_t fwmark, + uint8_t dsfield, void *buf, size_t buflen) { uint8_t protocol = RTPROT_ZEBRA; int family; @@ -76,6 +78,8 @@ static ssize_t netlink_rule_msg_encode( char buf1[PREFIX_STRLEN]; char buf2[PREFIX_STRLEN]; + if (buflen < sizeof(*req)) + return 0; memset(req, 0, sizeof(*req)); family = PREFIX_FAMILY(src_ip); bytelen = (family == AF_INET ? 4 : 16); @@ -122,6 +126,10 @@ static ssize_t netlink_rule_msg_encode( return 0; } + /* dsfield, if specified */ + if (filter_bm & PBR_FILTER_DSFIELD) + req->frh.tos = dsfield; + /* Route table to use to forward, if filter criteria matches. */ if (table < 256) req->frh.table = table; @@ -142,53 +150,55 @@ static ssize_t netlink_rule_msg_encode( return NLMSG_ALIGN(req->n.nlmsg_len); } -/* Install or uninstall specified rule for a specific interface. - * Form netlink message and ship it. - */ -static int -netlink_rule_update_internal(int cmd, const struct zebra_dplane_ctx *ctx, - uint32_t filter_bm, uint32_t priority, - uint32_t table, const struct prefix *src_ip, - const struct prefix *dst_ip, uint32_t fwmark) +static ssize_t netlink_rule_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, + size_t buflen) { - char buf[NL_PKT_BUF_SIZE]; + int cmd = RTM_NEWRULE; + + if (dplane_ctx_get_op(ctx) == DPLANE_OP_RULE_DELETE) + cmd = RTM_DELRULE; + + return netlink_rule_msg_encode( + cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx), + dplane_ctx_rule_get_priority(ctx), + dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx), + dplane_ctx_rule_get_dst_ip(ctx), + dplane_ctx_rule_get_fwmark(ctx), + dplane_ctx_rule_get_dsfield(ctx), buf, buflen); +} - netlink_rule_msg_encode(cmd, ctx, filter_bm, priority, table, src_ip, - dst_ip, fwmark, buf, sizeof(buf)); - return netlink_talk_info(netlink_talk_filter, (void *)&buf, - dplane_ctx_get_ns(ctx), 0); +static ssize_t netlink_oldrule_msg_encoder(struct zebra_dplane_ctx *ctx, + void *buf, size_t buflen) +{ + return netlink_rule_msg_encode( + RTM_DELRULE, ctx, dplane_ctx_rule_get_old_filter_bm(ctx), + dplane_ctx_rule_get_old_priority(ctx), + dplane_ctx_rule_get_old_table(ctx), + dplane_ctx_rule_get_old_src_ip(ctx), + dplane_ctx_rule_get_old_dst_ip(ctx), + dplane_ctx_rule_get_old_fwmark(ctx), + dplane_ctx_rule_get_old_dsfield(ctx), buf, buflen); } + /* Public functions */ -/* - * Add, update or delete a rule from the - * kernel, using info from a dataplane context. - */ -enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx) +enum netlink_msg_status +netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx) { enum dplane_op_e op; - int cmd; - int ret; + enum netlink_msg_status ret; op = dplane_ctx_get_op(ctx); - if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE) - cmd = RTM_NEWRULE; - else if (op == DPLANE_OP_RULE_DELETE) - cmd = RTM_DELRULE; - else { + if (!(op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE + || op == DPLANE_OP_RULE_DELETE)) { flog_err( EC_ZEBRA_PBR_RULE_UPDATE, "Context received for kernel rule update with incorrect OP code (%u)", op); - return ZEBRA_DPLANE_REQUEST_FAILURE; + return FRR_NETLINK_ERROR; } - ret = netlink_rule_update_internal( - cmd, ctx, dplane_ctx_rule_get_filter_bm(ctx), - dplane_ctx_rule_get_priority(ctx), - dplane_ctx_rule_get_table(ctx), dplane_ctx_rule_get_src_ip(ctx), - dplane_ctx_rule_get_dst_ip(ctx), - dplane_ctx_rule_get_fwmark(ctx)); + ret = netlink_batch_add_msg(bth, ctx, netlink_rule_msg_encoder, false); /** * Delete the old one. @@ -196,18 +206,10 @@ enum zebra_dplane_result kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx) * Don't care about this result right? */ if (op == DPLANE_OP_RULE_UPDATE) - netlink_rule_update_internal( - RTM_DELRULE, ctx, - dplane_ctx_rule_get_old_filter_bm(ctx), - dplane_ctx_rule_get_old_priority(ctx), - dplane_ctx_rule_get_old_table(ctx), - dplane_ctx_rule_get_old_src_ip(ctx), - dplane_ctx_rule_get_old_dst_ip(ctx), - dplane_ctx_rule_get_old_fwmark(ctx)); - - - return (ret == 0 ? ZEBRA_DPLANE_REQUEST_SUCCESS - : ZEBRA_DPLANE_REQUEST_FAILURE); + netlink_batch_add_msg(bth, ctx, netlink_oldrule_msg_encoder, + true); + + return ret; } /* @@ -247,7 +249,16 @@ int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) } frh = NLMSG_DATA(h); + if (frh->family != AF_INET && frh->family != AF_INET6) { + if (frh->family == RTNL_FAMILY_IPMR + || frh->family == RTNL_FAMILY_IP6MR) { + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug( + "Received rule netlink that we are ignoring for family %u, rule change: %u", + frh->family, h->nlmsg_type); + return 0; + } flog_warn( EC_ZEBRA_NETLINK_INVALID_AF, "Invalid address family: %u received from kernel rule change: %u", diff --git a/zebra/rule_netlink.h b/zebra/rule_netlink.h index 8c4741dc06..cf4d978e78 100644 --- a/zebra/rule_netlink.h +++ b/zebra/rule_netlink.h @@ -40,6 +40,9 @@ extern int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup); */ extern int netlink_rules_read(struct zebra_ns *zns); +extern enum netlink_msg_status +netlink_put_rule_update_msg(struct nl_batch *bth, struct zebra_dplane_ctx *ctx); + #ifdef __cplusplus } #endif diff --git a/zebra/subdir.am b/zebra/subdir.am index 49e60820bc..a6ef1537c0 100644 --- a/zebra/subdir.am +++ b/zebra/subdir.am @@ -12,6 +12,7 @@ vtysh_scan += \ zebra/rtadv.c \ zebra/zebra_gr.c \ zebra/zebra_mlag_vty.c \ + zebra/zebra_evpn_mh.c \ zebra/zebra_mpls_vty.c \ zebra/zebra_ptm.c \ zebra/zebra_pw.c \ @@ -79,6 +80,9 @@ zebra_zebra_SOURCES = \ zebra/zebra_errors.c \ zebra/zebra_gr.c \ zebra/zebra_l2.c \ + zebra/zebra_evpn.c \ + zebra/zebra_evpn_mac.c \ + zebra/zebra_evpn_neigh.c \ zebra/zebra_mlag.c \ zebra/zebra_mlag_vty.c \ zebra/zebra_memory.c \ @@ -105,9 +109,11 @@ zebra_zebra_SOURCES = \ zebra/zebra_router.c \ zebra/zebra_rnh.c \ zebra/zebra_routemap.c \ + zebra/zebra_srte.c \ zebra/zebra_vrf.c \ zebra/zebra_vty.c \ zebra/zebra_vxlan.c \ + zebra/zebra_evpn_mh.c \ zebra/zserv.c \ # end @@ -115,6 +121,7 @@ clippy_scan += \ zebra/debug.c \ zebra/interface.c \ zebra/rtadv.c \ + zebra/zebra_evpn_mh.c \ zebra/zebra_mlag_vty.c \ zebra/zebra_routemap.c \ zebra/zebra_vty.c \ @@ -143,6 +150,10 @@ noinst_HEADERS += \ zebra/zapi_msg.h \ zebra/zebra_dplane.h \ zebra/zebra_errors.h \ + zebra/zebra_evpn.h \ + zebra/zebra_evpn_mac.h \ + zebra/zebra_evpn_neigh.h \ + zebra/zebra_evpn_vxlan.h \ zebra/zebra_fpm_private.h \ zebra/zebra_l2.h \ zebra/zebra_memory.h \ @@ -164,9 +175,11 @@ noinst_HEADERS += \ zebra/zebra_rnh.h \ zebra/zebra_routemap.h \ zebra/zebra_router.h \ + zebra/zebra_srte.h \ zebra/zebra_vrf.h \ zebra/zebra_vxlan.h \ zebra/zebra_vxlan_private.h \ + zebra/zebra_evpn_mh.h \ zebra/zserv.h \ # end diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index dc7c595d26..285f28ccec 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -51,6 +51,7 @@ #include "zebra/zebra_mpls.h" #include "zebra/zebra_mroute.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" #include "zebra/rt.h" #include "zebra/zebra_pbr.h" #include "zebra/table_manager.h" @@ -59,6 +60,7 @@ #include "zebra/zebra_mlag.h" #include "zebra/connected.h" #include "zebra/zebra_opaque.h" +#include "zebra/zebra_srte.h" /* Encoding helpers -------------------------------------------------------- */ @@ -729,9 +731,10 @@ static int route_notify_internal(const struct prefix *p, int type, char buff[PREFIX_STRLEN]; zlog_debug( - "Not Notifying Owner: %u about prefix %s(%u) %d vrf: %u", - type, prefix2str(p, buff, sizeof(buff)), - table_id, note, vrf_id); + "Not Notifying Owner: %s about prefix %s(%u) %d vrf: %u", + zebra_route_string(type), + prefix2str(p, buff, sizeof(buff)), table_id, + note, vrf_id); } return 0; } @@ -739,9 +742,10 @@ static int route_notify_internal(const struct prefix *p, int type, if (IS_ZEBRA_DEBUG_PACKET) { char buff[PREFIX_STRLEN]; - zlog_debug("Notifying Owner: %u about prefix %s(%u) %d vrf: %u", - type, prefix2str(p, buff, sizeof(buff)), - table_id, note, vrf_id); + zlog_debug("Notifying Owner: %s about prefix %s(%u) %d vrf: %u", + zebra_route_string(type), + prefix2str(p, buff, sizeof(buff)), table_id, note, + vrf_id); } s = stream_new(ZEBRA_MAX_PACKET_SIZ); @@ -904,17 +908,18 @@ void zsend_iptable_notify_owner(struct zebra_pbr_iptable *iptable, zserv_send_message(client, s); } -/* Router-id is updated. Send ZEBRA_ROUTER_ID_ADD to client. */ -int zsend_router_id_update(struct zserv *client, struct prefix *p, +/* Router-id is updated. Send ZEBRA_ROUTER_ID_UPDATE to client. */ +int zsend_router_id_update(struct zserv *client, afi_t afi, struct prefix *p, vrf_id_t vrf_id) { int blen; + struct stream *s; /* Check this client need interface information. */ - if (!vrf_bitmap_check(client->ridinfo, vrf_id)) + if (!vrf_bitmap_check(client->ridinfo[afi], vrf_id)) return 0; - struct stream *s = stream_new(ZEBRA_MAX_PACKET_SIZ); + s = stream_new(ZEBRA_MAX_PACKET_SIZ); /* Message type. */ zclient_create_header(s, ZEBRA_ROUTER_ID_UPDATE, vrf_id); @@ -1073,7 +1078,8 @@ static void zread_rnh_register(ZAPI_HANDLER_ARGS) s = msg; - client->nh_reg_time = monotime(NULL); + if (!client->nh_reg_time) + client->nh_reg_time = monotime(NULL); while (l < hdr->length) { STREAM_GETC(s, flags); @@ -1416,6 +1422,7 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re, struct nexthop *nexthop = NULL; struct ipaddr vtep_ip; struct interface *ifp; + int i; char nhbuf[INET6_ADDRSTRLEN] = ""; switch (api_nh->type) { @@ -1521,17 +1528,36 @@ static struct nexthop *nexthop_from_zapi(struct route_entry *re, nexthop->weight = api_nh->weight; if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { - if (api_nh->backup_idx < api->backup_nexthop_num) { - /* Capture backup info */ - SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); - nexthop->backup_idx = api_nh->backup_idx; - } else { - /* Warn about invalid backup index */ + /* Validate count */ + if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) { if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) - zlog_debug("%s: invalid backup nh idx %d", - __func__, api_nh->backup_idx); + zlog_debug("%s: invalid backup nh count %d", + __func__, api_nh->backup_num); + nexthop_free(nexthop); + nexthop = NULL; + goto done; + } + + /* Copy backup info */ + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nexthop->backup_num = api_nh->backup_num; + + for (i = 0; i < api_nh->backup_num; i++) { + /* Validate backup index */ + if (api_nh->backup_idx[i] < api->backup_nexthop_num) { + nexthop->backup_idx[i] = api_nh->backup_idx[i]; + } else { + if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT) + zlog_debug("%s: invalid backup nh idx %d", + __func__, + api_nh->backup_idx[i]); + nexthop_free(nexthop); + nexthop = NULL; + goto done; + } } } + done: return nexthop; } @@ -1627,6 +1653,11 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) return; } + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); + nexthop->srte_color = api_nh->srte_color; + } + /* MPLS labels for BGP-LU or Segment Routing */ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL) && api_nh->type != NEXTHOP_TYPE_IFINDEX @@ -1703,7 +1734,12 @@ static void zread_route_add(ZAPI_HANDLER_ARGS) __func__, nhbuf); } UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); - nexthop->backup_idx = 0; + nexthop->backup_num = 0; + } + + if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRTE)) { + SET_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE); + nexthop->srte_color = api_nh->srte_color; } /* MPLS labels for BGP-LU or Segment Routing */ @@ -1874,20 +1910,48 @@ stream_failure: /* Register zebra server router-id information. Send current router-id */ static void zread_router_id_add(ZAPI_HANDLER_ARGS) { + afi_t afi; + struct prefix p; + STREAM_GETW(msg, afi); + + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while registering for router ID notifications", + afi); + goto stream_failure; + } + /* Router-id information is needed. */ - vrf_bitmap_set(client->ridinfo, zvrf_id(zvrf)); + vrf_bitmap_set(client->ridinfo[afi], zvrf_id(zvrf)); + + router_id_get(afi, &p, zvrf); - router_id_get(&p, zvrf_id(zvrf)); + zsend_router_id_update(client, afi, &p, zvrf_id(zvrf)); - zsend_router_id_update(client, &p, zvrf_id(zvrf)); +stream_failure: + return; } /* Unregister zebra server router-id information. */ static void zread_router_id_delete(ZAPI_HANDLER_ARGS) { - vrf_bitmap_unset(client->ridinfo, zvrf_id(zvrf)); + afi_t afi; + + STREAM_GETW(msg, afi); + + if (afi <= AFI_UNSPEC || afi >= AFI_MAX) { + zlog_warn( + "Invalid AFI %u while unregistering from router ID notifications", + afi); + goto stream_failure; + } + + vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf)); + +stream_failure: + return; } static void zsend_capabilities(struct zserv *client, struct zebra_vrf *zvrf) @@ -1976,8 +2040,58 @@ static void zread_vrf_unregister(ZAPI_HANDLER_ARGS) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) vrf_bitmap_unset(client->redist[afi][i], zvrf_id(zvrf)); vrf_bitmap_unset(client->redist_default[afi], zvrf_id(zvrf)); + vrf_bitmap_unset(client->ridinfo[afi], zvrf_id(zvrf)); + } +} + +/* + * Validate incoming zapi mpls lsp / labels message + */ +static int zapi_labels_validate(const struct zapi_labels *zl) +{ + int ret = -1; + int i, j, idx; + uint32_t bits[8]; + uint32_t ival; + const struct zapi_nexthop *znh; + + /* Validate backup info: no duplicates for a single primary */ + if (zl->backup_nexthop_num == 0) { + ret = 0; + goto done; + } + + for (j = 0; j < zl->nexthop_num; j++) { + znh = &zl->nexthops[j]; + + memset(bits, 0, sizeof(bits)); + + for (i = 0; i < znh->backup_num; i++) { + idx = znh->backup_idx[i] / 32; + + ival = 1 << znh->backup_idx[i] % 32; + + /* Check whether value is already used */ + if (ival & bits[idx]) { + /* Fail */ + + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: invalid zapi mpls message: duplicate backup nexthop index %d", + __func__, + znh->backup_idx[i]); + goto done; + } + + /* Mark index value */ + bits[idx] |= ival; + } } - vrf_bitmap_unset(client->ridinfo, zvrf_id(zvrf)); + + ret = 0; + +done: + + return ret; } /* @@ -2006,6 +2120,10 @@ static void zread_mpls_labels_add(ZAPI_HANDLER_ARGS) if (!mpls_enabled) return; + /* Validate; will debug on failure */ + if (zapi_labels_validate(&zl) < 0) + return; + ret = mpls_zapi_labels_process(true, zvrf, &zl); if (ret < 0) { if (IS_ZEBRA_DEBUG_RECV) @@ -2087,6 +2205,10 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS) if (!mpls_enabled) return; + /* Validate; will debug on failure */ + if (zapi_labels_validate(&zl) < 0) + return; + /* This removes everything, then re-adds from the client's * zapi message. Since the LSP will be processed later, on this * this same pthread, all of the changes will 'appear' at once. @@ -2099,6 +2221,107 @@ static void zread_mpls_labels_replace(ZAPI_HANDLER_ARGS) mpls_zapi_labels_process(true, zvrf, &zl); } +static void zread_sr_policy_set(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zapi_srte_tunnel *zt; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_sr_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __PRETTY_FUNCTION__); + return; + } + zt = &zp.segment_list; + if (zt->label_num < 1) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug( + "%s: SR-TE tunnel must contain at least one label", + __PRETTY_FUNCTION__); + return; + } + + if (!mpls_enabled) + return; + + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) + policy = zebra_sr_policy_add(zp.color, &zp.endpoint, zp.name); + /* TODO: per-VRF list of SR-TE policies. */ + policy->zvrf = zvrf; + + zebra_sr_policy_validate(policy, &zp.segment_list); +} + +static void zread_sr_policy_delete(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct zapi_sr_policy zp; + struct zebra_sr_policy *policy; + + /* Get input stream. */ + s = msg; + if (zapi_sr_policy_decode(s, &zp) < 0) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to decode zapi_sr_policy sent", + __PRETTY_FUNCTION__); + return; + } + + if (!mpls_enabled) + return; + + policy = zebra_sr_policy_find(zp.color, &zp.endpoint); + if (!policy) { + if (IS_ZEBRA_DEBUG_RECV) + zlog_debug("%s: Unable to find SR-TE policy", + __PRETTY_FUNCTION__); + return; + } + + zebra_sr_policy_del(policy); +} + +int zsend_sr_policy_notify_status(uint32_t color, struct ipaddr *endpoint, + char *name, int status) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_SRTE, 0); + if (!client) { + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Not notifying pathd about policy %s" + " status change to %d", + name, status); + return 0; + } + + if (IS_ZEBRA_DEBUG_PACKET) + zlog_debug( + "Notifying pathd about policy %s status change" + " to %d", + name, status); + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_SR_POLICY_NOTIFY_STATUS, VRF_DEFAULT); + stream_putl(s, color); + stream_put_ipaddr(s, endpoint); + stream_write(s, name, SRTE_POLICY_NAME_MAX_LENGTH); + stream_putl(s, status); + + stream_putw_at(s, 0, stream_get_endp(s)); + + return zserv_send_message(client, s); +} + /* Send response to a table manager connect request to client */ static void zread_table_manager_connect(struct zserv *client, struct stream *msg, vrf_id_t vrf_id) @@ -2526,6 +2749,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) STREAM_GET(&zpr.rule.filter.dst_ip.u.prefix, s, prefix_blen(&zpr.rule.filter.dst_ip)); STREAM_GETW(s, zpr.rule.filter.dst_port); + STREAM_GETC(s, zpr.rule.filter.dsfield); STREAM_GETL(s, zpr.rule.filter.fwmark); STREAM_GETL(s, zpr.rule.action.table); STREAM_GETL(s, zpr.rule.ifindex); @@ -2556,14 +2780,16 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) if (zpr.rule.filter.dst_port) zpr.rule.filter.filter_bm |= PBR_FILTER_DST_PORT; + if (zpr.rule.filter.dsfield) + zpr.rule.filter.filter_bm |= PBR_FILTER_DSFIELD; + if (zpr.rule.filter.fwmark) zpr.rule.filter.filter_bm |= PBR_FILTER_FWMARK; if (!(zpr.rule.filter.src_ip.family == AF_INET || zpr.rule.filter.src_ip.family == AF_INET6)) { zlog_warn( - "Unsupported PBR source IP family: %s (%" PRIu8 - ")", + "Unsupported PBR source IP family: %s (%hhu)", family2str(zpr.rule.filter.src_ip.family), zpr.rule.filter.src_ip.family); return; @@ -2571,8 +2797,7 @@ static inline void zread_rule(ZAPI_HANDLER_ARGS) if (!(zpr.rule.filter.dst_ip.family == AF_INET || zpr.rule.filter.dst_ip.family == AF_INET6)) { zlog_warn( - "Unsupported PBR destination IP family: %s (%" PRIu8 - ")", + "Unsupported PBR destination IP family: %s (%hhu)", family2str(zpr.rule.filter.dst_ip.family), zpr.rule.filter.dst_ip.family); return; @@ -2667,16 +2892,14 @@ static inline void zread_ipset_entry(ZAPI_HANDLER_ARGS) if (!(zpi.dst.family == AF_INET || zpi.dst.family == AF_INET6)) { zlog_warn( - "Unsupported PBR destination IP family: %s (%" PRIu8 - ")", + "Unsupported PBR destination IP family: %s (%hhu)", family2str(zpi.dst.family), zpi.dst.family); goto stream_failure; } if (!(zpi.src.family == AF_INET || zpi.src.family == AF_INET6)) { zlog_warn( - "Unsupported PBR source IP family: %s (%" PRIu8 - ")", + "Unsupported PBR source IP family: %s (%hhu)", family2str(zpi.src.family), zpi.src.family); goto stream_failure; } @@ -2739,6 +2962,41 @@ stream_failure: return; } +static inline void zread_neigh_discover(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + ifindex_t ifindex; + struct interface *ifp; + struct prefix p; + struct ipaddr ip; + + s = msg; + + STREAM_GETL(s, ifindex); + + ifp = if_lookup_by_index_per_ns(zvrf->zns, ifindex); + if (!ifp) { + zlog_debug("Failed to lookup ifindex: %u", ifindex); + return; + } + + STREAM_GETC(s, p.family); + STREAM_GETC(s, p.prefixlen); + STREAM_GET(&p.u.prefix, s, prefix_blen(&p)); + + if (p.family == AF_INET) + SET_IPADDR_V4(&ip); + else + SET_IPADDR_V6(&ip); + + memcpy(&ip.ip.addr, &p.u.prefix, prefix_blen(&p)); + + dplane_neigh_discover(ifp, &ip); + +stream_failure: + return; +} + static void zsend_error_msg(struct zserv *client, enum zebra_error_types error, struct zmsghdr *bad_hdr) { @@ -2798,6 +3056,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_BFD_CLIENT_REGISTER] = zebra_ptm_bfd_client_register, [ZEBRA_INTERFACE_ENABLE_RADV] = zebra_interface_radv_enable, [ZEBRA_INTERFACE_DISABLE_RADV] = zebra_interface_radv_disable, + [ZEBRA_SR_POLICY_SET] = zread_sr_policy_set, + [ZEBRA_SR_POLICY_DELETE] = zread_sr_policy_delete, [ZEBRA_MPLS_LABELS_ADD] = zread_mpls_labels_add, [ZEBRA_MPLS_LABELS_DELETE] = zread_mpls_labels_delete, [ZEBRA_MPLS_LABELS_REPLACE] = zread_mpls_labels_replace, @@ -2812,6 +3072,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_ADVERTISE_SVI_MACIP] = zebra_vxlan_advertise_svi_macip, [ZEBRA_ADVERTISE_SUBNET] = zebra_vxlan_advertise_subnet, [ZEBRA_ADVERTISE_ALL_VNI] = zebra_vxlan_advertise_all_vni, + [ZEBRA_REMOTE_ES_VTEP_ADD] = zebra_evpn_proc_remote_es, + [ZEBRA_REMOTE_ES_VTEP_DEL] = zebra_evpn_proc_remote_es, [ZEBRA_REMOTE_VTEP_ADD] = zebra_vxlan_remote_vtep_add, [ZEBRA_REMOTE_VTEP_DEL] = zebra_vxlan_remote_vtep_del, [ZEBRA_REMOTE_MACIP_ADD] = zebra_vxlan_remote_macip_add, @@ -2838,8 +3100,8 @@ void (*const zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_MLAG_CLIENT_REGISTER] = zebra_mlag_client_register, [ZEBRA_MLAG_CLIENT_UNREGISTER] = zebra_mlag_client_unregister, [ZEBRA_MLAG_FORWARD_MSG] = zebra_mlag_forward_client_msg, - [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities -}; + [ZEBRA_CLIENT_CAPABILITIES] = zread_client_capabilities, + [ZEBRA_NEIGH_DISCOVER] = zread_neigh_discover}; #if defined(HANDLE_ZAPI_FUZZING) extern struct zebra_privs_t zserv_privs; @@ -2890,7 +3152,8 @@ void zserv_handle_commands(struct zserv *client, struct stream_fifo *fifo) zapi_parse_header(msg, &hdr); - if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV) + if (IS_ZEBRA_DEBUG_PACKET && IS_ZEBRA_DEBUG_RECV + && IS_ZEBRA_DEBUG_DETAIL) zserv_log_message(NULL, msg, &hdr); #if defined(HANDLE_ZAPI_FUZZING) diff --git a/zebra/zapi_msg.h b/zebra/zapi_msg.h index eb50e3c410..e7c755c2bf 100644 --- a/zebra/zapi_msg.h +++ b/zebra/zapi_msg.h @@ -68,8 +68,8 @@ extern int zsend_redistribute_route(int cmd, struct zserv *zclient, const struct prefix *src_p, const struct route_entry *re); -extern int zsend_router_id_update(struct zserv *zclient, struct prefix *p, - vrf_id_t vrf_id); +extern int zsend_router_id_update(struct zserv *zclient, afi_t afi, + struct prefix *p, vrf_id_t vrf_id); extern int zsend_interface_vrf_update(struct zserv *zclient, struct interface *ifp, vrf_id_t vrf_id); extern int zsend_interface_link_params(struct zserv *zclient, @@ -100,7 +100,9 @@ extern int zsend_assign_label_chunk_response(struct zserv *client, extern int zsend_label_manager_connect_response(struct zserv *client, vrf_id_t vrf_id, unsigned short result); - +extern int zsend_sr_policy_notify_status(uint32_t color, + struct ipaddr *endpoint, char *name, + int status); #ifdef __cplusplus } diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index e34b6f23ff..6e8d35aa77 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -180,16 +180,19 @@ struct dplane_mac_info { struct ethaddr mac; struct in_addr vtep_ip; bool is_sticky; + uint32_t nhg_id; + uint32_t update_flags; }; /* - * EVPN neighbor info for the dataplane + * Neighbor info for the dataplane */ struct dplane_neigh_info { struct ipaddr ip_addr; struct ethaddr mac; uint32_t flags; uint16_t state; + uint32_t update_flags; }; /* @@ -204,6 +207,7 @@ struct dplane_ctx_rule { /* Filter criteria */ uint32_t filter_bm; uint32_t fwmark; + uint8_t dsfield; struct prefix src_ip; struct prefix dst_ip; }; @@ -440,13 +444,14 @@ static enum zebra_dplane_result mac_update_common( enum dplane_op_e op, const struct interface *ifp, const struct interface *br_ifp, vlanid_t vid, const struct ethaddr *mac, - struct in_addr vtep_ip, bool sticky); + struct in_addr vtep_ip, bool sticky, uint32_t nhg_id, + uint32_t update_flags); static enum zebra_dplane_result neigh_update_internal( enum dplane_op_e op, const struct interface *ifp, const struct ethaddr *mac, const struct ipaddr *ip, - uint32_t flags, uint16_t state); + uint32_t flags, uint16_t state, uint32_t update_flags); /* * Public APIs @@ -602,6 +607,7 @@ static void dplane_ctx_free_internal(struct zebra_dplane_ctx *ctx) case DPLANE_OP_RULE_ADD: case DPLANE_OP_RULE_DELETE: case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_NONE: break; } @@ -834,6 +840,10 @@ const char *dplane_op2str(enum dplane_op_e op) case DPLANE_OP_RULE_UPDATE: ret = "RULE_UPDATE"; break; + + case DPLANE_OP_NEIGH_DISCOVER: + ret = "NEIGH_DISCOVER"; + break; } return ret; @@ -853,8 +863,6 @@ const char *dplane_res2str(enum zebra_dplane_result res) case ZEBRA_DPLANE_REQUEST_SUCCESS: ret = "SUCCESS"; break; - case ZEBRA_DPLANE_REQUEST_PENDING: - ret = "PENDING"; } return ret; @@ -1551,6 +1559,18 @@ bool dplane_ctx_mac_is_sticky(const struct zebra_dplane_ctx *ctx) return ctx->u.macinfo.is_sticky; } +uint32_t dplane_ctx_mac_get_nhg_id(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.macinfo.nhg_id; +} + +uint32_t dplane_ctx_mac_get_update_flags(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.macinfo.update_flags; +} + const struct ethaddr *dplane_ctx_mac_get_addr( const struct zebra_dplane_ctx *ctx) { @@ -1598,6 +1618,12 @@ uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx) return ctx->u.neigh.state; } +uint32_t dplane_ctx_neigh_get_update_flags(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + return ctx->u.neigh.update_flags; +} + /* Accessors for PBR rule information */ int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx) { @@ -1676,6 +1702,20 @@ uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx) return ctx->u.rule.old.fwmark; } +uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.new.dsfield; +} + +uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx) +{ + DPLANE_CTX_VALID(ctx); + + return ctx->u.rule.old.dsfield; +} + const struct prefix * dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx) { @@ -1942,6 +1982,7 @@ int dplane_ctx_nexthop_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, * it probably won't require two messages */ dplane_ctx_ns_init(ctx, zns, (op == DPLANE_OP_NH_UPDATE)); + ctx->zd_is_update = (op == DPLANE_OP_NH_UPDATE); ret = AOK; @@ -1964,6 +2005,7 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, /* Capture namespace info */ dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), (op == DPLANE_OP_LSP_UPDATE)); + ctx->zd_is_update = (op == DPLANE_OP_LSP_UPDATE); memset(&ctx->u.lsp, 0, sizeof(ctx->u.lsp)); @@ -2001,10 +2043,19 @@ int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, break; } - /* Need to copy flags too */ + /* Need to copy flags and backup info too */ new_nhlfe->flags = nhlfe->flags; new_nhlfe->nexthop->flags = nhlfe->nexthop->flags; + if (CHECK_FLAG(new_nhlfe->nexthop->flags, + NEXTHOP_FLAG_HAS_BACKUP)) { + new_nhlfe->nexthop->backup_num = + nhlfe->nexthop->backup_num; + memcpy(new_nhlfe->nexthop->backup_idx, + nhlfe->nexthop->backup_idx, + new_nhlfe->nexthop->backup_num); + } + if (nhlfe == lsp->best_nhlfe) ctx->u.lsp.best_nhlfe = new_nhlfe; } @@ -2104,8 +2155,15 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx, if (re) { nhg = rib_get_fib_nhg(re); - copy_nexthops(&(ctx->u.pw.nhg.nexthop), - nhg->nexthop, NULL); + if (nhg && nhg->nexthop) + copy_nexthops(&(ctx->u.pw.nhg.nexthop), + nhg->nexthop, NULL); + + /* Include any installed backup nexthops */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) + copy_nexthops(&(ctx->u.pw.nhg.nexthop), + nhg->nexthop, NULL); } route_unlock_node(rn); } @@ -2129,6 +2187,7 @@ static void dplane_ctx_rule_init_single(struct dplane_ctx_rule *dplane_rule, dplane_rule->filter_bm = rule->rule.filter.filter_bm; dplane_rule->fwmark = rule->rule.filter.fwmark; + dplane_rule->dsfield = rule->rule.filter.dsfield; prefix_copy(&(dplane_rule->dst_ip), &rule->rule.filter.dst_ip); prefix_copy(&(dplane_rule->src_ip), &rule->rule.filter.src_ip); } @@ -2168,6 +2227,7 @@ static int dplane_ctx_rule_init(struct zebra_dplane_ctx *ctx, dplane_ctx_ns_init(ctx, zebra_ns_lookup(NS_DEFAULT), op == DPLANE_OP_RULE_UPDATE); + ctx->zd_is_update = (op == DPLANE_OP_RULE_UPDATE); ctx->zd_vrf_id = new_rule->vrf_id; memcpy(ctx->zd_ifname, new_rule->ifname, sizeof(new_rule->ifname)); @@ -2458,9 +2518,11 @@ dplane_route_notif_update(struct route_node *rn, enum dplane_op_e op, struct zebra_dplane_ctx *ctx) { - enum zebra_dplane_result ret = ZEBRA_DPLANE_REQUEST_FAILURE; + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + int ret = EINVAL; struct zebra_dplane_ctx *new_ctx = NULL; struct nexthop *nexthop; + struct nexthop_group *nhg; if (rn == NULL || re == NULL) goto done; @@ -2482,8 +2544,17 @@ dplane_route_notif_update(struct route_node *rn, nexthops_free(new_ctx->u.rinfo.zd_ng.nexthop); new_ctx->u.rinfo.zd_ng.nexthop = NULL; - copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), - (rib_get_fib_nhg(re))->nexthop, NULL); + nhg = rib_get_fib_nhg(re); + if (nhg && nhg->nexthop) + copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), + nhg->nexthop, NULL); + + /* Check for installed backup nexthops also */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg && nhg->nexthop) { + copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), + nhg->nexthop, NULL); + } for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); @@ -2494,12 +2565,15 @@ dplane_route_notif_update(struct route_node *rn, dplane_ctx_set_notif_provider(new_ctx, dplane_ctx_get_notif_provider(ctx)); - dplane_update_enqueue(new_ctx); - - ret = ZEBRA_DPLANE_REQUEST_QUEUED; + ret = dplane_update_enqueue(new_ctx); done: - return ret; + if (ret == AOK) + result = ZEBRA_DPLANE_REQUEST_QUEUED; + else if (new_ctx) + dplane_ctx_free(&new_ctx); + + return result; } /* @@ -2583,6 +2657,8 @@ dplane_lsp_notif_update(zebra_lsp_t *lsp, enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret = EINVAL; struct zebra_dplane_ctx *ctx = NULL; + struct nhlfe_list_head *head; + zebra_nhlfe_t *nhlfe, *new_nhlfe; /* Obtain context block */ ctx = dplane_ctx_alloc(); @@ -2591,10 +2667,27 @@ dplane_lsp_notif_update(zebra_lsp_t *lsp, goto done; } + /* Copy info from zebra LSP */ ret = dplane_ctx_lsp_init(ctx, op, lsp); if (ret != AOK) goto done; + /* Add any installed backup nhlfes */ + head = &(ctx->u.lsp.backup_nhlfe_list); + frr_each(nhlfe_list, head, nhlfe) { + + if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_FIB)) { + new_nhlfe = zebra_mpls_lsp_add_nh(&(ctx->u.lsp), + nhlfe->type, + nhlfe->nexthop); + + /* Need to copy flags too */ + new_nhlfe->flags = nhlfe->flags; + new_nhlfe->nexthop->flags = nhlfe->nexthop->flags; + } + } + /* Capture info about the source of the notification */ dplane_ctx_set_notif_provider( ctx, @@ -2830,35 +2923,75 @@ static enum zebra_dplane_result intf_addr_update_internal( /* * Enqueue vxlan/evpn mac add (or update). */ -enum zebra_dplane_result dplane_mac_add(const struct interface *ifp, +enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, const struct interface *bridge_ifp, vlanid_t vid, const struct ethaddr *mac, struct in_addr vtep_ip, - bool sticky) + bool sticky, + uint32_t nhg_id, + bool was_static) { enum zebra_dplane_result result; + uint32_t update_flags = 0; + + update_flags |= DPLANE_MAC_REMOTE; + if (was_static) + update_flags |= DPLANE_MAC_WAS_STATIC; /* Use common helper api */ result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp, - vid, mac, vtep_ip, sticky); + vid, mac, vtep_ip, sticky, nhg_id, update_flags); return result; } /* * Enqueue vxlan/evpn mac delete. */ -enum zebra_dplane_result dplane_mac_del(const struct interface *ifp, +enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, const struct interface *bridge_ifp, vlanid_t vid, const struct ethaddr *mac, struct in_addr vtep_ip) { enum zebra_dplane_result result; + uint32_t update_flags = 0; + + update_flags |= DPLANE_MAC_REMOTE; /* Use common helper api */ result = mac_update_common(DPLANE_OP_MAC_DELETE, ifp, bridge_ifp, - vid, mac, vtep_ip, false); + vid, mac, vtep_ip, false, 0, update_flags); + return result; +} + +/* + * Enqueue local mac add (or update). + */ +enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + bool sticky, + uint32_t set_static, + uint32_t set_inactive) +{ + enum zebra_dplane_result result; + uint32_t update_flags = 0; + struct in_addr vtep_ip; + + if (set_static) + update_flags |= DPLANE_MAC_SET_STATIC; + + if (set_inactive) + update_flags |= DPLANE_MAC_SET_INACTIVE; + + vtep_ip.s_addr = 0; + + /* Use common helper api */ + result = mac_update_common(DPLANE_OP_MAC_INSTALL, ifp, bridge_ifp, + vid, mac, vtep_ip, sticky, 0, + update_flags); return result; } @@ -2872,7 +3005,9 @@ void dplane_mac_init(struct zebra_dplane_ctx *ctx, vlanid_t vid, const struct ethaddr *mac, struct in_addr vtep_ip, - bool sticky) + bool sticky, + uint32_t nhg_id, + uint32_t update_flags) { struct zebra_ns *zns; @@ -2893,6 +3028,8 @@ void dplane_mac_init(struct zebra_dplane_ctx *ctx, ctx->u.macinfo.mac = *mac; ctx->u.macinfo.vid = vid; ctx->u.macinfo.is_sticky = sticky; + ctx->u.macinfo.nhg_id = nhg_id; + ctx->u.macinfo.update_flags = update_flags; } /* @@ -2905,7 +3042,9 @@ mac_update_common(enum dplane_op_e op, vlanid_t vid, const struct ethaddr *mac, struct in_addr vtep_ip, - bool sticky) + bool sticky, + uint32_t nhg_id, + uint32_t update_flags) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret; @@ -2925,7 +3064,8 @@ mac_update_common(enum dplane_op_e op, ctx->zd_op = op; /* Common init for the ctx */ - dplane_mac_init(ctx, ifp, br_ifp, vid, mac, vtep_ip, sticky); + dplane_mac_init(ctx, ifp, br_ifp, vid, mac, vtep_ip, sticky, + nhg_id, update_flags); /* Enqueue for processing on the dplane pthread */ ret = dplane_update_enqueue(ctx); @@ -2949,30 +3089,56 @@ mac_update_common(enum dplane_op_e op, /* * Enqueue evpn neighbor add for the dataplane. */ -enum zebra_dplane_result dplane_neigh_add(const struct interface *ifp, +enum zebra_dplane_result dplane_rem_neigh_add(const struct interface *ifp, const struct ipaddr *ip, const struct ethaddr *mac, - uint32_t flags) + uint32_t flags, bool was_static) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + uint32_t update_flags = 0; + + update_flags |= DPLANE_NEIGH_REMOTE; + + if (was_static) + update_flags |= DPLANE_NEIGH_WAS_STATIC; result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, - ifp, mac, ip, flags, DPLANE_NUD_NOARP); + ifp, mac, ip, flags, DPLANE_NUD_NOARP, + update_flags); return result; } /* - * Enqueue evpn neighbor update for the dataplane. + * Enqueue local neighbor add for the dataplane. */ -enum zebra_dplane_result dplane_neigh_update(const struct interface *ifp, - const struct ipaddr *ip, - const struct ethaddr *mac) +enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp, + const struct ipaddr *ip, + const struct ethaddr *mac, + bool set_router, bool set_static, + bool set_inactive) { - enum zebra_dplane_result result; + enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; + uint32_t update_flags = 0; + uint32_t ntf = 0; + uint16_t state; + + if (set_static) + update_flags |= DPLANE_NEIGH_SET_STATIC; + + if (set_inactive) { + update_flags |= DPLANE_NEIGH_SET_INACTIVE; + state = DPLANE_NUD_STALE; + } else { + state = DPLANE_NUD_REACHABLE; + } - result = neigh_update_internal(DPLANE_OP_NEIGH_UPDATE, - ifp, mac, ip, 0, DPLANE_NUD_PROBE); + if (set_router) + ntf |= DPLANE_NTF_ROUTER; + + result = neigh_update_internal(DPLANE_OP_NEIGH_INSTALL, + ifp, mac, ip, ntf, + state, update_flags); return result; } @@ -2980,13 +3146,16 @@ enum zebra_dplane_result dplane_neigh_update(const struct interface *ifp, /* * Enqueue evpn neighbor delete for the dataplane. */ -enum zebra_dplane_result dplane_neigh_delete(const struct interface *ifp, +enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp, const struct ipaddr *ip) { enum zebra_dplane_result result; + uint32_t update_flags = 0; + + update_flags |= DPLANE_NEIGH_REMOTE; result = neigh_update_internal(DPLANE_OP_NEIGH_DELETE, - ifp, NULL, ip, 0, 0); + ifp, NULL, ip, 0, 0, update_flags); return result; } @@ -3010,7 +3179,7 @@ enum zebra_dplane_result dplane_vtep_add(const struct interface *ifp, addr.ipaddr_v4 = *ip; result = neigh_update_internal(DPLANE_OP_VTEP_ADD, - ifp, &mac, &addr, 0, 0); + ifp, &mac, &addr, 0, 0, 0); return result; } @@ -3035,20 +3204,32 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, addr.ipaddr_v4 = *ip; result = neigh_update_internal(DPLANE_OP_VTEP_DELETE, - ifp, &mac, &addr, 0, 0); + ifp, &mac, &addr, 0, 0, 0); + + return result; +} + +enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, + const struct ipaddr *ip) +{ + enum zebra_dplane_result result; + + result = neigh_update_internal(DPLANE_OP_NEIGH_DISCOVER, ifp, NULL, ip, + DPLANE_NTF_USE, DPLANE_NUD_INCOMPLETE, 0); return result; } /* - * Common helper api for evpn neighbor updates + * Common helper api for neighbor updates */ static enum zebra_dplane_result neigh_update_internal(enum dplane_op_e op, const struct interface *ifp, const struct ethaddr *mac, const struct ipaddr *ip, - uint32_t flags, uint16_t state) + uint32_t flags, uint16_t state, + uint32_t update_flags) { enum zebra_dplane_result result = ZEBRA_DPLANE_REQUEST_FAILURE; int ret; @@ -3059,9 +3240,8 @@ neigh_update_internal(enum dplane_op_e op, char buf1[ETHER_ADDR_STRLEN], buf2[PREFIX_STRLEN]; zlog_debug("init neigh ctx %s: ifp %s, mac %s, ip %s", - dplane_op2str(op), + dplane_op2str(op), ifp->name, prefix_mac2str(mac, buf1, sizeof(buf1)), - ifp->name, ipaddr2str(ip, buf2, sizeof(buf2))); } @@ -3085,6 +3265,7 @@ neigh_update_internal(enum dplane_op_e op, ctx->u.neigh.mac = *mac; ctx->u.neigh.flags = flags; ctx->u.neigh.state = state; + ctx->u.neigh.update_flags = update_flags; /* Enqueue for processing on the dplane pthread */ ret = dplane_update_enqueue(ctx); @@ -3261,8 +3442,9 @@ int dplane_show_provs_helper(struct vty *vty, bool detailed) out_max = atomic_load_explicit(&prov->dp_out_max, memory_order_relaxed); - vty_out(vty, "%s (%u): in: %"PRIu64", q_max: %"PRIu64", " - "out: %"PRIu64", q_max: %"PRIu64"\n", + vty_out(vty, + "%s (%u): in: %" PRIu64 ", q_max: %" PRIu64 + ", out: %" PRIu64 ", q_max: %" PRIu64 "\n", prov->dp_name, prov->dp_id, in, in_max, out, out_max); DPLANE_LOCK(); @@ -3534,149 +3716,100 @@ void dplane_provider_enqueue_to_zebra(struct zebra_dplane_ctx *ctx) * Kernel dataplane provider */ -/* - * Handler for kernel LSP updates - */ -static enum zebra_dplane_result -kernel_dplane_lsp_update(struct zebra_dplane_ctx *ctx) +static void kernel_dplane_log_detail(struct zebra_dplane_ctx *ctx) { - return kernel_lsp_update(ctx); -} + char buf[PREFIX_STRLEN]; -/* - * Handler for kernel pseudowire updates - */ -static enum zebra_dplane_result -kernel_dplane_pw_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) - zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u", - dplane_ctx_get_ifname(ctx), - dplane_op2str(ctx->zd_op), - dplane_ctx_get_pw_af(ctx), - dplane_ctx_get_pw_local_label(ctx), - dplane_ctx_get_pw_remote_label(ctx)); - - return kernel_pw_update(ctx); -} - -/* - * Handler for kernel route updates - */ -static enum zebra_dplane_result -kernel_dplane_route_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char dest_str[PREFIX_STRLEN]; + switch (dplane_ctx_get_op(ctx)) { - prefix2str(dplane_ctx_get_dest(ctx), - dest_str, sizeof(dest_str)); + case DPLANE_OP_ROUTE_INSTALL: + case DPLANE_OP_ROUTE_UPDATE: + case DPLANE_OP_ROUTE_DELETE: + prefix2str(dplane_ctx_get_dest(ctx), buf, sizeof(buf)); zlog_debug("%u:%s Dplane route update ctx %p op %s", - dplane_ctx_get_vrf(ctx), dest_str, - ctx, dplane_op2str(dplane_ctx_get_op(ctx))); - } - - return kernel_route_update(ctx); -} - -/* - * Handler for kernel-facing interface address updates - */ -static enum zebra_dplane_result -kernel_dplane_address_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char dest_str[PREFIX_STRLEN]; - - prefix2str(dplane_ctx_get_intf_addr(ctx), dest_str, - sizeof(dest_str)); - - zlog_debug("Dplane intf %s, idx %u, addr %s", - dplane_op2str(dplane_ctx_get_op(ctx)), - dplane_ctx_get_ifindex(ctx), dest_str); - } - - return kernel_address_update_ctx(ctx); -} + dplane_ctx_get_vrf(ctx), buf, ctx, + dplane_op2str(dplane_ctx_get_op(ctx))); + break; -/** - * kernel_dplane_nexthop_update() - Handler for kernel nexthop updates - * - * @ctx: Dataplane context - * - * Return: Dataplane result flag - */ -static enum zebra_dplane_result -kernel_dplane_nexthop_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { + case DPLANE_OP_NH_INSTALL: + case DPLANE_OP_NH_UPDATE: + case DPLANE_OP_NH_DELETE: zlog_debug("ID (%u) Dplane nexthop update ctx %p op %s", dplane_ctx_get_nhe_id(ctx), ctx, dplane_op2str(dplane_ctx_get_op(ctx))); - } + break; - return kernel_nexthop_update(ctx); -} + case DPLANE_OP_LSP_INSTALL: + case DPLANE_OP_LSP_UPDATE: + case DPLANE_OP_LSP_DELETE: + break; -/* - * Handler for kernel-facing EVPN MAC address updates - */ -static enum zebra_dplane_result -kernel_dplane_mac_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char buf[ETHER_ADDR_STRLEN]; + case DPLANE_OP_PW_INSTALL: + case DPLANE_OP_PW_UNINSTALL: + zlog_debug("Dplane pw %s: op %s af %d loc: %u rem: %u", + dplane_ctx_get_ifname(ctx), + dplane_op2str(ctx->zd_op), dplane_ctx_get_pw_af(ctx), + dplane_ctx_get_pw_local_label(ctx), + dplane_ctx_get_pw_remote_label(ctx)); + break; + case DPLANE_OP_ADDR_INSTALL: + case DPLANE_OP_ADDR_UNINSTALL: + prefix2str(dplane_ctx_get_intf_addr(ctx), buf, sizeof(buf)); + + zlog_debug("Dplane intf %s, idx %u, addr %s", + dplane_op2str(dplane_ctx_get_op(ctx)), + dplane_ctx_get_ifindex(ctx), buf); + break; + + case DPLANE_OP_MAC_INSTALL: + case DPLANE_OP_MAC_DELETE: prefix_mac2str(dplane_ctx_mac_get_addr(ctx), buf, sizeof(buf)); zlog_debug("Dplane %s, mac %s, ifindex %u", dplane_op2str(dplane_ctx_get_op(ctx)), buf, dplane_ctx_get_ifindex(ctx)); - } - - return kernel_mac_update_ctx(ctx); -} - -/* - * Handler for kernel-facing EVPN neighbor updates - */ -static enum zebra_dplane_result -kernel_dplane_neigh_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) { - char buf[PREFIX_STRLEN]; + break; + case DPLANE_OP_NEIGH_INSTALL: + case DPLANE_OP_NEIGH_UPDATE: + case DPLANE_OP_NEIGH_DELETE: + case DPLANE_OP_VTEP_ADD: + case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: ipaddr2str(dplane_ctx_neigh_get_ipaddr(ctx), buf, sizeof(buf)); zlog_debug("Dplane %s, ip %s, ifindex %u", dplane_op2str(dplane_ctx_get_op(ctx)), buf, dplane_ctx_get_ifindex(ctx)); - } - - return kernel_neigh_update_ctx(ctx); -} + break; -/* - * Handler for kernel PBR rule updates - */ -static enum zebra_dplane_result -kernel_dplane_rule_update(struct zebra_dplane_ctx *ctx) -{ - if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + case DPLANE_OP_RULE_ADD: + case DPLANE_OP_RULE_DELETE: + case DPLANE_OP_RULE_UPDATE: zlog_debug("Dplane rule update op %s, if %s(%u), ctx %p", dplane_op2str(dplane_ctx_get_op(ctx)), dplane_ctx_get_ifname(ctx), dplane_ctx_get_ifindex(ctx), ctx); + break; + + case DPLANE_OP_SYS_ROUTE_ADD: + case DPLANE_OP_SYS_ROUTE_DELETE: + case DPLANE_OP_ROUTE_NOTIFY: + case DPLANE_OP_LSP_NOTIFY: - return kernel_pbr_rule_update(ctx); + case DPLANE_OP_NONE: + break; + } } -static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, - enum zebra_dplane_result res) +static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx) { + enum zebra_dplane_result res = dplane_ctx_get_status(ctx); + switch (dplane_ctx_get_op(ctx)) { case DPLANE_OP_ROUTE_INSTALL: @@ -3685,6 +3818,27 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_route_errors, 1, memory_order_relaxed); + + if ((dplane_ctx_get_op(ctx) != DPLANE_OP_ROUTE_DELETE) + && (res == ZEBRA_DPLANE_REQUEST_SUCCESS)) { + struct nexthop *nexthop; + + /* Update installed nexthops to signal which have been + * installed. + */ + for (ALL_NEXTHOPS_PTR(dplane_ctx_get_ng(ctx), + nexthop)) { + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE)) + continue; + + if (CHECK_FLAG(nexthop->flags, + NEXTHOP_FLAG_ACTIVE)) { + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_FIB); + } + } + } break; case DPLANE_OP_NH_INSTALL: @@ -3731,6 +3885,7 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) atomic_fetch_add_explicit(&zdplane_info.dg_neigh_errors, 1, memory_order_relaxed); @@ -3749,11 +3904,14 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, case DPLANE_OP_SYS_ROUTE_DELETE: case DPLANE_OP_ROUTE_NOTIFY: case DPLANE_OP_LSP_NOTIFY: + break; + case DPLANE_OP_NONE: + if (res != ZEBRA_DPLANE_REQUEST_SUCCESS) + atomic_fetch_add_explicit(&zdplane_info.dg_other_errors, + 1, memory_order_relaxed); break; } - - dplane_ctx_set_status(ctx, res); } /* @@ -3761,7 +3919,6 @@ static void kernel_dplane_handle_result(struct zebra_dplane_ctx *ctx, */ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) { - enum zebra_dplane_result res; struct zebra_dplane_ctx *ctx, *tctx; struct dplane_ctx_q work_list; int counter, limit; @@ -3775,97 +3932,21 @@ static int kernel_dplane_process_func(struct zebra_dplane_provider *prov) dplane_provider_get_name(prov)); for (counter = 0; counter < limit; counter++) { - ctx = dplane_provider_dequeue_in_ctx(prov); if (ctx == NULL) break; - /* A previous provider plugin may have asked to skip the - * kernel update. - */ - if (dplane_ctx_is_skip_kernel(ctx)) { - res = ZEBRA_DPLANE_REQUEST_SUCCESS; - goto skip_one; - } - - /* Dispatch to appropriate kernel-facing apis */ - switch (dplane_ctx_get_op(ctx)) { - - case DPLANE_OP_ROUTE_INSTALL: - case DPLANE_OP_ROUTE_UPDATE: - case DPLANE_OP_ROUTE_DELETE: - res = kernel_dplane_route_update(ctx); - break; - - case DPLANE_OP_NH_INSTALL: - case DPLANE_OP_NH_UPDATE: - case DPLANE_OP_NH_DELETE: - res = kernel_dplane_nexthop_update(ctx); - break; - - case DPLANE_OP_LSP_INSTALL: - case DPLANE_OP_LSP_UPDATE: - case DPLANE_OP_LSP_DELETE: - res = kernel_dplane_lsp_update(ctx); - break; - - case DPLANE_OP_PW_INSTALL: - case DPLANE_OP_PW_UNINSTALL: - res = kernel_dplane_pw_update(ctx); - break; - - case DPLANE_OP_ADDR_INSTALL: - case DPLANE_OP_ADDR_UNINSTALL: - res = kernel_dplane_address_update(ctx); - break; - - case DPLANE_OP_MAC_INSTALL: - case DPLANE_OP_MAC_DELETE: - res = kernel_dplane_mac_update(ctx); - break; - - case DPLANE_OP_NEIGH_INSTALL: - case DPLANE_OP_NEIGH_UPDATE: - case DPLANE_OP_NEIGH_DELETE: - case DPLANE_OP_VTEP_ADD: - case DPLANE_OP_VTEP_DELETE: - res = kernel_dplane_neigh_update(ctx); - break; - - case DPLANE_OP_RULE_ADD: - case DPLANE_OP_RULE_DELETE: - case DPLANE_OP_RULE_UPDATE: - res = kernel_dplane_rule_update(ctx); - break; - - /* Ignore 'notifications' - no-op */ - case DPLANE_OP_SYS_ROUTE_ADD: - case DPLANE_OP_SYS_ROUTE_DELETE: - case DPLANE_OP_ROUTE_NOTIFY: - case DPLANE_OP_LSP_NOTIFY: - res = ZEBRA_DPLANE_REQUEST_SUCCESS; - break; - - default: - atomic_fetch_add_explicit( - &zdplane_info.dg_other_errors, 1, - memory_order_relaxed); - - res = ZEBRA_DPLANE_REQUEST_FAILURE; - break; - } - - skip_one: - /* If the request isn't pending, we can handle the result right - * away. - */ - if (res != ZEBRA_DPLANE_REQUEST_PENDING) - kernel_dplane_handle_result(ctx, res); + if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) + kernel_dplane_log_detail(ctx); TAILQ_INSERT_TAIL(&work_list, ctx, zd_q_entries); } + kernel_update_multi(&work_list); + TAILQ_FOREACH_SAFE (ctx, &work_list, zd_q_entries, tctx) { + kernel_dplane_handle_result(ctx); + TAILQ_REMOVE(&work_list, ctx, zd_q_entries); dplane_provider_enqueue_out_ctx(prov, ctx); } @@ -3910,7 +3991,6 @@ static int test_dplane_process_func(struct zebra_dplane_provider *prov) limit = dplane_provider_get_work_limit(prov); for (counter = 0; counter < limit; counter++) { - ctx = dplane_provider_dequeue_in_ctx(prov); if (ctx == NULL) break; @@ -4003,19 +4083,19 @@ bool dplane_is_in_shutdown(void) */ void zebra_dplane_pre_finish(void) { - struct zebra_dplane_provider *dp; + struct zebra_dplane_provider *prov; if (IS_ZEBRA_DEBUG_DPLANE) - zlog_debug("Zebra dataplane pre-fini called"); + zlog_debug("Zebra dataplane pre-finish called"); zdplane_info.dg_is_shutdown = true; /* Notify provider(s) of pending shutdown. */ - TAILQ_FOREACH(dp, &zdplane_info.dg_providers_q, dp_prov_link) { - if (dp->dp_fini == NULL) + TAILQ_FOREACH(prov, &zdplane_info.dg_providers_q, dp_prov_link) { + if (prov->dp_fini == NULL) continue; - dp->dp_fini(dp, true); + prov->dp_fini(prov, true /* early */); } } @@ -4337,7 +4417,10 @@ void zebra_dplane_shutdown(void) zdplane_info.dg_pthread = NULL; zdplane_info.dg_master = NULL; - /* Notify provider(s) of final shutdown. */ + /* Notify provider(s) of final shutdown. + * Note that this call is in the main pthread, so providers must + * be prepared for that. + */ TAILQ_FOREACH(dp, &zdplane_info.dg_providers_q, dp_prov_link) { if (dp->dp_fini == NULL) continue; diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 8e873886df..5dd789a851 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -90,7 +90,6 @@ enum zebra_dplane_result { ZEBRA_DPLANE_REQUEST_QUEUED, ZEBRA_DPLANE_REQUEST_SUCCESS, ZEBRA_DPLANE_REQUEST_FAILURE, - ZEBRA_DPLANE_REQUEST_PENDING, }; /* @@ -150,6 +149,9 @@ enum dplane_op_e { DPLANE_OP_RULE_ADD, DPLANE_OP_RULE_DELETE, DPLANE_OP_RULE_UPDATE, + + /* Link layer address discovery */ + DPLANE_OP_NEIGH_DISCOVER, }; /* @@ -161,12 +163,26 @@ enum dplane_op_e { /* Neighbor cache flags */ #define DPLANE_NTF_EXT_LEARNED 0x01 #define DPLANE_NTF_ROUTER 0x02 +#define DPLANE_NTF_USE 0x04 /* Neighbor cache states */ #define DPLANE_NUD_REACHABLE 0x01 #define DPLANE_NUD_STALE 0x02 #define DPLANE_NUD_NOARP 0x04 #define DPLANE_NUD_PROBE 0x08 +#define DPLANE_NUD_INCOMPLETE 0x10 + +/* MAC update flags - dplane_mac_info.update_flags */ +#define DPLANE_MAC_REMOTE (1 << 0) +#define DPLANE_MAC_WAS_STATIC (1 << 1) +#define DPLANE_MAC_SET_STATIC (1 << 2) +#define DPLANE_MAC_SET_INACTIVE (1 << 3) + +/* Neigh update flags - dplane_neigh_info.update_flags */ +#define DPLANE_NEIGH_REMOTE (1 << 0) +#define DPLANE_NEIGH_WAS_STATIC (1 << 1) +#define DPLANE_NEIGH_SET_STATIC (1 << 2) +#define DPLANE_NEIGH_SET_INACTIVE (1 << 3) /* Enable system route notifications */ void dplane_enable_sys_route_notifs(void); @@ -386,6 +402,8 @@ const char *dplane_ctx_get_intf_label(const struct zebra_dplane_ctx *ctx); /* Accessors for MAC information */ vlanid_t dplane_ctx_mac_get_vlan(const struct zebra_dplane_ctx *ctx); bool dplane_ctx_mac_is_sticky(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_mac_get_update_flags(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_mac_get_nhg_id(const struct zebra_dplane_ctx *ctx); const struct ethaddr *dplane_ctx_mac_get_addr( const struct zebra_dplane_ctx *ctx); const struct in_addr *dplane_ctx_mac_get_vtep_ip( @@ -399,6 +417,7 @@ const struct ethaddr *dplane_ctx_neigh_get_mac( const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_neigh_get_flags(const struct zebra_dplane_ctx *ctx); uint16_t dplane_ctx_neigh_get_state(const struct zebra_dplane_ctx *ctx); +uint32_t dplane_ctx_neigh_get_update_flags(const struct zebra_dplane_ctx *ctx); /* Accessors for policy based routing rule information */ int dplane_ctx_rule_get_sock(const struct zebra_dplane_ctx *ctx); @@ -412,6 +431,8 @@ uint32_t dplane_ctx_rule_get_filter_bm(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_rule_get_old_filter_bm(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_rule_get_fwmark(const struct zebra_dplane_ctx *ctx); uint32_t dplane_ctx_rule_get_old_fwmark(const struct zebra_dplane_ctx *ctx); +uint8_t dplane_ctx_rule_get_dsfield(const struct zebra_dplane_ctx *ctx); +uint8_t dplane_ctx_rule_get_old_dsfield(const struct zebra_dplane_ctx *ctx); const struct prefix * dplane_ctx_rule_get_src_ip(const struct zebra_dplane_ctx *ctx); const struct prefix * @@ -495,20 +516,24 @@ enum zebra_dplane_result dplane_intf_addr_unset(const struct interface *ifp, /* * Enqueue evpn mac operations for the dataplane. */ -extern struct zebra_dplane_ctx *mac_update_internal( - enum dplane_op_e op, const struct interface *ifp, - const struct interface *br_ifp, - vlanid_t vid, const struct ethaddr *mac, - struct in_addr vtep_ip, bool sticky); - -enum zebra_dplane_result dplane_mac_add(const struct interface *ifp, +enum zebra_dplane_result dplane_rem_mac_add(const struct interface *ifp, const struct interface *bridge_ifp, vlanid_t vid, const struct ethaddr *mac, struct in_addr vtep_ip, - bool sticky); + bool sticky, + uint32_t nhg_id, + bool was_static); -enum zebra_dplane_result dplane_mac_del(const struct interface *ifp, +enum zebra_dplane_result dplane_local_mac_add(const struct interface *ifp, + const struct interface *bridge_ifp, + vlanid_t vid, + const struct ethaddr *mac, + bool sticky, + uint32_t set_static, + uint32_t set_inactive); + +enum zebra_dplane_result dplane_rem_mac_del(const struct interface *ifp, const struct interface *bridge_ifp, vlanid_t vid, const struct ethaddr *mac, @@ -521,19 +546,22 @@ void dplane_mac_init(struct zebra_dplane_ctx *ctx, vlanid_t vid, const struct ethaddr *mac, struct in_addr vtep_ip, - bool sticky); + bool sticky, + uint32_t nhg_id, uint32_t update_flags); /* * Enqueue evpn neighbor updates for the dataplane. */ -enum zebra_dplane_result dplane_neigh_add(const struct interface *ifp, +enum zebra_dplane_result dplane_rem_neigh_add(const struct interface *ifp, + const struct ipaddr *ip, + const struct ethaddr *mac, + uint32_t flags, bool was_static); +enum zebra_dplane_result dplane_local_neigh_add(const struct interface *ifp, const struct ipaddr *ip, const struct ethaddr *mac, - uint32_t flags); -enum zebra_dplane_result dplane_neigh_update(const struct interface *ifp, - const struct ipaddr *ip, - const struct ethaddr *mac); -enum zebra_dplane_result dplane_neigh_delete(const struct interface *ifp, + bool set_router, bool set_static, + bool set_inactive); +enum zebra_dplane_result dplane_rem_neigh_delete(const struct interface *ifp, const struct ipaddr *ip); /* @@ -546,6 +574,12 @@ enum zebra_dplane_result dplane_vtep_delete(const struct interface *ifp, const struct in_addr *ip, vni_t vni); +/* + * Enqueue a neighbour discovery request for the dataplane. + */ +enum zebra_dplane_result dplane_neigh_discover(const struct interface *ifp, + const struct ipaddr *ip); + /* Forward ref of zebra_pbr_rule */ struct zebra_pbr_rule; diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index 5f2a7a12c6..03953ed17f 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -134,6 +134,7 @@ enum zebra_log_refs { EC_ZEBRA_BAD_NHG_MESSAGE, EC_ZEBRA_DUPLICATE_NHG_MESSAGE, EC_ZEBRA_VRF_MISCONFIGURED, + EC_ZEBRA_ES_CREATE, }; void zebra_error_init(void); diff --git a/zebra/zebra_evpn.c b/zebra/zebra_evpn.c new file mode 100644 index 0000000000..73df93258e --- /dev/null +++ b/zebra/zebra_evpn.c @@ -0,0 +1,1448 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#include <zebra.h> + +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" +#include "vlan.h" +#include "vxlan.h" +#ifdef GNU_LINUX +#include <linux/neighbour.h> +#endif + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_l2.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" +#include "zebra/zebra_router.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN, "VNI hash"); +DEFINE_MTYPE_STATIC(ZEBRA, ZEVPN_VTEP, "VNI remote VTEP"); + +/* PMSI strings. */ +#define VXLAN_FLOOD_STR_NO_INFO "-" +#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO +static const struct message zvtep_flood_str[] = { + {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO}, + {VXLAN_FLOOD_PIM_SM, "PIM-SM"}, + {VXLAN_FLOOD_HEAD_END_REPL, "HER"}, + {0} +}; + +int advertise_gw_macip_enabled(zebra_evpn_t *zevpn) +{ + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + if (zvrf && zvrf->advertise_gw_macip) + return 1; + + if (zevpn && zevpn->advertise_gw_macip) + return 1; + + return 0; +} + +int advertise_svi_macip_enabled(zebra_evpn_t *zevpn) +{ + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + if (zvrf && zvrf->advertise_svi_macip) + return 1; + + if (zevpn && zevpn->advertise_svi_macip) + return 1; + + return 0; +} + +/* + * Print a specific EVPN entry. + */ +void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt) +{ + struct vty *vty; + zebra_vtep_t *zvtep; + uint32_t num_macs; + uint32_t num_neigh; + json_object *json = NULL; + json_object *json_vtep_list = NULL; + json_object *json_ip_str = NULL; + + vty = ctxt[0]; + json = ctxt[1]; + + if (json == NULL) { + vty_out(vty, "VNI: %u\n", zevpn->vni); + vty_out(vty, " Type: %s\n", "L2"); + vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zevpn->vrf_id)); + } else { + json_object_int_add(json, "vni", zevpn->vni); + json_object_string_add(json, "type", "L2"); + json_object_string_add(json, "vrf", + vrf_id_to_name(zevpn->vrf_id)); + } + + if (!zevpn->vxlan_if) { // unexpected + if (json == NULL) + vty_out(vty, " VxLAN interface: unknown\n"); + return; + } + num_macs = num_valid_macs(zevpn); + num_neigh = hashcount(zevpn->neigh_table); + if (json == NULL) { + vty_out(vty, " VxLAN interface: %s\n", zevpn->vxlan_if->name); + vty_out(vty, " VxLAN ifIndex: %u\n", zevpn->vxlan_if->ifindex); + vty_out(vty, " Local VTEP IP: %s\n", + inet_ntoa(zevpn->local_vtep_ip)); + vty_out(vty, " Mcast group: %s\n", + inet_ntoa(zevpn->mcast_grp)); + } else { + json_object_string_add(json, "vxlanInterface", + zevpn->vxlan_if->name); + json_object_int_add(json, "ifindex", zevpn->vxlan_if->ifindex); + json_object_string_add(json, "vtepIp", + inet_ntoa(zevpn->local_vtep_ip)); + json_object_string_add(json, "mcastGroup", + inet_ntoa(zevpn->mcast_grp)); + json_object_string_add(json, "advertiseGatewayMacip", + zevpn->advertise_gw_macip ? "Yes" : "No"); + json_object_int_add(json, "numMacs", num_macs); + json_object_int_add(json, "numArpNd", num_neigh); + } + if (!zevpn->vteps) { + if (json == NULL) + vty_out(vty, " No remote VTEPs known for this VNI\n"); + } else { + if (json == NULL) + vty_out(vty, " Remote VTEPs for this VNI:\n"); + else + json_vtep_list = json_object_new_array(); + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + const char *flood_str = lookup_msg(zvtep_flood_str, + zvtep->flood_control, + VXLAN_FLOOD_STR_DEFAULT); + + if (json == NULL) { + vty_out(vty, " %s flood: %s\n", + inet_ntoa(zvtep->vtep_ip), + flood_str); + } else { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + } + if (json) + json_object_object_add(json, "numRemoteVteps", + json_vtep_list); + } + if (json == NULL) { + vty_out(vty, + " Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, + " Number of ARPs (IPv4 and IPv6, local and remote) " + "known for this VNI: %u\n", + num_neigh); + vty_out(vty, " Advertise-gw-macip: %s\n", + zevpn->advertise_gw_macip ? "Yes" : "No"); + } +} + +/* + * Print an EVPN hash entry - called for display of all VNIs. + */ +void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[]) +{ + struct vty *vty; + zebra_evpn_t *zevpn; + zebra_vtep_t *zvtep; + uint32_t num_vteps = 0; + uint32_t num_macs = 0; + uint32_t num_neigh = 0; + json_object *json = NULL; + json_object *json_evpn = NULL; + json_object *json_ip_str = NULL; + json_object *json_vtep_list = NULL; + + vty = ctxt[0]; + json = ctxt[1]; + + zevpn = (zebra_evpn_t *)bucket->data; + + zvtep = zevpn->vteps; + while (zvtep) { + num_vteps++; + zvtep = zvtep->next; + } + + num_macs = num_valid_macs(zevpn); + num_neigh = hashcount(zevpn->neigh_table); + if (json == NULL) + vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n", + zevpn->vni, "L2", + zevpn->vxlan_if ? zevpn->vxlan_if->name : "unknown", + num_macs, num_neigh, num_vteps, + vrf_id_to_name(zevpn->vrf_id)); + else { + char vni_str[VNI_STR_LEN]; + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "vni", zevpn->vni); + json_object_string_add(json_evpn, "type", "L2"); + json_object_string_add(json_evpn, "vxlanIf", + zevpn->vxlan_if ? zevpn->vxlan_if->name + : "unknown"); + json_object_int_add(json_evpn, "numMacs", num_macs); + json_object_int_add(json_evpn, "numArpNd", num_neigh); + json_object_int_add(json_evpn, "numRemoteVteps", num_vteps); + json_object_string_add(json_evpn, "tenantVrf", + vrf_id_to_name(zevpn->vrf_id)); + if (num_vteps) { + json_vtep_list = json_object_new_array(); + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + json_ip_str = json_object_new_string( + inet_ntoa(zvtep->vtep_ip)); + json_object_array_add(json_vtep_list, + json_ip_str); + } + json_object_object_add(json_evpn, "remoteVteps", + json_vtep_list); + } + json_object_object_add(json, vni_str, json_evpn); + } +} + +/* + * Print an EVPN hash entry in detail - called for display of all EVPNs. + */ +void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data) +{ + struct vty *vty; + zebra_evpn_t *zevpn; + json_object *json_array = NULL; + bool use_json = false; + struct zebra_evpn_show *zes = data; + + vty = zes->vty; + json_array = zes->json; + use_json = zes->use_json; + + zevpn = (zebra_evpn_t *)bucket->data; + + zebra_vxlan_print_vni(vty, zes->zvrf, zevpn->vni, use_json, json_array); + + if (!use_json) + vty_out(vty, "\n"); +} + +int zebra_evpn_del_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn) +{ + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) + continue; + + if (c->address->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), + sizeof(struct in_addr)); + } else if (c->address->family == AF_INET6) { + ip.ipa_type = IPADDR_V6; + memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), + sizeof(struct in6_addr)); + } else { + continue; + } + + zebra_evpn_gw_macip_del(ifp, zevpn, &ip); + } + + return 0; +} + +int zebra_evpn_add_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn) +{ + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct ipaddr ip; + + memset(&ip, 0, sizeof(struct ipaddr)); + if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) + continue; + + if (c->address->family == AF_INET) { + ip.ipa_type = IPADDR_V4; + memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), + sizeof(struct in_addr)); + } else if (c->address->family == AF_INET6) { + ip.ipa_type = IPADDR_V6; + memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), + sizeof(struct in6_addr)); + } else { + continue; + } + + zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip); + } + return 0; +} + +static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, + uint16_t cmd) +{ + struct zserv *client = NULL; + struct stream *s = NULL; + char buf[PREFIX_STRLEN]; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, cmd, vrf_id); + stream_put(s, p, sizeof(struct prefix)); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send ip prefix %s %s on vrf %s", + prefix2str(p, buf, sizeof(buf)), + (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL", + vrf_id_to_name(vrf_id)); + + if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) + client->prefixadd_cnt++; + else + client->prefixdel_cnt++; + + return zserv_send_message(client, s); +} + +int zebra_evpn_advertise_subnet(zebra_evpn_t *zevpn, struct interface *ifp, + int advertise) +{ + struct listnode *cnode = NULL, *cnnode = NULL; + struct connected *c = NULL; + struct ethaddr macaddr; + + memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); + + for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { + struct prefix p; + + memcpy(&p, c->address, sizeof(struct prefix)); + + /* skip link local address */ + if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) + continue; + + apply_mask(&p); + if (advertise) + ip_prefix_send_to_client(ifp->vrf_id, &p, + ZEBRA_IP_PREFIX_ROUTE_ADD); + else + ip_prefix_send_to_client(ifp->vrf_id, &p, + ZEBRA_IP_PREFIX_ROUTE_DEL); + } + return 0; +} + +/* + * zebra_evpn_gw_macip_add_to_client + */ +int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ethaddr *macaddr, struct ipaddr *ip) +{ + zebra_mac_t *mac = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan *vxl = NULL; + + zif = zevpn->vxlan_if->info; + if (!zif) + return -1; + + vxl = &zif->l2info.vxl; + + if (zebra_evpn_mac_gw_macip_add(ifp, zevpn, ip, &mac, macaddr, + vxl->access_vlan) + != 0) + return -1; + + return zebra_evpn_neigh_gw_macip_add(ifp, zevpn, ip, mac); +} + +/* + * zebra_evpn_gw_macip_del_from_client + */ +int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip) +{ + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *mac = NULL; + + /* If the neigh entry is not present nothing to do*/ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + /* mac entry should be present */ + mac = zebra_evpn_mac_lookup(zevpn, &n->emac); + if (!mac) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u", + prefix_mac2str(&n->emac, + buf1, sizeof(buf1)), + ipaddr2str(ip, buf2, sizeof(buf2)), + zevpn->vni); + return -1; + } + + /* If the entry is not local nothing to do*/ + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) + return -1; + + /* only need to delete the entry from bgp if we sent it before */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", + ifp->vrf_id, ifp->name, ifp->ifindex, zevpn->vni, + prefix_mac2str(&(n->emac), buf1, sizeof(buf1)), + ipaddr2str(ip, buf2, sizeof(buf2))); + + /* Remove neighbor from BGP. */ + zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac, + n->flags, ZEBRA_NEIGH_ACTIVE, + false /*force*/); + + /* Delete this neighbor entry. */ + zebra_evpn_neigh_del(zevpn, n); + + /* see if the mac needs to be deleted as well*/ + if (mac) + zebra_evpn_deref_ip2mac(zevpn, mac); + + return 0; +} + +void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *vrr_if = NULL; + struct interface *ifp; + + /* Add primary SVI MAC*/ + zevpn = (zebra_evpn_t *)bucket->data; + + /* Global (Zvrf) advertise-default-gw is disabled, + * but zevpn advertise-default-gw is enabled + */ + if (zevpn->advertise_gw_macip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip", + zevpn->vni); + return; + } + + ifp = zevpn->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + + zl2_info = zif->l2info.vxl; + + vlan_if = + zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Del primary MAC-IP */ + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); + + /* Del VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_del_macip_for_intf(vrr_if, zevpn); + + return; +} + +void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *vrr_if = NULL; + struct interface *ifp = NULL; + + zevpn = (zebra_evpn_t *)bucket->data; + + ifp = zevpn->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + zl2_info = zif->l2info.vxl; + + vlan_if = + zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Add primary SVI MAC-IP */ + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); + + if (advertise_gw_macip_enabled(zevpn)) { + /* Add VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); + } + + return; +} + +void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + struct interface *vlan_if = NULL; + struct interface *ifp; + + /* Add primary SVI MAC*/ + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) + return; + + /* Global(vrf) advertise-svi-ip disabled, but zevpn advertise-svi-ip + * enabled + */ + if (zevpn->advertise_svi_macip) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip", + zevpn->vni); + return; + } + + ifp = zevpn->vxlan_if; + if (!ifp) + return; + zif = ifp->info; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return; + + zl2_info = zif->l2info.vxl; + + vlan_if = + zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); + if (!vlan_if) + return; + + /* Del primary MAC-IP */ + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); + + return; +} + +/* + * Map port or (port, VLAN) to an EVPN. This is invoked upon getting MAC + * notifications, to see if they are of interest. + */ +zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, + struct interface *br_if, vlanid_t vid) +{ + struct zebra_ns *zns; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + struct zebra_l2info_vxlan *vxl = NULL; + uint8_t bridge_vlan_aware; + zebra_evpn_t *zevpn; + int found = 0; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + bridge_vlan_aware = br->vlan_aware; + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!bridge_vlan_aware || vxl->access_vlan == vid) { + found = 1; + break; + } + } + + if (!found) + return NULL; + + zevpn = zebra_evpn_lookup(vxl->vni); + return zevpn; +} + +/* + * Map SVI and associated bridge to an EVPN. This is invoked upon getting + * neighbor notifications, to see if they are of interest. + */ +zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, + struct interface *br_if) +{ + struct zebra_ns *zns; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + struct zebra_l2info_bridge *br; + struct zebra_l2info_vxlan *vxl = NULL; + uint8_t bridge_vlan_aware; + vlanid_t vid = 0; + zebra_evpn_t *zevpn; + int found = 0; + + if (!br_if) + return NULL; + + /* Make sure the linked interface is a bridge. */ + if (!IS_ZEBRA_IF_BRIDGE(br_if)) + return NULL; + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + br = &zif->l2info.br; + bridge_vlan_aware = br->vlan_aware; + if (bridge_vlan_aware) { + struct zebra_l2info_vlan *vl; + + if (!IS_ZEBRA_IF_VLAN(ifp)) + return NULL; + + zif = ifp->info; + assert(zif); + vl = &zif->l2info.vl; + vid = vl->vid; + } + + /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ + /* TODO: Optimize with a hash. */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + if (!tmp_if) + continue; + zif = tmp_if->info; + if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) + continue; + if (!if_is_operative(tmp_if)) + continue; + vxl = &zif->l2info.vxl; + + if (zif->brslave_info.br_if != br_if) + continue; + + if (!bridge_vlan_aware || vxl->access_vlan == vid) { + found = 1; + break; + } + } + + if (!found) + return NULL; + + zevpn = zebra_evpn_lookup(vxl->vni); + return zevpn; +} + +/* Map to MAC-VLAN interface corresponding to specified SVI interface. + */ +struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, + struct interface *svi_if) +{ + struct zebra_ns *zns; + struct route_node *rn; + struct interface *tmp_if = NULL; + struct zebra_if *zif; + int found = 0; + + /* Defensive check, caller expected to invoke only with valid bridge. */ + if (!br_if) + return NULL; + + if (!svi_if) { + zlog_debug("svi_if is not passed."); + return NULL; + } + + /* Determine if bridge is VLAN-aware or not */ + zif = br_if->info; + assert(zif); + + /* Identify corresponding VLAN interface. */ + zns = zebra_ns_lookup(NS_DEFAULT); + for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { + tmp_if = (struct interface *)rn->info; + /* Check oper status of the SVI. */ + if (!tmp_if || !if_is_operative(tmp_if)) + continue; + zif = tmp_if->info; + + if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) + continue; + + if (zif->link == svi_if) { + found = 1; + break; + } + } + + return found ? tmp_if : NULL; +} + +/* + * Install MAC hash entry - called upon access VLAN change. + */ +void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_mac_t *mac; + struct mac_walk_ctx *wctx = ctxt; + + mac = (zebra_mac_t *)bucket->data; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) + zebra_evpn_rem_mac_install(wctx->zevpn, mac, false); +} + +/* + * Read and populate local MACs and neighbors corresponding to this EVPN. + */ +void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp) +{ + struct zebra_ns *zns; + struct zebra_if *zif; + struct interface *vlan_if; + struct zebra_l2info_vxlan *vxl; + struct interface *vrr_if; + + zif = ifp->info; + vxl = &zif->l2info.vxl; + zns = zebra_ns_lookup(NS_DEFAULT); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u", + ifp->name, ifp->ifindex, zevpn->vni, + zif->brslave_info.bridge_ifindex); + + macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if); + vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); + if (vlan_if) { + + /* Add SVI MAC-IP */ + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); + + /* Add VRR MAC-IP - if any*/ + vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); + if (vrr_if) + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); + + neigh_read_for_vlan(zns, vlan_if); + } +} + +/* + * Hash function for EVPN. + */ +unsigned int zebra_evpn_hash_keymake(const void *p) +{ + const zebra_evpn_t *zevpn = p; + + return (jhash_1word(zevpn->vni, 0)); +} + +/* + * Compare 2 evpn hash entries. + */ +bool zebra_evpn_hash_cmp(const void *p1, const void *p2) +{ + const zebra_evpn_t *zevpn1 = p1; + const zebra_evpn_t *zevpn2 = p2; + + return (zevpn1->vni == zevpn2->vni); +} + +int zebra_evpn_list_cmp(void *p1, void *p2) +{ + const zebra_evpn_t *zevpn1 = p1; + const zebra_evpn_t *zevpn2 = p2; + + if (zevpn1->vni == zevpn2->vni) + return 0; + return (zevpn1->vni < zevpn2->vni) ? -1 : 1; +} + +/* + * Callback to allocate VNI hash entry. + */ +void *zebra_evpn_alloc(void *p) +{ + const zebra_evpn_t *tmp_vni = p; + zebra_evpn_t *zevpn; + + zevpn = XCALLOC(MTYPE_ZEVPN, sizeof(zebra_evpn_t)); + zevpn->vni = tmp_vni->vni; + return ((void *)zevpn); +} + +/* + * Look up EVPN hash entry. + */ +zebra_evpn_t *zebra_evpn_lookup(vni_t vni) +{ + struct zebra_vrf *zvrf; + zebra_evpn_t tmp_vni; + zebra_evpn_t *zevpn = NULL; + + zvrf = zebra_vrf_get_evpn(); + assert(zvrf); + memset(&tmp_vni, 0, sizeof(zebra_evpn_t)); + tmp_vni.vni = vni; + zevpn = hash_lookup(zvrf->evpn_table, &tmp_vni); + + return zevpn; +} + +/* + * Add EVPN hash entry. + */ +zebra_evpn_t *zebra_evpn_add(vni_t vni) +{ + struct zebra_vrf *zvrf; + zebra_evpn_t tmp_zevpn; + zebra_evpn_t *zevpn = NULL; + + zvrf = zebra_vrf_get_evpn(); + assert(zvrf); + memset(&tmp_zevpn, 0, sizeof(zebra_evpn_t)); + tmp_zevpn.vni = vni; + zevpn = hash_get(zvrf->evpn_table, &tmp_zevpn, zebra_evpn_alloc); + assert(zevpn); + + zebra_evpn_evpn_es_init(zevpn); + + /* Create hash table for MAC */ + zevpn->mac_table = zebra_mac_db_create("Zebra EVPN MAC Table"); + + /* Create hash table for neighbors */ + zevpn->neigh_table = zebra_neigh_db_create("Zebra EVPN Neighbor Table"); + + return zevpn; +} + +/* + * Delete EVPN hash entry. + */ +int zebra_evpn_del(zebra_evpn_t *zevpn) +{ + struct zebra_vrf *zvrf; + zebra_evpn_t *tmp_zevpn; + + zvrf = zebra_vrf_get_evpn(); + assert(zvrf); + + /* Free the neighbor hash table. */ + hash_free(zevpn->neigh_table); + zevpn->neigh_table = NULL; + + /* Free the MAC hash table. */ + hash_free(zevpn->mac_table); + zevpn->mac_table = NULL; + + zebra_evpn_evpn_es_cleanup(zevpn); + + /* Free the EVPN hash entry and allocated memory. */ + tmp_zevpn = hash_release(zvrf->evpn_table, zevpn); + XFREE(MTYPE_ZEVPN, tmp_zevpn); + + return 0; +} + +/* + * Inform BGP about local EVPN addition. + */ +int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn) +{ + struct zserv *client; + struct stream *s; + int rc; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); + stream_putl(s, zevpn->vni); + stream_put_in_addr(s, &zevpn->local_vtep_ip); + stream_put(s, &zevpn->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ + stream_put_in_addr(s, &zevpn->mcast_grp); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send EVPN_ADD %u %s tenant vrf %s to %s", zevpn->vni, + inet_ntoa(zevpn->local_vtep_ip), + vrf_id_to_name(zevpn->vrf_id), + zebra_route_string(client->proto)); + + client->vniadd_cnt++; + rc = zserv_send_message(client, s); + + if (!(zevpn->flags & ZEVPN_READY_FOR_BGP)) { + zevpn->flags |= ZEVPN_READY_FOR_BGP; + /* once the EVPN is sent the ES-EVIs can also be replayed + * to BGP + */ + zebra_evpn_update_all_es(zevpn); + } + return rc; +} + +/* + * Inform BGP about local EVPN deletion. + */ +int zebra_evpn_send_del_to_client(zebra_evpn_t *zevpn) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + if (zevpn->flags & ZEVPN_READY_FOR_BGP) { + zevpn->flags &= ~ZEVPN_READY_FOR_BGP; + /* the ES-EVIs must be removed from BGP before the EVPN is */ + zebra_evpn_update_all_es(zevpn); + } + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id()); + stream_putl(s, zevpn->vni); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Send EVPN_DEL %u to %s", zevpn->vni, + zebra_route_string(client->proto)); + + client->vnidel_cnt++; + return zserv_send_message(client, s); +} + +/* + * See if remote VTEP matches with prefix. + */ +static int zebra_evpn_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep) +{ + return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip)); +} + +/* + * Locate remote VTEP in EVPN hash table. + */ +zebra_vtep_t *zebra_evpn_vtep_find(zebra_evpn_t *zevpn, struct in_addr *vtep_ip) +{ + zebra_vtep_t *zvtep; + + if (!zevpn) + return NULL; + + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + if (zebra_evpn_vtep_match(vtep_ip, zvtep)) + break; + } + + return zvtep; +} + +/* + * Add remote VTEP to EVPN hash table. + */ +zebra_vtep_t *zebra_evpn_vtep_add(zebra_evpn_t *zevpn, struct in_addr *vtep_ip, + int flood_control) + +{ + zebra_vtep_t *zvtep; + + zvtep = XCALLOC(MTYPE_ZEVPN_VTEP, sizeof(zebra_vtep_t)); + + zvtep->vtep_ip = *vtep_ip; + zvtep->flood_control = flood_control; + + if (zevpn->vteps) + zevpn->vteps->prev = zvtep; + zvtep->next = zevpn->vteps; + zevpn->vteps = zvtep; + + return zvtep; +} + +/* + * Remove remote VTEP from EVPN hash table. + */ +int zebra_evpn_vtep_del(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep) +{ + if (zvtep->next) + zvtep->next->prev = zvtep->prev; + if (zvtep->prev) + zvtep->prev->next = zvtep->next; + else + zevpn->vteps = zvtep->next; + + zvtep->prev = zvtep->next = NULL; + XFREE(MTYPE_ZEVPN_VTEP, zvtep); + + return 0; +} + +/* + * Delete all remote VTEPs for this EVPN (upon VNI delete). Also + * uninstall from kernel if asked to. + */ +int zebra_evpn_vtep_del_all(zebra_evpn_t *zevpn, int uninstall) +{ + zebra_vtep_t *zvtep, *zvtep_next; + + if (!zevpn) + return -1; + + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep_next) { + zvtep_next = zvtep->next; + if (uninstall) + zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip); + zebra_evpn_vtep_del(zevpn, zvtep); + } + + return 0; +} + +/* + * Install remote VTEP into the kernel if the remote VTEP has asked + * for head-end-replication. + */ +int zebra_evpn_vtep_install(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep) +{ + if (is_vxlan_flooding_head_end() && + (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) { + if (ZEBRA_DPLANE_REQUEST_FAILURE == + dplane_vtep_add(zevpn->vxlan_if, + &zvtep->vtep_ip, zevpn->vni)) + return -1; + } + + return 0; +} + +/* + * Uninstall remote VTEP from the kernel. + */ +int zebra_evpn_vtep_uninstall(zebra_evpn_t *zevpn, struct in_addr *vtep_ip) +{ + if (!zevpn->vxlan_if) { + zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", + zevpn->vni, zevpn); + return -1; + } + + if (ZEBRA_DPLANE_REQUEST_FAILURE == + dplane_vtep_delete(zevpn->vxlan_if, vtep_ip, zevpn->vni)) + return -1; + + return 0; +} + +/* + * Install or uninstall flood entries in the kernel corresponding to + * remote VTEPs. This is invoked upon change to BUM handling. + */ +void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket, + void *zvrf) +{ + zebra_evpn_t *zevpn; + zebra_vtep_t *zvtep; + + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) + return; + + for (zvtep = zevpn->vteps; zvtep; zvtep = zvtep->next) { + if (is_vxlan_flooding_head_end()) + zebra_evpn_vtep_install(zevpn, zvtep); + else + zebra_evpn_vtep_uninstall(zevpn, &zvtep->vtep_ip); + } +} + +/* + * Cleanup EVPN/VTEP and update kernel + */ +void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg) +{ + zebra_evpn_t *zevpn = NULL; + + zevpn = (zebra_evpn_t *)bucket->data; + + /* Free up all neighbors and MACs, if any. */ + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + + /* Free up all remote VTEPs, if any. */ + zebra_evpn_vtep_del_all(zevpn, 1); + + /* Delete the hash entry. */ + zebra_evpn_del(zevpn); +} + +static void +zebra_evpn_process_sync_macip_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi) +{ + struct sync_mac_ip_ctx ctx; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool sticky; + bool remote_gw; + zebra_neigh_t *n = NULL; + + sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + /* if sticky or remote-gw ignore updates from the peer */ + if (sticky || remote_gw) { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH + || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "Ignore sync-macip vni %u mac %s%s%s%s%s", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + sticky ? " sticky" : "", + remote_gw ? " remote_gw" : ""); + return; + } + + if (ipa_len) { + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + if (n + && !zebra_evpn_neigh_is_bgp_seq_ok(zevpn, n, macaddr, seq)) + return; + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.mac = zebra_evpn_proc_sync_mac_update( + zevpn, macaddr, ipa_len, ipaddr, flags, seq, esi, &ctx); + if (ctx.ignore_macip || !ctx.mac || !ipa_len) + return; + + zebra_evpn_proc_sync_neigh_update(zevpn, n, ipa_len, ipaddr, flags, seq, + esi, &ctx); +} + +/************************** remote mac-ip handling **************************/ +/* Process a remote MACIP add from BGP. */ +void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, + struct in_addr vtep_ip, esi_t *esi) +{ + zebra_evpn_t *zevpn; + zebra_vtep_t *zvtep; + zebra_mac_t *mac = NULL; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; + struct zebra_vrf *zvrf; + + /* Locate EVPN hash entry - expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni); + return; + } + + ifp = zevpn->vxlan_if; + if (ifp) + zif = ifp->info; + if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) { + zlog_warn( + "Ignoring remote MACIP ADD VNI %u, invalid interface state or info", + vni); + return; + } + + /* Type-2 routes from another PE can be interpreted as remote or + * SYNC based on the destination ES - + * SYNC - if ES is local + * REMOTE - if ES is not local + */ + if (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) { + zebra_evpn_process_sync_macip_add(zevpn, macaddr, ipa_len, + ipaddr, flags, seq, esi); + return; + } + + /* The remote VTEP specified should normally exist, but it is + * possible that when peering comes up, peer may advertise MACIP + * routes before advertising type-3 routes. + */ + if (vtep_ip.s_addr) { + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); + if (!zvtep) { + zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip, + VXLAN_FLOOD_DISABLED); + if (!zvtep) { + flog_err( + EC_ZEBRA_VTEP_ADD_FAILED, + "Failed to add remote VTEP, VNI %u zevpn %p upon remote MACIP ADD", + vni, zevpn); + return; + } + + zebra_evpn_vtep_install(zevpn, zvtep); + } + } + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + if (!zvrf) + return; + + + if (process_mac_remote_macip_add(zevpn, zvrf, macaddr, ipa_len, ipaddr, + &mac, vtep_ip, flags, seq, esi) + != 0) + return; + + process_neigh_remote_macip_add(zevpn, zvrf, ipaddr, mac, vtep_ip, flags, + seq); +} + +/* Process a remote MACIP delete from BGP. */ +void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + struct in_addr vtep_ip) +{ + zebra_evpn_t *zevpn; + zebra_mac_t *mac = NULL; + zebra_neigh_t *n = NULL; + struct interface *ifp = NULL; + struct zebra_if *zif = NULL; + struct zebra_ns *zns; + struct zebra_l2info_vxlan *vxl; + struct zebra_vrf *zvrf; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + + /* Locate EVPN hash entry - expected to exist. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni); + return; + } + + ifp = zevpn->vxlan_if; + if (ifp) + zif = ifp->info; + if (!ifp || !if_is_operative(ifp) || !zif || !zif->brslave_info.br_if) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignoring remote MACIP DEL VNI %u, invalid interface state or info", + vni); + return; + } + zns = zebra_ns_lookup(NS_DEFAULT); + vxl = &zif->l2info.vxl; + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (ipa_len) + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + + if (n && !mac) { + zlog_warn( + "Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni); + return; + } + + /* If the remote mac or neighbor doesn't exist there is nothing + * more to do. Otherwise, uninstall the entry and then remove it. + */ + if (!mac && !n) + return; + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + + /* Ignore the delete if this mac is a gateway mac-ip */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) { + zlog_warn( + "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", + vni, prefix_mac2str(macaddr, buf, sizeof(buf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); + return; + } + + /* Uninstall remote neighbor or MAC. */ + if (n) + zebra_evpn_neigh_remote_uninstall(zevpn, zvrf, n, mac, ipaddr); + else { + /* DAD: when MAC is freeze state as remote learn event, + * remote mac-ip delete event is received will result in freeze + * entry removal, first fetch kernel for the same entry present + * as LOCAL and reachable, avoid deleting this entry instead + * use kerenel local entry to update during unfreeze time. + */ + if (zvrf->dad_freeze + && CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) + && CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry", + __func__, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + mac->flags); + macfdb_read_specific_mac(zns, zif->brslave_info.br_if, + macaddr, vxl->access_vlan); + } + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + if (!ipa_len) + zebra_evpn_sync_mac_del(mac); + } else if (CHECK_FLAG(mac->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_mac_del(zevpn, mac); + } + } +} + +/************************** EVPN BGP config management ************************/ +void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt) +{ + zebra_evpn_t *zevpn = NULL; + + zevpn = (zebra_evpn_t *)bucket->data; + zevpn->advertise_gw_macip = 0; + zevpn->advertise_svi_macip = 0; + zevpn->advertise_subnet = 0; + + zebra_evpn_neigh_del_all(zevpn, 1, 0, + DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP); + zebra_evpn_mac_del_all(zevpn, 1, 0, + DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP); + zebra_evpn_vtep_del_all(zevpn, 1); +} diff --git a/zebra/zebra_evpn.h b/zebra/zebra_evpn.h new file mode 100644 index 0000000000..3b6a5b21e8 --- /dev/null +++ b/zebra/zebra_evpn.h @@ -0,0 +1,208 @@ +/* + * Zebra EVPN Data structures and definitions + * These are "internal" to this function. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * Copyright (C) 2020 Volta Networks. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_EVPN_H +#define _ZEBRA_EVPN_H + +#include <zebra.h> + +#include "if.h" +#include "linklist.h" +#include "bitfield.h" + +#include "zebra/zebra_l2.h" +#include "zebra/interface.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zebra_evpn_t_ zebra_evpn_t; +typedef struct zebra_vtep_t_ zebra_vtep_t; + +RB_HEAD(zebra_es_evi_rb_head, zebra_evpn_es_evi); +RB_PROTOTYPE(zebra_es_evi_rb_head, zebra_evpn_es_evi, rb_node, + zebra_es_evi_rb_cmp); + +/* Private Structure to pass callback data for hash iterator */ +struct zebra_evpn_show { + struct vty *vty; + json_object *json; + struct zebra_vrf *zvrf; + bool use_json; +}; + +/* + * VTEP info + * + * Right now, this just has each remote VTEP's IP address. + */ +struct zebra_vtep_t_ { + /* Remote IP. */ + /* NOTE: Can only be IPv4 right now. */ + struct in_addr vtep_ip; + /* Flood mode (one of enum vxlan_flood_control) based on the PMSI + * tunnel type advertised by the remote VTEP + */ + int flood_control; + + /* Links. */ + struct zebra_vtep_t_ *next; + struct zebra_vtep_t_ *prev; +}; + +/* + * VNI hash table + * + * Contains information pertaining to a VNI: + * - the list of remote VTEPs (with this VNI) + */ +struct zebra_evpn_t_ { + /* VNI - key */ + vni_t vni; + + /* ES flags */ + uint32_t flags; +#define ZEVPN_READY_FOR_BGP (1 << 0) /* ready to be sent to BGP */ + + /* Flag for advertising gw macip */ + uint8_t advertise_gw_macip; + + /* Flag for advertising svi macip */ + uint8_t advertise_svi_macip; + + /* Flag for advertising gw macip */ + uint8_t advertise_subnet; + + /* Corresponding VxLAN interface. */ + struct interface *vxlan_if; + + /* List of remote VTEPs */ + zebra_vtep_t *vteps; + + /* Local IP */ + struct in_addr local_vtep_ip; + + /* PIM-SM MDT group for BUM flooding */ + struct in_addr mcast_grp; + + /* tenant VRF, if any */ + vrf_id_t vrf_id; + + /* List of local or remote MAC */ + struct hash *mac_table; + + /* List of local or remote neighbors (MAC+IP) */ + struct hash *neigh_table; + + /* RB tree of ES-EVIs */ + struct zebra_es_evi_rb_head es_evi_rb_tree; + + /* List of local ESs */ + struct list *local_es_evi_list; +}; + +struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); + +static inline struct interface *zevpn_map_to_svi(zebra_evpn_t *zevpn) +{ + struct interface *ifp; + struct zebra_if *zif = NULL; + struct zebra_l2info_vxlan zl2_info; + + ifp = zevpn->vxlan_if; + if (!ifp) + return NULL; + zif = ifp->info; + if (!zif) + return NULL; + + /* If down or not mapped to a bridge, we're done. */ + if (!if_is_operative(ifp) || !zif->brslave_info.br_if) + return NULL; + zl2_info = zif->l2info.vxl; + return zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); +} + +int advertise_gw_macip_enabled(zebra_evpn_t *zevpn); +int advertise_svi_macip_enabled(zebra_evpn_t *zevpn); +void zebra_evpn_print(zebra_evpn_t *zevpn, void **ctxt); +void zebra_evpn_print_hash(struct hash_bucket *bucket, void *ctxt[]); +void zebra_evpn_print_hash_detail(struct hash_bucket *bucket, void *data); +int zebra_evpn_add_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn); +int zebra_evpn_del_macip_for_intf(struct interface *ifp, zebra_evpn_t *zevpn); +int zebra_evpn_advertise_subnet(zebra_evpn_t *zevpn, struct interface *ifp, + int advertise); +int zebra_evpn_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ethaddr *macaddr, struct ipaddr *ip); +int zebra_evpn_gw_macip_del(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip); +void zebra_evpn_gw_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt); +void zebra_evpn_gw_macip_add_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt); +void zebra_evpn_svi_macip_del_for_evpn_hash(struct hash_bucket *bucket, + void *ctxt); +zebra_evpn_t *zebra_evpn_map_vlan(struct interface *ifp, + struct interface *br_if, vlanid_t vid); +zebra_evpn_t *zebra_evpn_from_svi(struct interface *ifp, + struct interface *br_if); +struct interface *zebra_evpn_map_to_macvlan(struct interface *br_if, + struct interface *svi_if); +void zebra_evpn_install_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_read_mac_neigh(zebra_evpn_t *zevpn, struct interface *ifp); +unsigned int zebra_evpn_hash_keymake(const void *p); +bool zebra_evpn_hash_cmp(const void *p1, const void *p2); +int zebra_evpn_list_cmp(void *p1, void *p2); +void *zebra_evpn_alloc(void *p); +zebra_evpn_t *zebra_evpn_lookup(vni_t vni); +zebra_evpn_t *zebra_evpn_add(vni_t vni); +int zebra_evpn_del(zebra_evpn_t *zevpn); +int zebra_evpn_send_add_to_client(zebra_evpn_t *zevpn); +int zebra_evpn_send_del_to_client(zebra_evpn_t *zevpn); +zebra_vtep_t *zebra_evpn_vtep_find(zebra_evpn_t *zevpn, + struct in_addr *vtep_ip); +zebra_vtep_t *zebra_evpn_vtep_add(zebra_evpn_t *zevpn, struct in_addr *vtep_ip, + int flood_control); +int zebra_evpn_vtep_del(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep); +int zebra_evpn_vtep_del_all(zebra_evpn_t *zevpn, int uninstall); +int zebra_evpn_vtep_install(zebra_evpn_t *zevpn, zebra_vtep_t *zvtep); +int zebra_evpn_vtep_uninstall(zebra_evpn_t *zevpn, struct in_addr *vtep_ip); +void zebra_evpn_handle_flooding_remote_vteps(struct hash_bucket *bucket, + void *zvrf); +void zebra_evpn_cleanup_all(struct hash_bucket *bucket, void *arg); +void process_remote_macip_add(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, + struct in_addr vtep_ip, esi_t *esi); +void process_remote_macip_del(vni_t vni, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + struct in_addr vtep_ip); +void zebra_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt); + +#ifdef __cplusplus +} +#endif + +#endif /*_ZEBRA_EVPN_H */ diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c new file mode 100644 index 0000000000..b9cc02a276 --- /dev/null +++ b/zebra/zebra_evpn_mac.c @@ -0,0 +1,2231 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#include "hash.h" +#include "interface.h" +#include "jhash.h" +#include "memory.h" +#include "prefix.h" +#include "vlan.h" +#include "json.h" + +#include "zebra/zserv.h" +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" + +DEFINE_MTYPE_STATIC(ZEBRA, MAC, "EVPN MAC"); + +/* + * Return number of valid MACs in an EVPN's MAC hash table - all + * remote MACs and non-internal (auto) local MACs count. + */ +uint32_t num_valid_macs(zebra_evpn_t *zevpn) +{ + unsigned int i; + uint32_t num_macs = 0; + struct hash *hash; + struct hash_bucket *hb; + zebra_mac_t *mac; + + hash = zevpn->mac_table; + if (!hash) + return num_macs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + mac = (zebra_mac_t *)hb->data; + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + || !CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) + num_macs++; + } + } + + return num_macs; +} + +uint32_t num_dup_detected_macs(zebra_evpn_t *zevpn) +{ + unsigned int i; + uint32_t num_macs = 0; + struct hash *hash; + struct hash_bucket *hb; + zebra_mac_t *mac; + + hash = zevpn->mac_table; + if (!hash) + return num_macs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + mac = (zebra_mac_t *)hb->data; + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + num_macs++; + } + } + + return num_macs; +} + +/* + * Install remote MAC into the forwarding plane. + */ +int zebra_evpn_rem_mac_install(zebra_evpn_t *zevpn, zebra_mac_t *mac, + bool was_static) +{ + const struct zebra_if *zif, *br_zif; + const struct zebra_l2info_vxlan *vxl; + bool sticky; + enum zebra_dplane_result res; + const struct interface *br_ifp; + vlanid_t vid; + uint32_t nhg_id; + struct in_addr vtep_ip; + + if (!(mac->flags & ZEBRA_MAC_REMOTE)) + return 0; + + zif = zevpn->vxlan_if->info; + if (!zif) + return -1; + + br_ifp = zif->brslave_info.br_if; + if (br_ifp == NULL) + return -1; + + vxl = &zif->l2info.vxl; + + sticky = !!CHECK_FLAG(mac->flags, + (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)); + + /* If nexthop group for the FDB entry is inactive (not programmed in + * the dataplane) the MAC entry cannot be installed + */ + if (mac->es) { + if (!(mac->es->flags & ZEBRA_EVPNES_NHG_ACTIVE)) + return -1; + nhg_id = mac->es->nhg_id; + vtep_ip.s_addr = 0; + } else { + nhg_id = 0; + vtep_ip = mac->fwd_info.r_vtep_ip; + } + + br_zif = (const struct zebra_if *)(br_ifp->info); + + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) + vid = vxl->access_vlan; + else + vid = 0; + + res = dplane_rem_mac_add(zevpn->vxlan_if, br_ifp, vid, &mac->macaddr, + vtep_ip, sticky, nhg_id, was_static); + if (res != ZEBRA_DPLANE_REQUEST_FAILURE) + return 0; + else + return -1; +} + +/* + * Uninstall remote MAC from the forwarding plane. + */ +int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + const struct zebra_if *zif, *br_zif; + const struct zebra_l2info_vxlan *vxl; + struct in_addr vtep_ip; + const struct interface *ifp, *br_ifp; + vlanid_t vid; + enum zebra_dplane_result res; + + if (!(mac->flags & ZEBRA_MAC_REMOTE)) + return 0; + + if (!zevpn->vxlan_if) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "VNI %u hash %p couldn't be uninstalled - no intf", + zevpn->vni, zevpn); + return -1; + } + + zif = zevpn->vxlan_if->info; + if (!zif) + return -1; + + br_ifp = zif->brslave_info.br_if; + if (br_ifp == NULL) + return -1; + + vxl = &zif->l2info.vxl; + + br_zif = (const struct zebra_if *)br_ifp->info; + + if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) + vid = vxl->access_vlan; + else + vid = 0; + + ifp = zevpn->vxlan_if; + vtep_ip = mac->fwd_info.r_vtep_ip; + + res = dplane_rem_mac_del(ifp, br_ifp, vid, &mac->macaddr, vtep_ip); + if (res != ZEBRA_DPLANE_REQUEST_FAILURE) + return 0; + else + return -1; +} + +/* + * Decrement neighbor refcount of MAC; uninstall and free it if + * appropriate. + */ +void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) + return; + + /* If all remote neighbors referencing a remote MAC go away, + * we need to uninstall the MAC. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + && remote_neigh_count(mac) == 0) { + zebra_evpn_rem_mac_uninstall(zevpn, mac); + zebra_evpn_es_mac_deref_entry(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + } + + /* If no neighbors, delete the MAC. */ + if (list_isempty(mac->neigh_list)) + zebra_evpn_mac_del(zevpn, mac); +} + +static void zebra_evpn_mac_get_access_info(zebra_mac_t *mac, + struct interface **ifpP, + vlanid_t *vid) +{ + /* if the mac is associated with an ES we must get the access + * info from the ES + */ + if (mac->es) { + struct zebra_if *zif; + + /* get the access port from the es */ + *ifpP = mac->es->zif ? mac->es->zif->ifp : NULL; + /* get the vlan from the EVPN */ + if (mac->zevpn->vxlan_if) { + zif = mac->zevpn->vxlan_if->info; + *vid = zif->l2info.vxl.access_vlan; + } else { + *vid = 0; + } + } else { + struct zebra_ns *zns; + + *vid = mac->fwd_info.local.vid; + zns = zebra_ns_lookup(NS_DEFAULT); + *ifpP = if_lookup_by_index_per_ns(zns, + mac->fwd_info.local.ifindex); + } +} + +static int zebra_evpn_dad_mac_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_mac_t *mac = NULL; + zebra_evpn_t *zevpn = NULL; + struct listnode *node = NULL; + zebra_neigh_t *nbr = NULL; + char buf[ETHER_ADDR_STRLEN]; + + mac = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + zvrf = vrf_info_lookup(mac->zevpn->vrf_id); + if (!zvrf) + return 0; + + zevpn = zebra_evpn_lookup(mac->zevpn->vni); + if (!zevpn) + return 0; + + mac = zebra_evpn_mac_lookup(zevpn, &mac->macaddr); + if (!mac) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired", + __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, mac->dad_count, listcount(mac->neigh_list)); + + /* Remove all IPs as duplicate associcated with this MAC */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + ZEBRA_NEIGH_SET_INACTIVE(nbr); + else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) + zebra_evpn_rem_neigh_install( + zevpn, nbr, false /*was_static*/); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->dad_dup_detect_time = 0; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + mac->dad_count = 0; + mac->detect_start_time.tv_sec = 0; + mac->detect_start_time.tv_usec = 0; + mac->dad_dup_detect_time = 0; + mac->dad_mac_auto_recovery_timer = NULL; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* Inform to BGP */ + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr, + mac->flags, mac->loc_seq, + mac->es)) + return -1; + + /* Process all neighbors associated with this MAC. */ + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + 0 /*es_change*/); + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); + + /* Install the entry. */ + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); + } + + return 0; +} + +static void zebra_evpn_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, + zebra_mac_t *mac, + struct in_addr vtep_ip, + bool do_dad, bool *is_dup_detect, + bool is_local) +{ + zebra_neigh_t *nbr; + struct listnode *node = NULL; + struct timeval elapsed = {0, 0}; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + bool reset_params = false; + + if (!(zvrf->dup_addr_detect && do_dad)) + return; + + /* MAC is detected as duplicate, + * Local MAC event -> hold on advertising to BGP. + * Remote MAC event -> hold on installing it. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u", + __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, mac->dad_count, + zvrf->dad_freeze_time); + + /* For duplicate MAC do not update + * client but update neigh due to + * this MAC update. + */ + if (zvrf->dad_freeze) + *is_dup_detect = true; + + return; + } + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + */ + monotime_since(&mac->detect_start_time, &elapsed); + reset_params = (elapsed.tv_sec > zvrf->dad_time); + if (is_local && !reset_params) { + /* RFC-7432: A PE/VTEP that detects a MAC mobility + * event via LOCAL learning starts an M-second timer. + * + * NOTE: This is the START of the probe with count is + * 0 during LOCAL learn event. + * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time) + */ + reset_params = !mac->dad_count; + } + + if (reset_params) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u", + __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, mac->dad_count); + + mac->dad_count = 0; + /* Start dup. addr detection (DAD) start time, + * ONLY during LOCAL learn. + */ + if (is_local) + monotime(&mac->detect_start_time); + + } else if (!is_local) { + /* For REMOTE MAC, increment detection count + * ONLY while in probe window, once window passed, + * next local learn event should trigger DAD. + */ + mac->dad_count++; + } + + /* For LOCAL MAC learn event, once count is reset above via either + * initial/start detection time or passed the probe time, the count + * needs to be incremented. + */ + if (is_local) + mac->dad_count++; + + if (mac->dad_count >= zvrf->dad_max_moves) { + flog_warn(EC_ZEBRA_DUP_MAC_DETECTED, + "VNI %u: MAC %s detected as duplicate during %s VTEP %s", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + is_local ? "local update, last" : + "remote update, from", inet_ntoa(vtep_ip)); + + SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + + /* Capture Duplicate detection time */ + mac->dad_dup_detect_time = monotime(NULL); + + /* Mark all IPs/Neighs as duplicate + * associcated with this MAC + */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + + /* Ony Mark IPs which are Local */ + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + continue; + + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + + nbr->dad_dup_detect_time = monotime(NULL); + + flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, + buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + is_local ? "local" : "remote"); + } + + /* Start auto recovery timer for this MAC */ + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + if (zvrf->dad_freeze && zvrf->dad_freeze_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start", + __func__, + prefix_mac2str(&mac->macaddr, buf, + sizeof(buf)), + mac->flags, zvrf->dad_freeze_time); + + thread_add_timer(zrouter.master, + zebra_evpn_dad_mac_auto_recovery_exp, + mac, zvrf->dad_freeze_time, + &mac->dad_mac_auto_recovery_timer); + } + + /* In case of local update, do not inform to client (BGPd), + * upd_neigh for neigh sequence change. + */ + if (zvrf->dad_freeze) + *is_dup_detect = true; + } +} + +/* + * Print a specific MAC entry. + */ +void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) +{ + struct vty *vty; + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct zebra_vrf *zvrf; + struct timeval detect_start_time = {0, 0}; + char timebuf[MONOTIME_STRLEN]; + char thread_buf[THREAD_TIMER_STRLEN]; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return; + + vty = (struct vty *)ctxt; + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + if (json) { + json_object *json_mac = json_object_new_object(); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *ifp; + vlanid_t vid; + + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + json_object_string_add(json_mac, "type", "local"); + if (ifp) { + json_object_string_add(json_mac, "intf", + ifp->name); + json_object_int_add(json_mac, "ifindex", + ifp->ifindex); + } + if (vid) + json_object_int_add(json_mac, "vlan", vid); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + json_object_string_add(json_mac, "type", "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) + json_object_string_add(json_mac, "type", "auto"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) + json_object_boolean_true_add(json_mac, "stickyMac"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) + json_object_boolean_true_add(json_mac, + "defaultGateway"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) + json_object_boolean_true_add(json_mac, + "remoteGatewayMac"); + + json_object_int_add(json_mac, "localSequence", mac->loc_seq); + json_object_int_add(json_mac, "remoteSequence", mac->rem_seq); + + json_object_int_add(json_mac, "detectionCount", mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, "isDuplicate"); + else + json_object_boolean_false_add(json_mac, "isDuplicate"); + + json_object_int_add(json_mac, "syncNeighCount", + mac->sync_neigh_cnt); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) + json_object_boolean_true_add(json_mac, "localInactive"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY)) + json_object_boolean_true_add(json_mac, "peerProxy"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + json_object_boolean_true_add(json_mac, "peerActive"); + if (mac->hold_timer) + json_object_string_add( + json_mac, "peerActiveHold", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + mac->hold_timer)); + if (mac->es) + json_object_string_add(json_mac, "esi", + mac->es->esi_str); + /* print all the associated neigh */ + if (!listcount(mac->neigh_list)) + json_object_string_add(json_mac, "neighbors", "none"); + else { + json_object *json_active_nbrs = json_object_new_array(); + json_object *json_inactive_nbrs = + json_object_new_array(); + json_object *json_nbrs = json_object_new_object(); + + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) + json_object_array_add( + json_active_nbrs, + json_object_new_string( + ipaddr2str( + &n->ip, buf2, + sizeof(buf2)))); + else + json_object_array_add( + json_inactive_nbrs, + json_object_new_string( + ipaddr2str( + &n->ip, buf2, + sizeof(buf2)))); + } + + json_object_object_add(json_nbrs, "active", + json_active_nbrs); + json_object_object_add(json_nbrs, "inactive", + json_inactive_nbrs); + json_object_object_add(json_mac, "neighbors", + json_nbrs); + } + + json_object_object_add(json, buf1, json_mac); + } else { + vty_out(vty, "MAC: %s\n", buf1); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *ifp; + vlanid_t vid; + + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + + if (mac->es) + vty_out(vty, " ESI: %s\n", mac->es->esi_str); + + if (ifp) + vty_out(vty, " Intf: %s(%u)", ifp->name, + ifp->ifindex); + else + vty_out(vty, " Intf: -"); + vty_out(vty, " VLAN: %u", vid); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + if (mac->es) + vty_out(vty, " Remote ES: %s", + mac->es->esi_str); + else + vty_out(vty, " Remote VTEP: %s", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { + vty_out(vty, " Auto Mac "); + } + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) + vty_out(vty, " Sticky Mac "); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) + vty_out(vty, " Default-gateway Mac "); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) + vty_out(vty, " Remote-gateway Mac "); + + vty_out(vty, "\n"); + vty_out(vty, " Sync-info: neigh#: %u", mac->sync_neigh_cnt); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) + vty_out(vty, " local-inactive"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY)) + vty_out(vty, " peer-proxy"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + vty_out(vty, " peer-active"); + if (mac->hold_timer) + vty_out(vty, " (ht: %s)", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + mac->hold_timer)); + vty_out(vty, "\n"); + vty_out(vty, " Local Seq: %u Remote Seq: %u", mac->loc_seq, + mac->rem_seq); + vty_out(vty, "\n"); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + vty_out(vty, " Duplicate, detected at %s", + time_to_string(mac->dad_dup_detect_time, + timebuf)); + } else if (mac->dad_count) { + monotime_since(&mac->detect_start_time, + &detect_start_time); + if (detect_start_time.tv_sec <= zvrf->dad_time) { + time_to_string(mac->detect_start_time.tv_sec, + timebuf); + vty_out(vty, + " Duplicate detection started at %s, detection count %u\n", + timebuf, mac->dad_count); + } + } + + /* print all the associated neigh */ + vty_out(vty, " Neighbors:\n"); + if (!listcount(mac->neigh_list)) + vty_out(vty, " No Neighbors\n"); + else { + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { + vty_out(vty, " %s %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2)), + (IS_ZEBRA_NEIGH_ACTIVE(n) + ? "Active" + : "Inactive")); + } + } + + vty_out(vty, "\n"); + } +} + +static char *zebra_evpn_print_mac_flags(zebra_mac_t *mac, char *flags_buf, + uint32_t flags_buf_sz) +{ + snprintf(flags_buf, flags_buf_sz, "%s%s%s%s", + mac->sync_neigh_cnt ? + "N" : "", + (mac->flags & ZEBRA_MAC_ES_PEER_ACTIVE) ? + "P" : "", + (mac->flags & ZEBRA_MAC_ES_PEER_PROXY) ? + "X" : "", + (mac->flags & ZEBRA_MAC_LOCAL_INACTIVE) ? + "I" : ""); + + return flags_buf; +} + +/* + * Print MAC hash entry - called for display of all MACs. + */ +void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_mac_hdr = NULL, *json_mac = NULL; + zebra_mac_t *mac; + char buf1[ETHER_ADDR_STRLEN]; + struct mac_walk_ctx *wctx = ctxt; + char flags_buf[6]; + + vty = wctx->vty; + json_mac_hdr = wctx->json; + mac = (zebra_mac_t *)bucket->data; + + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + if (json_mac_hdr) + json_mac = json_object_new_object(); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *ifp; + vlanid_t vid; + + if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) + return; + + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + if (json_mac_hdr == NULL) { + vty_out(vty, "%-17s %-6s %-5s %-30s", buf1, "local", + zebra_evpn_print_mac_flags(mac, flags_buf, + sizeof(flags_buf)), + ifp ? ifp->name : "-"); + } else { + json_object_string_add(json_mac, "type", "local"); + if (ifp) + json_object_string_add(json_mac, "intf", + ifp->name); + } + if (vid) { + if (json_mac_hdr == NULL) + vty_out(vty, " %-5u", vid); + else + json_object_int_add(json_mac, "vlan", vid); + } else /* No vid? fill out the space */ + if (json_mac_hdr == NULL) + vty_out(vty, " %-5s", ""); + if (json_mac_hdr == NULL) { + vty_out(vty, " %u/%u", mac->loc_seq, mac->rem_seq); + vty_out(vty, "\n"); + } else { + json_object_int_add(json_mac, "localSequence", + mac->loc_seq); + json_object_int_add(json_mac, "remoteSequence", + mac->rem_seq); + json_object_int_add(json_mac, "detectionCount", + mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, + "isDuplicate"); + else + json_object_boolean_false_add(json_mac, + "isDuplicate"); + json_object_object_add(json_mac_hdr, buf1, json_mac); + } + + wctx->count++; + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + + if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) + && !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, + &wctx->r_vtep_ip)) + return; + + if (json_mac_hdr == NULL) { + if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) + && (wctx->count == 0)) { + vty_out(vty, "\nVNI %u\n\n", wctx->zevpn->vni); + vty_out(vty, "%-17s %-6s %-5s%-30s %-5s %s\n", + "MAC", "Type", "Flags", + "Intf/Remote ES/VTEP", "VLAN", + "Seq #'s"); + } + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %u/%u\n", buf1, + "remote", + zebra_evpn_print_mac_flags(mac, flags_buf, + sizeof(flags_buf)), + mac->es ? mac->es->esi_str + : inet_ntoa(mac->fwd_info.r_vtep_ip), + "", mac->loc_seq, mac->rem_seq); + } else { + json_object_string_add(json_mac, "type", "remote"); + json_object_string_add( + json_mac, "remoteVtep", + inet_ntoa(mac->fwd_info.r_vtep_ip)); + json_object_object_add(json_mac_hdr, buf1, json_mac); + json_object_int_add(json_mac, "localSequence", + mac->loc_seq); + json_object_int_add(json_mac, "remoteSequence", + mac->rem_seq); + json_object_int_add(json_mac, "detectionCount", + mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, + "isDuplicate"); + else + json_object_boolean_false_add(json_mac, + "isDuplicate"); + } + + wctx->count++; + } +} + +/* + * Print MAC hash entry in detail - called for display of all MACs. + */ +void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_mac_hdr = NULL; + zebra_mac_t *mac; + struct mac_walk_ctx *wctx = ctxt; + char buf1[ETHER_ADDR_STRLEN]; + + vty = wctx->vty; + json_mac_hdr = wctx->json; + mac = (zebra_mac_t *)bucket->data; + if (!mac) + return; + + wctx->count++; + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + zebra_evpn_print_mac(mac, vty, json_mac_hdr); +} + +/* + * Inform BGP about local MACIP. + */ +int zebra_evpn_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, + struct ipaddr *ip, uint8_t flags, + uint32_t seq, int state, + struct zebra_evpn_es *es, uint16_t cmd) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + int ipa_len; + struct zserv *client = NULL; + struct stream *s = NULL; + esi_t *esi = es ? &es->esi : zero_esi; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, cmd, zebra_vrf_get_evpn_id()); + stream_putl(s, vni); + stream_put(s, macaddr->octet, ETH_ALEN); + if (ip) { + ipa_len = 0; + if (IS_IPADDR_V4(ip)) + ipa_len = IPV4_MAX_BYTELEN; + else if (IS_IPADDR_V6(ip)) + ipa_len = IPV6_MAX_BYTELEN; + + stream_putl(s, ipa_len); /* IP address length */ + if (ipa_len) + stream_put(s, &ip->ip.addr, ipa_len); /* IP address */ + } else + stream_putl(s, 0); /* Just MAC. */ + + if (cmd == ZEBRA_MACIP_ADD) { + stream_putc(s, flags); /* sticky mac/gateway mac */ + stream_putl(s, seq); /* sequence number */ + stream_put(s, esi, sizeof(esi_t)); + } else { + stream_putl(s, state); /* state - active/inactive */ + } + + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Send MACIP %s f 0x%x MAC %s IP %s seq %u L2-VNI %u ESI %s to %s", + (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni, + es ? es->esi_str : "-", + zebra_route_string(client->proto)); + + if (cmd == ZEBRA_MACIP_ADD) + client->macipadd_cnt++; + else + client->macipdel_cnt++; + + return zserv_send_message(client, s); +} + +static unsigned int mac_hash_keymake(const void *p) +{ + const zebra_mac_t *pmac = p; + const void *pnt = (void *)pmac->macaddr.octet; + + return jhash(pnt, ETH_ALEN, 0xa5a5a55a); +} + +/* + * Compare two MAC addresses. + */ +static bool mac_cmp(const void *p1, const void *p2) +{ + const zebra_mac_t *pmac1 = p1; + const zebra_mac_t *pmac2 = p2; + + if (pmac1 == NULL && pmac2 == NULL) + return true; + + if (pmac1 == NULL || pmac2 == NULL) + return false; + + return (memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETH_ALEN) + == 0); +} + +/* + * Callback to allocate MAC hash entry. + */ +static void *zebra_evpn_mac_alloc(void *p) +{ + const zebra_mac_t *tmp_mac = p; + zebra_mac_t *mac; + + mac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); + *mac = *tmp_mac; + + return ((void *)mac); +} + +/* + * Add MAC entry. + */ +zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevpn, struct ethaddr *macaddr) +{ + zebra_mac_t tmp_mac; + zebra_mac_t *mac = NULL; + + memset(&tmp_mac, 0, sizeof(zebra_mac_t)); + memcpy(&tmp_mac.macaddr, macaddr, ETH_ALEN); + mac = hash_get(zevpn->mac_table, &tmp_mac, zebra_evpn_mac_alloc); + assert(mac); + + mac->zevpn = zevpn; + mac->dad_mac_auto_recovery_timer = NULL; + + mac->neigh_list = list_new(); + mac->neigh_list->cmp = neigh_list_cmp; + + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) { + char buf[ETHER_ADDR_STRLEN]; + + zlog_debug("%s: MAC %s flags 0x%x", __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags); + } + return mac; +} + +/* + * Delete MAC entry. + */ +int zebra_evpn_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + zebra_mac_t *tmp_mac; + + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) { + char buf[ETHER_ADDR_STRLEN]; + + zlog_debug("%s: MAC %s flags 0x%x", __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags); + } + + /* force de-ref any ES entry linked to the MAC */ + zebra_evpn_es_mac_deref_entry(mac); + + /* Cancel proxy hold timer */ + zebra_evpn_mac_stop_hold_timer(mac); + + /* Cancel auto recovery */ + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + + list_delete(&mac->neigh_list); + + /* Free the VNI hash entry and allocated memory. */ + tmp_mac = hash_release(zevpn->mac_table, mac); + XFREE(MTYPE_MAC, tmp_mac); + + return 0; +} + +static bool zebra_evpn_check_mac_del_from_db(struct mac_walk_ctx *wctx, + zebra_mac_t *mac) +{ + if ((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL)) + return true; + else if ((wctx->flags & DEL_REMOTE_MAC) + && (mac->flags & ZEBRA_MAC_REMOTE)) + return true; + else if ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) + && (mac->flags & ZEBRA_MAC_REMOTE) + && IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)) + return true; + else if ((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_AUTO) + && !listcount(mac->neigh_list)) { + if (IS_ZEBRA_DEBUG_VXLAN) { + char buf[ETHER_ADDR_STRLEN]; + + zlog_debug( + "%s: Del MAC %s flags 0x%x", __func__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags); + } + wctx->uninstall = 0; + + return true; + } + + return false; +} + +/* + * Free MAC hash entry (callback) + */ +static void zebra_evpn_mac_del_hash_entry(struct hash_bucket *bucket, void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_mac_t *mac = bucket->data; + + if (zebra_evpn_check_mac_del_from_db(wctx, mac)) { + if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) { + zebra_evpn_mac_send_del_to_client(wctx->zevpn->vni, + &mac->macaddr, + mac->flags, false); + } + if (wctx->uninstall) { + if (zebra_evpn_mac_is_static(mac)) + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + true /* force_clear_static */, + __func__); + + if (mac->flags & ZEBRA_MAC_REMOTE) + zebra_evpn_rem_mac_uninstall(wctx->zevpn, mac); + } + + zebra_evpn_mac_del(wctx->zevpn, mac); + } + + return; +} + +/* + * Delete all MAC entries for this EVPN. + */ +void zebra_evpn_mac_del_all(zebra_evpn_t *zevpn, int uninstall, int upd_client, + uint32_t flags) +{ + struct mac_walk_ctx wctx; + + if (!zevpn->mac_table) + return; + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zevpn = zevpn; + wctx.uninstall = uninstall; + wctx.upd_client = upd_client; + wctx.flags = flags; + + hash_iterate(zevpn->mac_table, zebra_evpn_mac_del_hash_entry, &wctx); +} + +/* + * Look up MAC hash entry. + */ +zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevpn, struct ethaddr *mac) +{ + zebra_mac_t tmp; + zebra_mac_t *pmac; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.macaddr, mac, ETH_ALEN); + pmac = hash_lookup(zevpn->mac_table, &tmp); + + return pmac; +} + +/* + * Inform BGP about local MAC addition. + */ +int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t mac_flags, uint32_t seq, + struct zebra_evpn_es *es) +{ + uint8_t flags = 0; + + if (CHECK_FLAG(mac_flags, ZEBRA_MAC_LOCAL_INACTIVE)) { + /* host reachability has not been verified locally */ + + /* if no ES peer is claiming reachability we can't advertise the + * entry + */ + if (!CHECK_FLAG(mac_flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + return 0; + + /* ES peers are claiming reachability; we will + * advertise the entry but with a proxy flag + */ + SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT); + } + + if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + + return zebra_evpn_macip_send_msg_to_client(vni, macaddr, NULL, flags, + seq, ZEBRA_NEIGH_ACTIVE, es, + ZEBRA_MACIP_ADD); +} + +/* + * Inform BGP about local MAC deletion. + */ +int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t flags, bool force) +{ + if (!force) { + if (CHECK_FLAG(flags, ZEBRA_MAC_LOCAL_INACTIVE) + && !CHECK_FLAG(flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + /* the host was not advertised - nothing to delete */ + return 0; + } + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, NULL, 0 /* flags */, 0 /* seq */, + ZEBRA_NEIGH_ACTIVE, NULL, ZEBRA_MACIP_DEL); +} + +/* + * wrapper to create a MAC hash table + */ +struct hash *zebra_mac_db_create(const char *desc) +{ + return hash_create(mac_hash_keymake, mac_cmp, desc); +} + +/* program sync mac flags in the dataplane */ +void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, const char *caller) +{ + char macbuf[ETHER_ADDR_STRLEN]; + struct interface *ifp; + bool sticky; + bool set_static; + zebra_evpn_t *zevpn = mac->zevpn; + vlanid_t vid; + struct zebra_if *zif; + struct interface *br_ifp; + + /* get the access vlan from the vxlan_device */ + zebra_evpn_mac_get_access_info(mac, &ifp, &vid); + + if (!ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no access-port", + caller, zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags, + set_inactive ? "inactive " : ""); + return; + } + + zif = ifp->info; + br_ifp = zif->brslave_info.br_if; + if (!br_ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "%s: dp-install sync-mac vni %u mac %s es %s 0x%x %sskipped, no br", + caller, zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags, + set_inactive ? "inactive " : ""); + return; + } + + sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY); + if (force_clear_static) + set_static = false; + else + set_static = zebra_evpn_mac_is_static(mac); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "dp-install sync-mac vni %u mac %s es %s 0x%x %s%s", + zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags, + set_static ? "static " : "", + set_inactive ? "inactive " : ""); + + dplane_local_mac_add(ifp, br_ifp, vid, &mac->macaddr, sticky, + set_static, set_inactive); +} + +void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, + bool new_bgp_ready) +{ + if (new_bgp_ready) + zebra_evpn_mac_send_add_to_client(mac->zevpn->vni, + &mac->macaddr, mac->flags, + mac->loc_seq, mac->es); + else if (old_bgp_ready) + zebra_evpn_mac_send_del_to_client(mac->zevpn->vni, + &mac->macaddr, mac->flags, + true /* force */); +} + +/* MAC hold timer is used to age out peer-active flag. + * + * During this wait time we expect the dataplane component or an + * external neighmgr daemon to probe existing hosts to independently + * establish their presence on the ES. + */ +static int zebra_evpn_mac_hold_exp_cb(struct thread *t) +{ + zebra_mac_t *mac; + bool old_bgp_ready; + bool new_bgp_ready; + bool old_static; + bool new_static; + char macbuf[ETHER_ADDR_STRLEN]; + + mac = THREAD_ARG(t); + /* the purpose of the hold timer is to age out the peer-active + * flag + */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + return 0; + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + old_static = zebra_evpn_mac_is_static(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE); + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + new_static = zebra_evpn_mac_is_static(mac); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac vni %u mac %s es %s 0x%x hold expired", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags); + + /* re-program the local mac in the dataplane if the mac is no + * longer static + */ + if (old_static != new_static) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* inform bgp if needed */ + if (old_bgp_ready != new_bgp_ready) + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + new_bgp_ready); + + return 0; +} + +static inline void zebra_evpn_mac_start_hold_timer(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + + if (mac->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac vni %u mac %s es %s 0x%x hold started", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags); + thread_add_timer(zrouter.master, zebra_evpn_mac_hold_exp_cb, mac, + zmh_info->mac_hold_time, &mac->hold_timer); +} + +void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + + if (!mac->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac vni %u mac %s es %s 0x%x hold stopped", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->flags); + THREAD_OFF(mac->hold_timer); +} + +void zebra_evpn_sync_mac_del(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + bool old_static; + bool new_static; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac del vni %u mac %s es %s seq %d f 0x%x", + mac->zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags); + old_static = zebra_evpn_mac_is_static(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + zebra_evpn_mac_start_hold_timer(mac); + new_static = zebra_evpn_mac_is_static(mac); + + if (old_static != new_static) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); +} + +static inline bool zebra_evpn_mac_is_bgp_seq_ok(zebra_evpn_t *zevpn, + zebra_mac_t *mac, uint32_t seq, + uint16_t ipa_len, + struct ipaddr *ipaddr) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + uint32_t tmp_seq; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + tmp_seq = mac->loc_seq; + else + tmp_seq = mac->rem_seq; + + if (seq < tmp_seq) { + /* if the mac was never advertised to bgp we must accept + * whatever sequence number bgp sends + * XXX - check with Vivek + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + && !zebra_evpn_mac_is_ready_for_bgp(mac->flags)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-macip accept vni %u mac %s%s%s lower seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + tmp_seq, mac->flags); + return true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-macip ignore vni %u mac %s%s%s as existing has higher seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(&mac->macaddr, macbuf, + sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + tmp_seq, mac->flags); + return false; + } + + return true; +} + +zebra_mac_t * +zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx) +{ + zebra_mac_t *mac; + bool inform_bgp = false; + bool inform_dataplane = false; + bool seq_change = false; + bool es_change = false; + uint32_t tmp_seq; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_local = false; + bool old_bgp_ready; + bool new_bgp_ready; + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + /* if it is a new local path we need to inform both + * the control protocol and the data-plane + */ + inform_bgp = true; + inform_dataplane = true; + ctx->mac_created = true; + ctx->mac_inactive = true; + + /* create the MAC and associate it with the dest ES */ + mac = zebra_evpn_mac_add(zevpn, macaddr); + zebra_evpn_es_mac_ref(mac, esi); + + /* local mac activated by an ES peer */ + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + /* if mac-only route setup peer flags */ + if (!ipa_len) { + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY); + else + SET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE); + } + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + old_bgp_ready = false; + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + } else { + uint32_t old_flags; + uint32_t new_flags; + bool old_static; + bool new_static; + bool sticky; + bool remote_gw; + + old_flags = mac->flags; + sticky = !!CHECK_FLAG(old_flags, ZEBRA_MAC_STICKY); + remote_gw = !!CHECK_FLAG(old_flags, ZEBRA_MAC_REMOTE_DEF_GW); + if (sticky || remote_gw) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "Ignore sync-macip vni %u mac %s%s%s%s%s", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, + sizeof(macbuf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, ipbuf, + sizeof(ipbuf)) + : "", + sticky ? " sticky" : "", + remote_gw ? " remote_gw" : ""); + ctx->ignore_macip = true; + return NULL; + } + if (!zebra_evpn_mac_is_bgp_seq_ok(zevpn, mac, seq, ipa_len, + ipaddr)) { + ctx->ignore_macip = true; + return NULL; + } + + old_local = !!CHECK_FLAG(old_flags, ZEBRA_MAC_LOCAL); + old_static = zebra_evpn_mac_is_static(mac); + + /* re-build the mac flags */ + new_flags = 0; + SET_FLAG(new_flags, ZEBRA_MAC_LOCAL); + /* retain old local activity flag */ + if (old_flags & ZEBRA_MAC_LOCAL) { + new_flags |= (old_flags & ZEBRA_MAC_LOCAL_INACTIVE); + } else { + new_flags |= ZEBRA_MAC_LOCAL_INACTIVE; + ctx->mac_inactive = true; + } + if (ipa_len) { + /* if mac-ip route do NOT update the peer flags + * i.e. retain only flags as is + */ + new_flags |= (old_flags & ZEBRA_MAC_ALL_PEER_FLAGS); + } else { + /* if mac-only route update peer flags */ + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) { + SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_PROXY); + /* if the mac was peer-active previously we + * need to keep the flag and start the + * holdtimer on it. the peer-active flag is + * cleared on holdtimer expiry. + */ + if (CHECK_FLAG(old_flags, + ZEBRA_MAC_ES_PEER_ACTIVE)) { + SET_FLAG(new_flags, + ZEBRA_MAC_ES_PEER_ACTIVE); + zebra_evpn_mac_start_hold_timer(mac); + } + } else { + SET_FLAG(new_flags, ZEBRA_MAC_ES_PEER_ACTIVE); + /* stop hold timer if a peer has verified + * reachability + */ + zebra_evpn_mac_stop_hold_timer(mac); + } + } + mac->rem_seq = 0; + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + mac->flags = new_flags; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC && (old_flags != new_flags)) + zlog_debug( + "sync-mac vni %u mac %s old_f 0x%x new_f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + old_flags, mac->flags); + + /* update es */ + es_change = zebra_evpn_es_mac_ref(mac, esi); + /* if mac dest change - inform both sides */ + if (es_change) { + inform_bgp = true; + inform_dataplane = true; + ctx->mac_inactive = true; + } + /* if peer-flag is being set notify dataplane that the + * entry must not be expired because of local inactivity + */ + new_static = zebra_evpn_mac_is_static(mac); + if (old_static != new_static) + inform_dataplane = true; + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(old_flags); + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + if (old_bgp_ready != new_bgp_ready) + inform_bgp = true; + } + + + /* update sequence number; if that results in a new local sequence + * inform bgp + */ + tmp_seq = MAX(mac->loc_seq, seq); + if (tmp_seq != mac->loc_seq) { + mac->loc_seq = tmp_seq; + seq_change = true; + inform_bgp = true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("sync-mac %s vni %u mac %s es %s seq %d f 0x%x%s%s", + ctx->mac_created ? "created" : "updated", zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags, inform_bgp ? " inform_bgp" : "", + inform_dataplane ? " inform_dp" : ""); + + if (inform_bgp) + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + new_bgp_ready); + + /* neighs using the mac may need to be re-sent to + * bgp with updated info + */ + if (seq_change || es_change || !old_local) + zebra_evpn_process_neigh_on_local_mac_change( + zevpn, mac, seq_change, es_change); + + if (inform_dataplane) { + if (ipa_len) + /* if the mac is being created as a part of MAC-IP + * route wait for the neigh to be updated or + * created before programming the mac + */ + ctx->mac_dp_update_deferred = true; + else + /* program the local mac in the kernel. when the ES + * change we need to force the dataplane to reset + * the activity as we are yet to establish activity + * locally + */ + zebra_evpn_sync_mac_dp_install( + mac, ctx->mac_inactive, + false /* force_clear_static */, __func__); + } + + return mac; +} + +/* update local fowarding info. return true if a dest-ES change + * is detected + */ +static bool zebra_evpn_local_mac_update_fwd_info(zebra_mac_t *mac, + struct interface *ifp, + vlanid_t vid) +{ + struct zebra_if *zif = ifp->info; + bool es_change; + + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + + es_change = zebra_evpn_es_mac_ref_entry(mac, zif->es_info.es); + + if (!mac->es) { + /* if es is set fwd_info is not-relevant/taped-out */ + mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.vid = vid; + } + + return es_change; +} + +/* Notify Local MACs to the clienti, skips GW MAC */ +static void zebra_evpn_send_mac_hash_entry_to_client(struct hash_bucket *bucket, + void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_mac_t *zmac = bucket->data; + + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_DEF_GW)) + return; + + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) + zebra_evpn_mac_send_add_to_client(wctx->zevpn->vni, + &zmac->macaddr, zmac->flags, + zmac->loc_seq, zmac->es); +} + +/* Iterator to Notify Local MACs of a EVPN */ +void zebra_evpn_send_mac_list_to_client(zebra_evpn_t *zevpn) +{ + struct mac_walk_ctx wctx; + + if (!zevpn->mac_table) + return; + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zevpn = zevpn; + + hash_iterate(zevpn->mac_table, zebra_evpn_send_mac_hash_entry_to_client, + &wctx); +} + +void zebra_evpn_rem_mac_del(zebra_evpn_t *zevpn, zebra_mac_t *mac) +{ + zebra_evpn_process_neigh_on_remote_mac_del(zevpn, mac); + /* the remote sequence number in the auto mac entry + * needs to be reset to 0 as the mac entry may have + * been removed on all VTEPs (including + * the originating one) + */ + mac->rem_seq = 0; + + /* If all remote neighbors referencing a remote MAC + * go away, we need to uninstall the MAC. + */ + if (remote_neigh_count(mac) == 0) { + zebra_evpn_rem_mac_uninstall(zevpn, mac); + zebra_evpn_es_mac_deref_entry(mac); + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + } + + if (list_isempty(mac->neigh_list)) + zebra_evpn_mac_del(zevpn, mac); + else + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); +} + +/* Print Duplicate MAC */ +void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_mac_t *mac; + + mac = (zebra_mac_t *)bucket->data; + if (!mac) + return; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + zebra_evpn_print_mac_hash(bucket, ctxt); +} + +/* Print Duplicate MAC in detail */ +void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_mac_t *mac; + + mac = (zebra_mac_t *)bucket->data; + if (!mac) + return; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + zebra_evpn_print_mac_hash_detail(bucket, ctxt); +} + +int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ethaddr *macaddr, uint16_t ipa_len, + struct ipaddr *ipaddr, zebra_mac_t **macp, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq, esi_t *esi) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + uint32_t tmp_seq; + bool sticky; + bool remote_gw; + int update_mac = 0; + bool do_dad = false; + bool is_dup_detect = false; + esi_t *old_esi; + bool old_static = false; + zebra_mac_t *mac; + + sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); + remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + + /* Ignore if the mac is already present as a gateway mac */ + if (mac && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) + && CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", + zevpn->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str(ipaddr, buf1, sizeof(buf1)) + : ""); + return -1; + } + + old_esi = (mac && mac->es) ? &mac->es->esi : zero_esi; + + /* check if the remote MAC is unknown or has a change. + * If so, that needs to be updated first. Note that client could + * install MAC and MACIP separately or just install the latter. + */ + if (!mac || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || sticky != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) + || remote_gw != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW) + || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip) + || memcmp(old_esi, esi, sizeof(esi_t)) || seq != mac->rem_seq) + update_mac = 1; + + if (update_mac) { + if (!mac) { + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) { + zlog_warn( + "Failed to add MAC %s VNI %u Remote VTEP %s", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + zevpn->vni, inet_ntoa(vtep_ip)); + return -1; + } + + zebra_evpn_es_mac_ref(mac, esi); + + /* Is this MAC created for a MACIP? */ + if (ipa_len) + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + } else { + zebra_evpn_es_mac_ref(mac, esi); + + /* When host moves but changes its (MAC,IP) + * binding, BGP may install a MACIP entry that + * corresponds to "older" location of the host + * in transient situations (because {IP1,M1} + * is a different route from {IP1,M2}). Check + * the sequence number and ignore this update + * if appropriate. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + tmp_seq = mac->loc_seq; + else + tmp_seq = mac->rem_seq; + + if (seq < tmp_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing MAC has higher seq %u flags 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + ipa_len ? " IP " : "", + ipa_len ? ipaddr2str( + ipaddr, buf1, + sizeof(buf1)) + : "", + tmp_seq, mac->flags); + return -1; + } + } + + /* Check MAC's curent state is local (this is the case + * where MAC has moved from L->R) and check previous + * detection started via local learning. + * RFC-7432: A PE/VTEP that detects a MAC mobility + * event via local learning starts an M-second timer. + * + * VTEP-IP or seq. change alone is not considered + * for dup. detection. + * + * MAC is already marked duplicate set dad, then + * is_dup_detect will be set to not install the entry. + */ + if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + && mac->dad_count) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + do_dad = true; + + /* Remove local MAC from BGP. */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* force drop the sync flags */ + old_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "sync-mac->remote vni %u mac %s es %s seq %d f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + mac->es ? mac->es->esi_str : "-", + mac->loc_seq, mac->flags); + zebra_evpn_mac_clear_sync_info(mac); + zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, + mac->flags, + false /* force */); + } + + /* Set "auto" and "remote" forwarding info. */ + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + mac->fwd_info.r_vtep_ip = vtep_ip; + + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + + if (remote_gw) + SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); + + zebra_evpn_dup_addr_detect_for_mac( + zvrf, mac, mac->fwd_info.r_vtep_ip, do_dad, + &is_dup_detect, false); + + if (!is_dup_detect) { + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); + /* Install the entry. */ + zebra_evpn_rem_mac_install(zevpn, mac, old_static); + } + } + + /* Update seq number. */ + mac->rem_seq = seq; + + /* If there is no IP, return after clearing AUTO flag of MAC. */ + if (!ipa_len) { + UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + return -1; + } + *macp = mac; + return 0; +} + +int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, + struct interface *ifp, + struct ethaddr *macaddr, vlanid_t vid, + bool sticky, bool local_inactive, + bool dp_static) +{ + zebra_mac_t *mac; + char buf[ETHER_ADDR_STRLEN]; + bool mac_sticky = false; + bool inform_client = false; + bool upd_neigh = false; + bool is_dup_detect = false; + struct in_addr vtep_ip = {.s_addr = 0}; + bool es_change = false; + bool new_bgp_ready; + /* assume inactive if not present or if not local */ + bool old_local_inactive = true; + bool old_bgp_ready = false; + bool inform_dataplane = false; + bool new_static = false; + + assert(ifp); + /* Check if we need to create or update or it is a NO-OP. */ + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u%s", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid, zevpn->vni, + local_inactive ? " local-inactive" : ""); + + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) { + flog_err( + EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add MAC %s intf %s(%u) VID %u VNI %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid, zevpn->vni); + return -1; + } + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + es_change = zebra_evpn_local_mac_update_fwd_info(mac, ifp, vid); + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + inform_client = true; + } else { + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u %scurFlags 0x%x", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vid, zevpn->vni, + local_inactive ? "local-inactive " : "", + mac->flags); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + struct interface *old_ifp; + vlanid_t old_vid; + bool old_static; + + zebra_evpn_mac_get_access_info(mac, &old_ifp, &old_vid); + old_bgp_ready = + zebra_evpn_mac_is_ready_for_bgp(mac->flags); + old_local_inactive = + !!(mac->flags & ZEBRA_MAC_LOCAL_INACTIVE); + old_static = zebra_evpn_mac_is_static(mac); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) + mac_sticky = true; + + /* + * Update any changes and if changes are relevant to + * BGP, note it. + */ + if (mac_sticky == sticky && old_ifp == ifp + && old_vid == vid + && old_local_inactive == local_inactive + && dp_static == old_static) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + " Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u%s, " + "entry exists and has not changed ", + sticky ? "sticky " : "", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + ifp->name, ifp->ifindex, vid, + zevpn->vni, + local_inactive + ? " local_inactive" + : ""); + return 0; + } + if (mac_sticky != sticky) { + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + else + UNSET_FLAG(mac->flags, + ZEBRA_MAC_STICKY); + inform_client = true; + } + + es_change = zebra_evpn_local_mac_update_fwd_info( + mac, ifp, vid); + /* If an es_change is detected we need to advertise + * the route with a sequence that is one + * greater. This is need to indicate a mac-move + * to the ES peers + */ + if (es_change) { + mac->loc_seq = mac->loc_seq + 1; + /* force drop the peer/sync info as it is + * simply no longer relevant + */ + if (CHECK_FLAG(mac->flags, + ZEBRA_MAC_ALL_PEER_FLAGS)) { + zebra_evpn_mac_clear_sync_info(mac); + new_static = + zebra_evpn_mac_is_static(mac); + /* if we clear peer-flags we + * also need to notify the dataplane + * to drop the static flag + */ + if (old_static != new_static) + inform_dataplane = true; + } + } + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) + || CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { + bool do_dad = false; + + /* + * MAC has either moved or was "internally" created due + * to a neighbor learn and is now actually learnt. If + * it was learnt as a remote sticky MAC, this is an + * operator error. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) { + flog_warn( + EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT, + "MAC %s already learnt as remote sticky MAC behind VTEP %s VNI %u", + prefix_mac2str(macaddr, buf, + sizeof(buf)), + inet_ntoa(mac->fwd_info.r_vtep_ip), + zevpn->vni); + return 0; + } + + /* If an actual move, compute MAC's seq number */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + mac->loc_seq = + MAX(mac->rem_seq + 1, mac->loc_seq); + vtep_ip = mac->fwd_info.r_vtep_ip; + /* Trigger DAD for remote MAC */ + do_dad = true; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); + UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + es_change = zebra_evpn_local_mac_update_fwd_info( + mac, ifp, vid); + if (sticky) + SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + /* + * We have to inform BGP of this MAC as well as process + * all neighbors. + */ + inform_client = true; + upd_neigh = true; + + zebra_evpn_dup_addr_detect_for_mac( + zvrf, mac, vtep_ip, do_dad, &is_dup_detect, + true); + if (is_dup_detect) { + inform_client = false; + upd_neigh = false; + } + } + } + + /* if the dataplane thinks the entry is sync but it is + * not sync in zebra we need to re-install to fixup + */ + if (dp_static) { + new_static = zebra_evpn_mac_is_static(mac); + if (!new_static) + inform_dataplane = true; + } + + if (local_inactive) + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + else + UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + + new_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + /* if local-activity has changed we need update bgp + * even if bgp already knows about the mac + */ + if ((old_local_inactive != local_inactive) + || (new_bgp_ready != old_bgp_ready)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "local mac vni %u mac %s es %s seq %d f 0x%x%s", + zevpn->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + mac->es ? mac->es->esi_str : "", mac->loc_seq, + mac->flags, + local_inactive ? " local-inactive" : ""); + inform_client = true; + } + + if (es_change) { + inform_client = true; + upd_neigh = true; + } + + /* Inform dataplane if required. */ + if (inform_dataplane) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* Inform BGP if required. */ + if (inform_client) + zebra_evpn_mac_send_add_del_to_client(mac, old_bgp_ready, + new_bgp_ready); + + /* Process all neighbors associated with this MAC, if required. */ + if (upd_neigh) + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + es_change); + + return 0; +} + +int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + struct interface *ifp) +{ + zebra_mac_t *mac; + char buf[ETHER_ADDR_STRLEN]; + bool old_bgp_ready; + bool new_bgp_ready; + /* If entry doesn't exist, nothing to do. */ + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) + return 0; + + /* Is it a local entry? */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "DEL MAC %s intf %s(%u) VID %u -> VNI %u seq %u flags 0x%x nbr count %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, + ifp->ifindex, mac->fwd_info.local.vid, zevpn->vni, + mac->loc_seq, mac->flags, listcount(mac->neigh_list)); + + old_bgp_ready = zebra_evpn_mac_is_ready_for_bgp(mac->flags); + if (zebra_evpn_mac_is_static(mac)) { + /* this is a synced entry and can only be removed when the + * es-peers stop advertising it. + */ + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug( + "re-add sync-mac vni %u mac %s es %s seq %d f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + mac->es ? mac->es->esi_str : "-", mac->loc_seq, + mac->flags); + + /* inform-bgp about change in local-activity if any */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE)) { + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL_INACTIVE); + new_bgp_ready = + zebra_evpn_mac_is_ready_for_bgp(mac->flags); + zebra_evpn_mac_send_add_del_to_client( + mac, old_bgp_ready, new_bgp_ready); + } + + /* re-install the entry in the kernel */ + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + return 0; + } + + /* Update all the neigh entries associated with this mac */ + zebra_evpn_process_neigh_on_local_mac_del(zevpn, mac); + + /* Remove MAC from BGP. */ + zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, mac->flags, + false /* force */); + + zebra_evpn_es_mac_deref_entry(mac); + + /* + * If there are no neigh associated with the mac delete the mac + * else mark it as AUTO for forward reference + */ + if (!listcount(mac->neigh_list)) { + zebra_evpn_mac_del(zevpn, mac); + } else { + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); + UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + } + + return 0; +} + +int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t **macp, + struct ethaddr *macaddr, vlanid_t vlan_id) +{ + char buf[ETHER_ADDR_STRLEN]; + zebra_mac_t *mac; + + mac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!mac) { + mac = zebra_evpn_mac_add(zevpn, macaddr); + if (!mac) { + flog_err(EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add MAC %s intf %s(%u) VID %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, vlan_id); + return -1; + } + } + + /* Set "local" forwarding info. */ + SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); + memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); + mac->fwd_info.local.ifindex = ifp->ifindex; + mac->fwd_info.local.vid = vlan_id; + + *macp = mac; + + return 0; +} diff --git a/zebra/zebra_evpn_mac.h b/zebra/zebra_evpn_mac.h new file mode 100644 index 0000000000..39aaf1fb30 --- /dev/null +++ b/zebra/zebra_evpn_mac.h @@ -0,0 +1,263 @@ +/* + * Zebra EVPN MAC Data structures and definitions + * These are "internal" to this function. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * Copyright (C) 2020 Volta Networks. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_EVPN_MAC_H +#define _ZEBRA_EVPN_MAC_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zebra_mac_t_ zebra_mac_t; + +struct host_rb_entry { + RB_ENTRY(host_rb_entry) hl_entry; + + struct prefix p; +}; + +RB_HEAD(host_rb_tree_entry, host_rb_entry); +RB_PROTOTYPE(host_rb_tree_entry, host_rb_entry, hl_entry, + host_rb_entry_compare); +/* + * MAC hash table. + * + * This table contains the MAC addresses pertaining to this VNI. + * This includes local MACs learnt on an attached VLAN that maps + * to this VNI as well as remote MACs learnt and installed by BGP. + * Local MACs will be known either on a VLAN sub-interface or + * on (port, VLAN); however, it is sufficient for zebra to maintain + * against the VNI i.e., it does not need to retain the local "port" + * information. The correct VNI will be obtained as zebra maintains + * the mapping (of VLAN to VNI). + */ +struct zebra_mac_t_ { + /* MAC address. */ + struct ethaddr macaddr; + + uint32_t flags; +#define ZEBRA_MAC_LOCAL 0x01 +#define ZEBRA_MAC_REMOTE 0x02 +#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */ +#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */ +#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */ +#define ZEBRA_MAC_DEF_GW 0x20 +/* remote VTEP advertised MAC as default GW */ +#define ZEBRA_MAC_REMOTE_DEF_GW 0x40 +#define ZEBRA_MAC_DUPLICATE 0x80 +#define ZEBRA_MAC_FPM_SENT 0x100 /* whether or not this entry was sent. */ +/* MAC is locally active on an ethernet segment peer */ +#define ZEBRA_MAC_ES_PEER_ACTIVE 0x200 +/* MAC has been proxy-advertised by peers. This means we need to + * keep the entry for forwarding but cannot advertise it + */ +#define ZEBRA_MAC_ES_PEER_PROXY 0x400 +/* We have not been able to independently establish that the host is + * local connected but one or more ES peers claims it is. + * We will maintain the entry for forwarding purposes and continue + * to advertise it as locally attached but with a "proxy" flag + */ +#define ZEBRA_MAC_LOCAL_INACTIVE 0x800 + +#define ZEBRA_MAC_ALL_LOCAL_FLAGS (ZEBRA_MAC_LOCAL | ZEBRA_MAC_LOCAL_INACTIVE) +#define ZEBRA_MAC_ALL_PEER_FLAGS \ + (ZEBRA_MAC_ES_PEER_PROXY | ZEBRA_MAC_ES_PEER_ACTIVE) + + /* back pointer to zevpn */ + zebra_evpn_t *zevpn; + + /* Local or remote info. */ + union { + struct { + ifindex_t ifindex; + vlanid_t vid; + } local; + + struct in_addr r_vtep_ip; + } fwd_info; + + /* Local or remote ES */ + struct zebra_evpn_es *es; + /* memory used to link the mac to the es */ + struct listnode es_listnode; + + /* Mobility sequence numbers associated with this entry. */ + uint32_t rem_seq; + uint32_t loc_seq; + + /* List of neigh associated with this mac */ + struct list *neigh_list; + + /* list of hosts pointing to this remote RMAC */ + struct host_rb_tree_entry host_rb; + + /* Duplicate mac detection */ + uint32_t dad_count; + + struct thread *dad_mac_auto_recovery_timer; + + struct timeval detect_start_time; + + time_t dad_dup_detect_time; + + /* used for ageing out the PEER_ACTIVE flag */ + struct thread *hold_timer; + + /* number of neigh entries (using this mac) that have + * ZEBRA_MAC_ES_PEER_ACTIVE or ZEBRA_NEIGH_ES_PEER_PROXY + */ + uint32_t sync_neigh_cnt; +}; + +/* + * Context for MAC hash walk - used by callbacks. + */ +struct mac_walk_ctx { + zebra_evpn_t *zevpn; /* EVPN hash */ + struct zebra_vrf *zvrf; /* VRF - for client notification. */ + int uninstall; /* uninstall from kernel? */ + int upd_client; /* uninstall from client? */ + + uint32_t flags; +#define DEL_LOCAL_MAC 0x1 +#define DEL_REMOTE_MAC 0x2 +#define DEL_ALL_MAC (DEL_LOCAL_MAC | DEL_REMOTE_MAC) +#define DEL_REMOTE_MAC_FROM_VTEP 0x4 +#define SHOW_REMOTE_MAC_FROM_VTEP 0x8 + + struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */ + + struct vty *vty; /* Used by VTY handlers */ + uint32_t count; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ + bool print_dup; /* Used to print dup addr list */ +}; + +struct rmac_walk_ctx { + struct vty *vty; + struct json_object *json; +}; + +/* temporary datastruct to pass info between the mac-update and + * neigh-update while handling mac-ip routes + */ +struct sync_mac_ip_ctx { + bool ignore_macip; + bool mac_created; + bool mac_inactive; + bool mac_dp_update_deferred; + zebra_mac_t *mac; +}; + +/**************************** SYNC MAC handling *****************************/ +/**************************** SYNC MAC handling *****************************/ +/* if the mac has been added of a mac-route from the peer + * or if it is being referenced by a neigh added by the + * peer we cannot let it age out i.e. we set the static bit + * in the dataplane + */ +static inline bool zebra_evpn_mac_is_static(zebra_mac_t *mac) +{ + return ((mac->flags & ZEBRA_MAC_ALL_PEER_FLAGS) || mac->sync_neigh_cnt); +} + +/* mac needs to be locally active or active on an ES peer */ +static inline bool zebra_evpn_mac_is_ready_for_bgp(uint32_t flags) +{ + return (flags & ZEBRA_MAC_LOCAL) + && (!(flags & ZEBRA_MAC_LOCAL_INACTIVE) + || (flags & ZEBRA_MAC_ES_PEER_ACTIVE)); +} + +void zebra_evpn_mac_stop_hold_timer(zebra_mac_t *mac); + +static inline void zebra_evpn_mac_clear_sync_info(zebra_mac_t *mac) +{ + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_PEER_FLAGS); + zebra_evpn_mac_stop_hold_timer(mac); +} + +struct hash *zebra_mac_db_create(const char *desc); +uint32_t num_valid_macs(zebra_evpn_t *zevi); +uint32_t num_dup_detected_macs(zebra_evpn_t *zevi); +int zebra_evpn_rem_mac_uninstall(zebra_evpn_t *zevi, zebra_mac_t *mac); +int zebra_evpn_rem_mac_install(zebra_evpn_t *zevi, zebra_mac_t *mac, + bool was_static); +void zebra_evpn_deref_ip2mac(zebra_evpn_t *zevi, zebra_mac_t *mac); +zebra_mac_t *zebra_evpn_mac_lookup(zebra_evpn_t *zevi, struct ethaddr *mac); +zebra_mac_t *zebra_evpn_mac_add(zebra_evpn_t *zevi, struct ethaddr *macaddr); +int zebra_evpn_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac); +int zebra_evpn_macip_send_msg_to_client(uint32_t id, struct ethaddr *macaddr, + struct ipaddr *ip, uint8_t flags, + uint32_t seq, int state, + struct zebra_evpn_es *es, uint16_t cmd); +void zebra_evpn_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json); +void zebra_evpn_print_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, + const char *caller); +void zebra_evpn_mac_send_add_del_to_client(zebra_mac_t *mac, bool old_bgp_ready, + bool new_bgp_ready); + +void zebra_evpn_mac_del_all(zebra_evpn_t *zevi, int uninstall, int upd_client, + uint32_t flags); +int zebra_evpn_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t mac_flags, uint32_t seq, + struct zebra_evpn_es *es); +int zebra_evpn_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr, + uint32_t flags, bool force); +void zebra_evpn_send_mac_list_to_client(zebra_evpn_t *zevi); +zebra_mac_t * +zebra_evpn_proc_sync_mac_update(zebra_evpn_t *zevi, struct ethaddr *macaddr, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx); +void zebra_evpn_sync_mac_del(zebra_mac_t *mac); +void zebra_evpn_rem_mac_del(zebra_evpn_t *zevi, zebra_mac_t *mac); +void zebra_evpn_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_dad_mac_hash_detail(struct hash_bucket *bucket, + void *ctxt); +int process_mac_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ethaddr *macaddr, uint16_t ipa_len, + struct ipaddr *ipaddr, zebra_mac_t **macp, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq, esi_t *esi); + +int zebra_evpn_add_update_local_mac(struct zebra_vrf *zvrf, zebra_evpn_t *zevpn, + struct interface *ifp, + struct ethaddr *macaddr, vlanid_t vid, + bool sticky, bool local_inactive, + bool dp_static); +int zebra_evpn_del_local_mac(zebra_evpn_t *zevpn, struct ethaddr *macaddr, + struct interface *ifp); +int zebra_evpn_mac_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t **macp, + struct ethaddr *macaddr, vlanid_t vlan_id); + +#ifdef __cplusplus +} +#endif + +#endif /*_ZEBRA_EVPN_MAC_H */ diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c new file mode 100644 index 0000000000..029480eb4a --- /dev/null +++ b/zebra/zebra_evpn_mh.c @@ -0,0 +1,2147 @@ +/* + * Zebra EVPN multihoming code + * + * Copyright (C) 2019 Cumulus Networks, Inc. + * Anuradha Karuppiah + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <zebra.h> + +#include "command.h" +#include "hash.h" +#include "if.h" +#include "jhash.h" +#include "linklist.h" +#include "log.h" +#include "memory.h" +#include "prefix.h" +#include "stream.h" +#include "table.h" +#include "vlan.h" +#include "vxlan.h" + +#include "zebra/zebra_router.h" +#include "zebra/debug.h" +#include "zebra/interface.h" +#include "zebra/rib.h" +#include "zebra/rt.h" +#include "zebra/rt_netlink.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_l2.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_ns.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_router.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_nhg.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZACC_BD, "Access Broadcast Domain"); +DEFINE_MTYPE_STATIC(ZEBRA, ZES, "Ethernet Segment"); +DEFINE_MTYPE_STATIC(ZEBRA, ZES_EVI, "ES info per-EVI"); +DEFINE_MTYPE_STATIC(ZEBRA, ZMH_INFO, "MH global info"); +DEFINE_MTYPE_STATIC(ZEBRA, ZES_VTEP, "VTEP attached to the ES"); + +static void zebra_evpn_es_get_one_base_evpn(void); +static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn, bool add); +static void zebra_evpn_local_es_del(struct zebra_evpn_es *es); +static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid, + struct ethaddr *sysmac); + +esi_t zero_esi_buf, *zero_esi = &zero_esi_buf; + +/*****************************************************************************/ +/* Ethernet Segment to EVI association - + * 1. The ES-EVI entry is maintained as a RB tree per L2-VNI + * (zebra_evpn_t.es_evi_rb_tree). + * 2. Each local ES-EVI entry is sent to BGP which advertises it as an + * EAD-EVI (Type-1 EVPN) route + * 3. Local ES-EVI setup is re-evaluated on the following triggers - + * a. When an ESI is set or cleared on an access port. + * b. When an access port associated with an ESI is deleted. + * c. When VLAN member ship changes on an access port. + * d. When a VXLAN_IF is set or cleared on an access broadcast domain. + * e. When a L2-VNI is added or deleted for a VxLAN_IF. + * 4. Currently zebra doesn't remote ES-EVIs. Those are managed and maintained + * entirely in BGP which consolidates them into a remote ES. The remote ES + * is then sent to zebra which allocates a NHG for it. + */ + +/* compare ES-IDs for the ES-EVI RB tree maintained per-EVPN */ +static int zebra_es_evi_rb_cmp(const struct zebra_evpn_es_evi *es_evi1, + const struct zebra_evpn_es_evi *es_evi2) +{ + return memcmp(&es_evi1->es->esi, &es_evi2->es->esi, ESI_BYTES); +} +RB_GENERATE(zebra_es_evi_rb_head, zebra_evpn_es_evi, + rb_node, zebra_es_evi_rb_cmp); + +/* allocate a new ES-EVI and insert it into the per-L2-VNI and per-ES + * tables. + */ +static struct zebra_evpn_es_evi *zebra_evpn_es_evi_new(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + + es_evi = XCALLOC(MTYPE_ZES_EVI, sizeof(struct zebra_evpn_es_evi)); + + es_evi->es = es; + es_evi->zevpn = zevpn; + + /* insert into the EVPN-ESI rb tree */ + if (RB_INSERT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi)) { + XFREE(MTYPE_ZES_EVI, es_evi); + return NULL; + } + + /* add to the ES's VNI list */ + listnode_init(&es_evi->es_listnode, es_evi); + listnode_add(es->es_evi_list, &es_evi->es_listnode); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s evi %d new", + es_evi->es->esi_str, es_evi->zevpn->vni); + + return es_evi; +} + +/* returns TRUE if the EVPN is ready to be sent to BGP */ +static inline bool zebra_evpn_send_to_client_ok(zebra_evpn_t *zevpn) +{ + return !!(zevpn->flags & ZEVPN_READY_FOR_BGP); +} + +/* Evaluate if the es_evi is ready to be sent BGP - + * 1. If it is ready an add is sent to BGP + * 2. If it is not ready a del is sent (if the ES had been previously added + * to BGP). + */ +static void zebra_evpn_es_evi_re_eval_send_to_client( + struct zebra_evpn_es_evi *es_evi) +{ + bool old_ready; + bool new_ready; + + old_ready = !!(es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP); + + /* ES and L2-VNI have to be individually ready for BGP */ + if ((es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL) && + (es_evi->es->flags & ZEBRA_EVPNES_READY_FOR_BGP) && + zebra_evpn_send_to_client_ok(es_evi->zevpn)) + es_evi->flags |= ZEBRA_EVPNES_EVI_READY_FOR_BGP; + else + es_evi->flags &= ~ZEBRA_EVPNES_EVI_READY_FOR_BGP; + + new_ready = !!(es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP); + + if (old_ready == new_ready) + return; + + if (new_ready) + zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zevpn, + true /* add */); + else + zebra_evpn_es_evi_send_to_client(es_evi->es, es_evi->zevpn, + false /* add */); +} + +/* remove the ES-EVI from the per-L2-VNI and per-ES tables and free + * up the memory. + */ +static void zebra_evpn_es_evi_free(struct zebra_evpn_es_evi *es_evi) +{ + struct zebra_evpn_es *es = es_evi->es; + zebra_evpn_t *zevpn = es_evi->zevpn; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s evi %d free", + es_evi->es->esi_str, es_evi->zevpn->vni); + + /* remove from the ES's VNI list */ + list_delete_node(es->es_evi_list, &es_evi->es_listnode); + + /* remove from the VNI-ESI rb tree */ + RB_REMOVE(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, es_evi); + + /* remove from the VNI-ESI rb tree */ + XFREE(MTYPE_ZES_EVI, es_evi); +} + +/* find the ES-EVI in the per-L2-VNI RB tree */ +static struct zebra_evpn_es_evi *zebra_evpn_es_evi_find( + struct zebra_evpn_es *es, zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi es_evi; + + es_evi.es = es; + + return RB_FIND(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree, &es_evi); +} + +/* Tell BGP about an ES-EVI deletion and then delete it */ +static void zebra_evpn_local_es_evi_do_del(struct zebra_evpn_es_evi *es_evi) +{ + if (!(es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("local es %s evi %d del", + es_evi->es->esi_str, es_evi->zevpn->vni); + + if (es_evi->flags & ZEBRA_EVPNES_EVI_READY_FOR_BGP) { + /* send a del only if add was sent for it earlier */ + zebra_evpn_es_evi_send_to_client(es_evi->es, + es_evi->zevpn, false /* add */); + } + + /* delete it from the EVPN's local list */ + list_delete_node(es_evi->zevpn->local_es_evi_list, + &es_evi->l2vni_listnode); + + es_evi->flags &= ~ZEBRA_EVPNES_EVI_LOCAL; + zebra_evpn_es_evi_free(es_evi); +} +static void zebra_evpn_local_es_evi_del(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + + es_evi = zebra_evpn_es_evi_find(es, zevpn); + if (es_evi) + zebra_evpn_local_es_evi_do_del(es_evi); +} + +/* Create an ES-EVI if it doesn't already exist and tell BGP */ +static void zebra_evpn_local_es_evi_add(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + + es_evi = zebra_evpn_es_evi_find(es, zevpn); + if (!es_evi) { + es_evi = zebra_evpn_es_evi_new(es, zevpn); + if (!es_evi) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("local es %s evi %d add", + es_evi->es->esi_str, es_evi->zevpn->vni); + es_evi->flags |= ZEBRA_EVPNES_EVI_LOCAL; + /* add to the EVPN's local list */ + listnode_init(&es_evi->l2vni_listnode, es_evi); + listnode_add(zevpn->local_es_evi_list, &es_evi->l2vni_listnode); + + zebra_evpn_es_evi_re_eval_send_to_client(es_evi); + } +} + +static void zebra_evpn_es_evi_show_entry(struct vty *vty, + struct zebra_evpn_es_evi *es_evi, json_object *json) +{ + char type_str[4]; + + if (json) { + /* XXX */ + } else { + type_str[0] = '\0'; + if (es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + + vty_out(vty, "%-8d %-30s %-4s\n", + es_evi->zevpn->vni, es_evi->es->esi_str, + type_str); + } +} + +static void zebra_evpn_es_evi_show_entry_detail(struct vty *vty, + struct zebra_evpn_es_evi *es_evi, json_object *json) +{ + char type_str[4]; + + if (json) { + /* XXX */ + } else { + type_str[0] = '\0'; + if (es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + + vty_out(vty, "VNI %d ESI: %s\n", + es_evi->zevpn->vni, es_evi->es->esi_str); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " Ready for BGP: %s\n", + (es_evi->flags & + ZEBRA_EVPNES_EVI_READY_FOR_BGP) ? + "yes" : "no"); + vty_out(vty, "\n"); + } +} + +static void zebra_evpn_es_evi_show_one_evpn(zebra_evpn_t *zevpn, + struct vty *vty, json_object *json, int detail) +{ + struct zebra_evpn_es_evi *es_evi; + + RB_FOREACH(es_evi, zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree) { + if (detail) + zebra_evpn_es_evi_show_entry_detail(vty, es_evi, json); + else + zebra_evpn_es_evi_show_entry(vty, es_evi, json); + } +} + +struct evpn_mh_show_ctx { + struct vty *vty; + json_object *json; + int detail; +}; + +static void zebra_evpn_es_evi_show_one_evpn_hash_cb(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_evpn_t *zevpn = (zebra_evpn_t *)bucket->data; + struct evpn_mh_show_ctx *wctx = (struct evpn_mh_show_ctx *)ctxt; + + zebra_evpn_es_evi_show_one_evpn(zevpn, wctx->vty, + wctx->json, wctx->detail); +} + +void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail) +{ + json_object *json = NULL; + struct zebra_vrf *zvrf; + struct evpn_mh_show_ctx wctx; + + zvrf = zebra_vrf_get_evpn(); + + memset(&wctx, 0, sizeof(wctx)); + wctx.vty = vty; + wctx.json = json; + wctx.detail = detail; + + if (!detail && !json) { + vty_out(vty, "Type: L local, R remote\n"); + vty_out(vty, "%-8s %-30s %-4s\n", "VNI", "ESI", "Type"); + } + /* Display all L2-VNIs */ + hash_iterate(zvrf->evpn_table, zebra_evpn_es_evi_show_one_evpn_hash_cb, + &wctx); +} + +void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, vni_t vni, int detail) +{ + json_object *json = NULL; + zebra_evpn_t *zevpn; + + zevpn = zebra_evpn_lookup(vni); + if (zevpn) { + if (!detail && !json) { + vty_out(vty, "Type: L local, R remote\n"); + vty_out(vty, "%-8s %-30s %-4s\n", "VNI", "ESI", "Type"); + } + } else { + if (!uj) + vty_out(vty, "VNI %d doesn't exist\n", vni); + } + zebra_evpn_es_evi_show_one_evpn(zevpn, vty, json, detail); +} + +/* Initialize the ES tables maintained per-L2_VNI */ +void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn) +{ + /* Initialize the ES-EVI RB tree */ + RB_INIT(zebra_es_evi_rb_head, &zevpn->es_evi_rb_tree); + + /* Initialize the local and remote ES lists maintained for quick + * walks by type + */ + zevpn->local_es_evi_list = list_new(); + listset_app_node_mem(zevpn->local_es_evi_list); +} + +/* Cleanup the ES info maintained per- EVPN */ +void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + struct zebra_evpn_es_evi *es_evi_next; + + RB_FOREACH_SAFE(es_evi, zebra_es_evi_rb_head, + &zevpn->es_evi_rb_tree, es_evi_next) { + zebra_evpn_local_es_evi_do_del(es_evi); + } + + list_delete(&zevpn->local_es_evi_list); + zebra_evpn_es_clear_base_evpn(zevpn); +} + +/* called when the oper state or bridge membership changes for the + * vxlan device + */ +void zebra_evpn_update_all_es(zebra_evpn_t *zevpn) +{ + struct zebra_evpn_es_evi *es_evi; + struct listnode *node; + + /* the EVPN is now elgible as a base for EVPN-MH */ + if (zebra_evpn_send_to_client_ok(zevpn)) + zebra_evpn_es_set_base_evpn(zevpn); + else + zebra_evpn_es_clear_base_evpn(zevpn); + + for (ALL_LIST_ELEMENTS_RO(zevpn->local_es_evi_list, node, es_evi)) + zebra_evpn_es_evi_re_eval_send_to_client(es_evi); +} + +/*****************************************************************************/ +/* Access broadcast domains (BD) + * 1. These broadcast domains can be VLAN aware (in which case + * the key is VID) or VLAN unaware (in which case the key is + * 2. A VID-BD is created when a VLAN is associated with an access port or + * when the VLAN is associated with VXLAN_IF + * 3. A BD is translated into ES-EVI entries when a VNI is associated + * with the broadcast domain + */ +/* Hash key for VLAN based broadcast domains */ +static unsigned int zebra_evpn_acc_vl_hash_keymake(const void *p) +{ + const struct zebra_evpn_access_bd *acc_bd = p; + + return jhash_1word(acc_bd->vid, 0); +} + +/* Compare two VLAN based broadcast domains */ +static bool zebra_evpn_acc_vl_cmp(const void *p1, const void *p2) +{ + const struct zebra_evpn_access_bd *acc_bd1 = p1; + const struct zebra_evpn_access_bd *acc_bd2 = p2; + + if (acc_bd1 == NULL && acc_bd2 == NULL) + return true; + + if (acc_bd1 == NULL || acc_bd2 == NULL) + return false; + + return (acc_bd1->vid == acc_bd2->vid); +} + +/* Lookup VLAN based broadcast domain */ +static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_find(vlanid_t vid) +{ + struct zebra_evpn_access_bd *acc_bd; + struct zebra_evpn_access_bd tmp; + + tmp.vid = vid; + acc_bd = hash_lookup(zmh_info->evpn_vlan_table, &tmp); + + return acc_bd; +} + +/* A new broadcast domain can be created when a VLAN member or VLAN<=>VxLAN_IF + * mapping is added. + */ +static struct zebra_evpn_access_bd *zebra_evpn_acc_vl_new(vlanid_t vid) +{ + struct zebra_evpn_access_bd *acc_bd; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d add", vid); + + acc_bd = XCALLOC(MTYPE_ZACC_BD, sizeof(struct zebra_evpn_access_bd)); + + acc_bd->vid = vid; + + /* Initialize the mbr list */ + acc_bd->mbr_zifs = list_new(); + + /* Add to hash */ + if (!hash_get(zmh_info->evpn_vlan_table, acc_bd, hash_alloc_intern)) { + XFREE(MTYPE_ZACC_BD, acc_bd); + return NULL; + } + + return acc_bd; +} + +/* Free VLAN based broadcast domain - + * This just frees appropriate memory, caller should have taken other + * needed actions. + */ +static void zebra_evpn_acc_vl_free(struct zebra_evpn_access_bd *acc_bd) +{ + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d del", acc_bd->vid); + + /* cleanup resources maintained against the ES */ + list_delete(&acc_bd->mbr_zifs); + + /* remove EVI from various tables */ + hash_release(zmh_info->evpn_vlan_table, acc_bd); + + XFREE(MTYPE_ZACC_BD, acc_bd); +} + +static void zebra_evpn_acc_vl_cleanup_all(struct hash_bucket *bucket, void *arg) +{ + struct zebra_evpn_access_bd *acc_bd = bucket->data; + + zebra_evpn_acc_vl_free(acc_bd); +} + +/* called when a bd mbr is removed or VxLAN_IF is diassociated from the access + * VLAN + */ +static void zebra_evpn_acc_bd_free_on_deref(struct zebra_evpn_access_bd *acc_bd) +{ + if (!list_isempty(acc_bd->mbr_zifs) || acc_bd->vxlan_zif) + return; + + /* if there are no references free the EVI */ + zebra_evpn_acc_vl_free(acc_bd); +} + +/* called when a EVPN-L2VNI is set or cleared against a BD */ +static void zebra_evpn_acc_bd_evpn_set(struct zebra_evpn_access_bd *acc_bd, + zebra_evpn_t *zevpn, zebra_evpn_t *old_zevpn) +{ + struct zebra_if *zif; + struct listnode *node; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d l2-vni %u set", + acc_bd->vid, zevpn ? zevpn->vni : 0); + + for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif)) { + if (!zif->es_info.es) + continue; + + if (zevpn) + zebra_evpn_local_es_evi_add(zif->es_info.es, zevpn); + else if (old_zevpn) + zebra_evpn_local_es_evi_del(zif->es_info.es, old_zevpn); + } +} + +/* handle VLAN->VxLAN_IF association */ +void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif) +{ + struct zebra_evpn_access_bd *acc_bd; + struct zebra_if *old_vxlan_zif; + zebra_evpn_t *old_zevpn; + + if (!vid) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) + acc_bd = zebra_evpn_acc_vl_new(vid); + + old_vxlan_zif = acc_bd->vxlan_zif; + acc_bd->vxlan_zif = vxlan_zif; + if (vxlan_zif == old_vxlan_zif) + return; + + old_zevpn = acc_bd->zevpn; + acc_bd->zevpn = zebra_evpn_lookup(vxlan_zif->l2info.vxl.vni); + if (acc_bd->zevpn == old_zevpn) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d vni %u ref", + acc_bd->vid, vxlan_zif->l2info.vxl.vni); + + if (old_zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn); + + if (acc_bd->zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, acc_bd->zevpn, NULL); +} + +/* handle VLAN->VxLAN_IF deref */ +void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif) +{ + struct zebra_evpn_access_bd *acc_bd; + + if (!vid) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) + return; + + /* clear vxlan_if only if it matches */ + if (acc_bd->vxlan_zif != vxlan_zif) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d vni %u deref", + acc_bd->vid, vxlan_zif->l2info.vxl.vni); + + if (acc_bd->zevpn) + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, acc_bd->zevpn); + + acc_bd->zevpn = NULL; + acc_bd->vxlan_zif = NULL; + + /* if there are no other references the access_bd can be freed */ + zebra_evpn_acc_bd_free_on_deref(acc_bd); +} + +/* handle EVPN add/del */ +void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, zebra_evpn_t *zevpn, + bool set) +{ + struct zebra_l2info_vxlan *vxl; + struct zebra_evpn_access_bd *acc_bd; + + if (!zif) + return; + + /* locate access_bd associated with the vxlan device */ + vxl = &zif->l2info.vxl; + acc_bd = zebra_evpn_acc_vl_find(vxl->access_vlan); + if (!acc_bd) + return; + + if (set) { + zebra_evpn_es_set_base_evpn(zevpn); + if (acc_bd->zevpn != zevpn) { + acc_bd->zevpn = zevpn; + zebra_evpn_acc_bd_evpn_set(acc_bd, zevpn, NULL); + } + } else { + if (acc_bd->zevpn) { + zebra_evpn_t *old_zevpn = acc_bd->zevpn; + acc_bd->zevpn = NULL; + zebra_evpn_acc_bd_evpn_set(acc_bd, NULL, old_zevpn); + } + } +} + +/* handle addition of new VLAN members */ +void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif) +{ + struct zebra_evpn_access_bd *acc_bd; + + if (!vid) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) + acc_bd = zebra_evpn_acc_vl_new(vid); + + if (listnode_lookup(acc_bd->mbr_zifs, zif)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d mbr %s ref", + vid, zif->ifp->name); + + listnode_add(acc_bd->mbr_zifs, zif); + if (acc_bd->zevpn && zif->es_info.es) + zebra_evpn_local_es_evi_add(zif->es_info.es, acc_bd->zevpn); +} + +/* handle deletion of VLAN members */ +void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif) +{ + struct zebra_evpn_access_bd *acc_bd; + struct listnode *node; + + if (!vid) + return; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) + return; + + node = listnode_lookup(acc_bd->mbr_zifs, zif); + if (!node) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("access vlan %d mbr %s deref", + vid, zif->ifp->name); + + list_delete_node(acc_bd->mbr_zifs, node); + + if (acc_bd->zevpn && zif->es_info.es) + zebra_evpn_local_es_evi_del(zif->es_info.es, acc_bd->zevpn); + + /* if there are no other references the access_bd can be freed */ + zebra_evpn_acc_bd_free_on_deref(acc_bd); +} + +static void zebra_evpn_acc_vl_show_entry_detail(struct vty *vty, + struct zebra_evpn_access_bd *acc_bd, json_object *json) +{ + struct zebra_if *zif; + struct listnode *node; + + if (json) { + /* XXX */ + } else { + vty_out(vty, "VLAN: %u\n", acc_bd->vid); + vty_out(vty, " VxLAN Interface: %s\n", + acc_bd->vxlan_zif ? + acc_bd->vxlan_zif->ifp->name : "-"); + vty_out(vty, " L2-VNI: %d\n", + acc_bd->zevpn ? acc_bd->zevpn->vni : 0); + vty_out(vty, " Member Count: %d\n", + listcount(acc_bd->mbr_zifs)); + vty_out(vty, " Members: \n"); + for (ALL_LIST_ELEMENTS_RO(acc_bd->mbr_zifs, node, zif)) + vty_out(vty, " %s\n", zif->ifp->name); + vty_out(vty, "\n"); + } +} + +static void zebra_evpn_acc_vl_show_entry(struct vty *vty, + struct zebra_evpn_access_bd *acc_bd, json_object *json) +{ + if (!json) + vty_out(vty, "%-5u %21s %-8d %u\n", + acc_bd->vid, + acc_bd->vxlan_zif ? + acc_bd->vxlan_zif->ifp->name : "-", + acc_bd->zevpn ? acc_bd->zevpn->vni : 0, + listcount(acc_bd->mbr_zifs)); +} + +static void zebra_evpn_acc_vl_show_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct evpn_mh_show_ctx *wctx = ctxt; + struct zebra_evpn_access_bd *acc_bd = bucket->data; + + if (wctx->detail) + zebra_evpn_acc_vl_show_entry_detail(wctx->vty, + acc_bd, wctx->json); + else + zebra_evpn_acc_vl_show_entry(wctx->vty, + acc_bd, wctx->json); +} + +void zebra_evpn_acc_vl_show(struct vty *vty, bool uj) +{ + json_object *json = NULL; + struct evpn_mh_show_ctx wctx; + + memset(&wctx, 0, sizeof(wctx)); + wctx.vty = vty; + wctx.json = json; + wctx.detail = false; + + if (!json) + vty_out(vty, "%-5s %21s %-8s %s\n", + "VLAN", "VxLAN-IF", "L2-VNI", "# Members"); + + hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash, + &wctx); +} + +void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj) +{ + json_object *json = NULL; + struct evpn_mh_show_ctx wctx; + + memset(&wctx, 0, sizeof(wctx)); + wctx.vty = vty; + wctx.json = json; + wctx.detail = true; + + hash_iterate(zmh_info->evpn_vlan_table, zebra_evpn_acc_vl_show_hash, + &wctx); +} + +void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid) +{ + json_object *json = NULL; + struct zebra_evpn_access_bd *acc_bd; + + acc_bd = zebra_evpn_acc_vl_find(vid); + if (!acc_bd) { + if (!json) { + vty_out(vty, "VLAN %u not present\n", vid); + return; + } + } + zebra_evpn_acc_vl_show_entry_detail(vty, acc_bd, json); +} + +/* Initialize VLAN member bitmap on an interface. Although VLAN membership + * is independent of EVPN we only process it if its of interest to EVPN-MH + * i.e. on access ports that can be setup as Ethernet Segments. And that is + * intended as an optimization. + */ +void zebra_evpn_if_init(struct zebra_if *zif) +{ + if (!zebra_evpn_is_if_es_capable(zif)) + return; + + if (!bf_is_inited(zif->vlan_bitmap)) + bf_init(zif->vlan_bitmap, IF_VLAN_BITMAP_MAX); + + /* if an es_id and sysmac are already present against the interface + * activate it + */ + zebra_evpn_local_es_update(zif, zif->es_info.lid, &zif->es_info.sysmac); +} + +/* handle deletion of an access port by removing it from all associated + * broadcast domains. + */ +void zebra_evpn_if_cleanup(struct zebra_if *zif) +{ + vlanid_t vid; + + if (!bf_is_inited(zif->vlan_bitmap)) + return; + + bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { + zebra_evpn_vl_mbr_deref(vid, zif); + } + + bf_free(zif->vlan_bitmap); + + /* Delete associated Ethernet Segment */ + if (zif->es_info.es) + zebra_evpn_local_es_del(zif->es_info.es); +} + +/***************************************************************************** + * L2 NH/NHG Management + * A L2 NH entry is programmed in the kernel for every ES-VTEP entry. This + * NH is then added to the L2-ECMP-NHG associated with the ES. + */ +static uint32_t zebra_evpn_nhid_alloc(bool is_nhg) +{ + uint32_t id; + int type; + + bf_assign_index(zmh_info->nh_id_bitmap, id); + + if (!id) + return 0; + + type = is_nhg ? EVPN_NHG_ID_TYPE_BIT : EVPN_NH_ID_TYPE_BIT; + return (id | type); +} + +static void zebra_evpn_nhid_free(uint32_t nh_id) +{ + uint32_t id = (nh_id & EVPN_NH_ID_VAL_MASK); + + if (!id) + return; + + bf_release_index(zmh_info->nh_id_bitmap, id); +} + +/* The MAC ECMP group is activated on the first VTEP */ +static void zebra_evpn_nhg_update(struct zebra_evpn_es *es) +{ + uint32_t nh_cnt = 0; + struct nh_grp nh_ids[ES_VTEP_MAX_CNT]; + struct zebra_evpn_es_vtep *es_vtep; + struct listnode *node; + + if (!es->nhg_id) + return; + + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + if (!es_vtep->nh_id) + continue; + + if (nh_cnt >= ES_VTEP_MAX_CNT) + break; + + memset(&nh_ids[nh_cnt], 0, sizeof(struct nh_grp)); + nh_ids[nh_cnt].id = es_vtep->nh_id; + ++nh_cnt; + } + + if (nh_cnt) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) { + char nh_str[ES_VTEP_LIST_STR_SZ]; + uint32_t i; + char nh_buf[16]; + + nh_str[0] = '\0'; + for (i = 0; i < nh_cnt; ++i) { + snprintf(nh_buf, sizeof(nh_buf), "%u ", + nh_ids[i].id); + strlcat(nh_str, nh_buf, sizeof(nh_str)); + } + zlog_debug("es %s nhg 0x%x add %s", + es->esi_str, es->nhg_id, nh_str); + } + + es->flags |= ZEBRA_EVPNES_NHG_ACTIVE; + kernel_upd_mac_nhg(es->nhg_id, nh_cnt, nh_ids); + } else { + if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) + zlog_debug("es %s nhg 0x%x del", + es->esi_str, es->nhg_id); + es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE; + kernel_del_mac_nhg(es->nhg_id); + } + } + + /* XXX - update remote macs associated with the ES */ +} + +static void zebra_evpn_nh_add(struct zebra_evpn_es_vtep *es_vtep) +{ + if (es_vtep->nh_id) + return; + + es_vtep->nh_id = zebra_evpn_nhid_alloc(false); + + if (!es_vtep->nh_id) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) + zlog_debug("es %s vtep %s nh 0x%x add", + es_vtep->es->esi_str, + inet_ntoa(es_vtep->vtep_ip), es_vtep->nh_id); + /* install the NH */ + kernel_upd_mac_nh(es_vtep->nh_id, es_vtep->vtep_ip); + /* add the NH to the parent NHG */ + zebra_evpn_nhg_update(es_vtep->es); +} + +static void zebra_evpn_nh_del(struct zebra_evpn_es_vtep *es_vtep) +{ + uint32_t nh_id; + + if (!es_vtep->nh_id) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NH) + zlog_debug("es %s vtep %s nh 0x%x del", + es_vtep->es->esi_str, + inet_ntoa(es_vtep->vtep_ip), es_vtep->nh_id); + + nh_id = es_vtep->nh_id; + es_vtep->nh_id = 0; + + /* remove the NH from the parent NHG */ + zebra_evpn_nhg_update(es_vtep->es); + /* uninstall the NH */ + kernel_del_mac_nh(nh_id); + zebra_evpn_nhid_free(nh_id); + +} + +/*****************************************************************************/ +/* Ethernet Segment Management + * 1. Ethernet Segment is a collection of links attached to the same + * server (MHD) or switch (MHN) + * 2. An Ethernet Segment can span multiple PEs and is identified by the + * 10-byte ES-ID. + * 3. Zebra manages the local ESI configuration. + * 4. It also maintains the aliasing that maps an ESI (local or remote) + * to one or more PEs/VTEPs. + * 5. remote ESs are added by BGP (on rxing EAD Type-1 routes) + */ +/* A list of remote VTEPs is maintained for each ES. This list includes - + * 1. VTEPs for which we have imported the ESR i.e. ES-peers + * 2. VTEPs that have an "active" ES-EVI VTEP i.e. EAD-per-ES and EAD-per-EVI + * have been imported into one or more EVPNs + */ +static int zebra_evpn_es_vtep_cmp(void *p1, void *p2) +{ + const struct zebra_evpn_es_vtep *es_vtep1 = p1; + const struct zebra_evpn_es_vtep *es_vtep2 = p2; + + return es_vtep1->vtep_ip.s_addr - es_vtep2->vtep_ip.s_addr; +} + +static struct zebra_evpn_es_vtep *zebra_evpn_es_vtep_new( + struct zebra_evpn_es *es, struct in_addr vtep_ip) +{ + struct zebra_evpn_es_vtep *es_vtep; + + es_vtep = XCALLOC(MTYPE_ZES_VTEP, sizeof(*es_vtep)); + + es_vtep->es = es; + es_vtep->vtep_ip.s_addr = vtep_ip.s_addr; + listnode_init(&es_vtep->es_listnode, es_vtep); + listnode_add_sort(es->es_vtep_list, &es_vtep->es_listnode); + + return es_vtep; +} + +static void zebra_evpn_es_vtep_free(struct zebra_evpn_es_vtep *es_vtep) +{ + struct zebra_evpn_es *es = es_vtep->es; + + list_delete_node(es->es_vtep_list, &es_vtep->es_listnode); + /* update the L2-NHG associated with the ES */ + zebra_evpn_nh_del(es_vtep); + XFREE(MTYPE_ZES_VTEP, es_vtep); +} + + +/* check if VTEP is already part of the list */ +static struct zebra_evpn_es_vtep *zebra_evpn_es_vtep_find( + struct zebra_evpn_es *es, struct in_addr vtep_ip) +{ + struct listnode *node = NULL; + struct zebra_evpn_es_vtep *es_vtep; + + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) { + if (es_vtep->vtep_ip.s_addr == vtep_ip.s_addr) + return es_vtep; + } + return NULL; +} + +static void zebra_evpn_es_vtep_add(struct zebra_evpn_es *es, + struct in_addr vtep_ip) +{ + struct zebra_evpn_es_vtep *es_vtep; + + es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip); + + if (!es_vtep) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s vtep %s add", + es->esi_str, inet_ntoa(vtep_ip)); + es_vtep = zebra_evpn_es_vtep_new(es, vtep_ip); + /* update the L2-NHG associated with the ES */ + zebra_evpn_nh_add(es_vtep); + } +} + +static void zebra_evpn_es_vtep_del(struct zebra_evpn_es *es, + struct in_addr vtep_ip) +{ + struct zebra_evpn_es_vtep *es_vtep; + + es_vtep = zebra_evpn_es_vtep_find(es, vtep_ip); + + if (es_vtep) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s vtep %s del", + es->esi_str, inet_ntoa(vtep_ip)); + zebra_evpn_es_vtep_free(es_vtep); + } +} + +/* compare ES-IDs for the global ES RB tree */ +static int zebra_es_rb_cmp(const struct zebra_evpn_es *es1, + const struct zebra_evpn_es *es2) +{ + return memcmp(&es1->esi, &es2->esi, ESI_BYTES); +} +RB_GENERATE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp); + +/* Lookup ES */ +struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi) +{ + struct zebra_evpn_es tmp; + + memcpy(&tmp.esi, esi, sizeof(esi_t)); + return RB_FIND(zebra_es_rb_head, &zmh_info->es_rb_tree, &tmp); +} + +/* A new local es is created when a local-es-id and sysmac is configured + * against an interface. + */ +static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi) +{ + struct zebra_evpn_es *es; + + es = XCALLOC(MTYPE_ZES, sizeof(struct zebra_evpn_es)); + + /* fill in ESI */ + memcpy(&es->esi, esi, sizeof(esi_t)); + esi_to_str(&es->esi, es->esi_str, sizeof(es->esi_str)); + + /* Add to rb_tree */ + if (RB_INSERT(zebra_es_rb_head, &zmh_info->es_rb_tree, es)) { + XFREE(MTYPE_ZES, es); + return NULL; + } + + /* Initialise the ES-EVI list */ + es->es_evi_list = list_new(); + listset_app_node_mem(es->es_evi_list); + + /* Initialise the VTEP list */ + es->es_vtep_list = list_new(); + listset_app_node_mem(es->es_vtep_list); + es->es_vtep_list->cmp = zebra_evpn_es_vtep_cmp; + + /* mac entries associated with the ES */ + es->mac_list = list_new(); + listset_app_node_mem(es->mac_list); + + /* reserve a NHG */ + es->nhg_id = zebra_evpn_nhid_alloc(true); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s nhg 0x%x new", es->esi_str, es->nhg_id); + + return es; +} + +/* Free a given ES - + * This just frees appropriate memory, caller should have taken other + * needed actions. + */ +static struct zebra_evpn_es *zebra_evpn_es_free(struct zebra_evpn_es *es) +{ + /* If the ES has a local or remote reference it cannot be freed. + * Free is also prevented if there are MAC entries referencing + * it. + */ + if ((es->flags & (ZEBRA_EVPNES_LOCAL | ZEBRA_EVPNES_REMOTE)) || + listcount(es->mac_list)) + return es; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s free", es->esi_str); + + /* If the NHG is still installed uninstall it and free the id */ + if (es->flags & ZEBRA_EVPNES_NHG_ACTIVE) { + es->flags &= ~ZEBRA_EVPNES_NHG_ACTIVE; + kernel_del_mac_nhg(es->nhg_id); + } + zebra_evpn_nhid_free(es->nhg_id); + + /* cleanup resources maintained against the ES */ + list_delete(&es->es_evi_list); + list_delete(&es->es_vtep_list); + list_delete(&es->mac_list); + + /* remove from the VNI-ESI rb tree */ + RB_REMOVE(zebra_es_rb_head, &zmh_info->es_rb_tree, es); + + XFREE(MTYPE_ZES, es); + + return NULL; +} + +/* Inform BGP about local ES addition */ +static int zebra_evpn_es_send_add_to_client(struct zebra_evpn_es *es) +{ + struct zserv *client; + struct stream *s; + uint8_t oper_up; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_LOCAL_ES_ADD, zebra_vrf_get_evpn_id()); + stream_put(s, &es->esi, sizeof(esi_t)); + stream_put_ipv4(s, zmh_info->es_originator_ip.s_addr); + oper_up = !!(es->flags & ZEBRA_EVPNES_OPER_UP); + stream_putc(s, oper_up); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("send add local es %s %s to %s", + es->esi_str, + inet_ntoa(zmh_info->es_originator_ip), + zebra_route_string(client->proto)); + + client->local_es_add_cnt++; + return zserv_send_message(client, s); +} + +/* Inform BGP about local ES deletion */ +static int zebra_evpn_es_send_del_to_client(struct zebra_evpn_es *es) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + stream_reset(s); + + zclient_create_header(s, ZEBRA_LOCAL_ES_DEL, zebra_vrf_get_evpn_id()); + stream_put(s, &es->esi, sizeof(esi_t)); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("send del local es %s to %s", es->esi_str, + zebra_route_string(client->proto)); + + client->local_es_del_cnt++; + return zserv_send_message(client, s); +} + +/* XXX - call any time ZEBRA_EVPNES_LOCAL gets set or cleared */ +static void zebra_evpn_es_re_eval_send_to_client(struct zebra_evpn_es *es, + bool es_evi_re_reval) +{ + bool old_ready; + bool new_ready; + struct listnode *node; + struct zebra_evpn_es_evi *es_evi; + + old_ready = !!(es->flags & ZEBRA_EVPNES_READY_FOR_BGP); + + if ((es->flags & ZEBRA_EVPNES_LOCAL) && + zmh_info->es_originator_ip.s_addr) + es->flags |= ZEBRA_EVPNES_READY_FOR_BGP; + else + es->flags &= ~ZEBRA_EVPNES_READY_FOR_BGP; + + new_ready = !!(es->flags & ZEBRA_EVPNES_READY_FOR_BGP); + if (old_ready == new_ready) + return; + + if (new_ready) + zebra_evpn_es_send_add_to_client(es); + else + zebra_evpn_es_send_del_to_client(es); + + /* re-eval associated EVIs */ + if (es_evi_re_reval) { + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, node, es_evi)) { + if (!(es_evi->flags & ZEBRA_EVPNES_EVI_LOCAL)) + continue; + zebra_evpn_es_evi_re_eval_send_to_client(es_evi); + } + } +} + +void zebra_evpn_es_send_all_to_client(bool add) +{ + struct listnode *es_node; + struct listnode *evi_node; + struct zebra_evpn_es *es; + struct zebra_evpn_es_evi *es_evi; + + if (!zmh_info) + return; + + for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, es_node, es)) { + if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) { + if (add) + zebra_evpn_es_send_add_to_client(es); + for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, + evi_node, es_evi)) { + if (!(es_evi->flags & + ZEBRA_EVPNES_EVI_READY_FOR_BGP)) + continue; + + if (add) + zebra_evpn_es_evi_send_to_client( + es, es_evi->zevpn, + true /* add */); + else + zebra_evpn_es_evi_send_to_client( + es, es_evi->zevpn, + false /* add */); + } + if (!add) + zebra_evpn_es_send_del_to_client(es); + } + } +} + +/* walk the vlan bitmap associated with the zif and create or delete + * es_evis for all vlans associated with a VNI. + * XXX: This API is really expensive. optimize later if possible. + */ +static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es) +{ + struct zebra_if *zif = es->zif; + uint16_t vid; + struct zebra_evpn_access_bd *acc_bd; + + + bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { + acc_bd = zebra_evpn_acc_vl_find(vid); + if (acc_bd->zevpn) + zebra_evpn_local_es_evi_add(es, acc_bd->zevpn); + } +} + +static void zebra_evpn_es_local_mac_update(struct zebra_evpn_es *es, + bool force_clear_static) +{ + zebra_mac_t *mac; + struct listnode *node; + + for (ALL_LIST_ELEMENTS_RO(es->mac_list, node, mac)) { + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) { + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + force_clear_static, __func__); + } + } +} + +static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, + struct zebra_if *zif) +{ + if (es->flags & ZEBRA_EVPNES_LOCAL) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("local es %s add; nhg 0x%x if %s", + es->esi_str, es->nhg_id, zif->ifp->name); + + es->flags |= ZEBRA_EVPNES_LOCAL; + listnode_init(&es->local_es_listnode, es); + listnode_add(zmh_info->local_es_list, &es->local_es_listnode); + + /* attach es to interface */ + zif->es_info.es = es; + + /* attach interface to es */ + es->zif = zif; + if (if_is_operative(zif->ifp)) + es->flags |= ZEBRA_EVPNES_OPER_UP; + + /* setup base-vni if one doesn't already exist; the ES will get sent + * to BGP as a part of that process + */ + if (!zmh_info->es_base_evpn) + zebra_evpn_es_get_one_base_evpn(); + else + /* send notification to bgp */ + zebra_evpn_es_re_eval_send_to_client(es, + false /* es_evi_re_reval */); + + /* Setup ES-EVIs for all VxLAN stretched VLANs associated with + * the zif + */ + zebra_evpn_es_setup_evis(es); + /* if there any local macs referring to the ES as dest we + * need to set the static reference on them if the MAC is + * synced from an ES peer + */ + zebra_evpn_es_local_mac_update(es, + false /* force_clear_static */); +} + +static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es *es) +{ + struct zebra_if *zif; + + if (!(es->flags & ZEBRA_EVPNES_LOCAL)) + return; + + es->flags &= ~ZEBRA_EVPNES_LOCAL; + /* if there any local macs referring to the ES as dest we + * need to clear the static reference on them + */ + zebra_evpn_es_local_mac_update(es, + true /* force_clear_static */); + + /* clear the es from the parent interface */ + zif = es->zif; + zif->es_info.es = NULL; + es->zif = NULL; + + /* remove from the ES list */ + list_delete_node(zmh_info->local_es_list, &es->local_es_listnode); + + /* free up the ES if there is no remote reference */ + zebra_evpn_es_free(es); +} + +/* Delete an ethernet segment and inform BGP */ +static void zebra_evpn_local_es_del(struct zebra_evpn_es *es) +{ + struct zebra_evpn_es_evi *es_evi; + struct listnode *node = NULL; + struct listnode *nnode = NULL; + struct zebra_if *zif; + + if (!CHECK_FLAG(es->flags, ZEBRA_EVPNES_LOCAL)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) { + zif = es->zif; + zlog_debug("local es %s del; nhg 0x%x if %s", + es->esi_str, es->nhg_id, + zif ? zif->ifp->name : "-"); + } + + /* remove all ES-EVIs associated with the ES */ + for (ALL_LIST_ELEMENTS(es->es_evi_list, node, nnode, es_evi)) + zebra_evpn_local_es_evi_do_del(es_evi); + + /* send a del if the ES had been sent to BGP earlier */ + if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) + zebra_evpn_es_send_del_to_client(es); + + zebra_evpn_es_local_info_clear(es); +} + +/* eval remote info associated with the ES */ +static void zebra_evpn_es_remote_info_re_eval(struct zebra_evpn_es *es) +{ + /* if there are remote VTEPs the ES-EVI is classified as "remote" */ + if (listcount(es->es_vtep_list)) { + if (!(es->flags & ZEBRA_EVPNES_REMOTE)) { + es->flags |= ZEBRA_EVPNES_REMOTE; + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("remote es %s add; nhg 0x%x", + es->esi_str, es->nhg_id); + } + } else { + if (es->flags & ZEBRA_EVPNES_REMOTE) { + es->flags &= ~ZEBRA_EVPNES_REMOTE; + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("remote es %s del; nhg 0x%x", + es->esi_str, es->nhg_id); + zebra_evpn_es_free(es); + } + } +} + +/* A new local es is created when a local-es-id and sysmac is configured + * against an interface. + */ +static int zebra_evpn_local_es_update(struct zebra_if *zif, uint32_t lid, + struct ethaddr *sysmac) +{ + struct zebra_evpn_es *old_es = zif->es_info.es; + struct zebra_evpn_es *es; + esi_t esi; + int offset = 0; + int field_bytes = 0; + + /* Complete config of the ES-ID bootstraps the ES */ + if (!lid || is_zero_mac(sysmac)) { + /* if in ES is attached to zif delete it */ + if (old_es) + zebra_evpn_local_es_del(old_es); + return 0; + } + + /* build 10-byte type-3-ESI - + * Type(1-byte), MAC(6-bytes), ES-LID (3-bytes) + */ + field_bytes = 1; + esi.val[offset] = ESI_TYPE_MAC; + offset += field_bytes; + + field_bytes = ETH_ALEN; + memcpy(&esi.val[offset], (uint8_t *)sysmac, field_bytes); + offset += field_bytes; + + esi.val[offset++] = (uint8_t)(lid >> 16); + esi.val[offset++] = (uint8_t)(lid >> 8); + esi.val[offset++] = (uint8_t)lid; + + if (old_es && !memcmp(&old_es->esi, &esi, sizeof(esi_t))) + /* dup - nothing to be done */ + return 0; + + /* release the old_es against the zif */ + if (old_es) + zebra_evpn_local_es_del(old_es); + + es = zebra_evpn_es_find(&esi); + if (es) { + /* if it exists against another interface flag an error */ + if (es->zif && es->zif != zif) + return -1; + } else { + /* create new es */ + es = zebra_evpn_es_new(&esi); + } + + zebra_evpn_es_local_info_set(es, zif); + + return 0; +} + +static int zebra_evpn_remote_es_del(esi_t *esi, struct in_addr vtep_ip) +{ + char buf[ESI_STR_LEN]; + struct zebra_evpn_es *es; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("remote es %s vtep %s del", + esi_to_str(esi, buf, sizeof(buf)), + inet_ntoa(vtep_ip)); + + es = zebra_evpn_es_find(esi); + if (!es) { + /* XXX - error log */ + return -1; + } + + zebra_evpn_es_vtep_del(es, vtep_ip); + zebra_evpn_es_remote_info_re_eval(es); + + return 0; +} + +/* force delete a remote ES on the way down */ +static void zebra_evpn_remote_es_flush(struct zebra_evpn_es *es) +{ + struct zebra_evpn_es_vtep *es_vtep; + struct listnode *node; + struct listnode *nnode; + + for (ALL_LIST_ELEMENTS(es->es_vtep_list, node, nnode, es_vtep)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s vtep %s flush", + es->esi_str, + inet_ntoa(es_vtep->vtep_ip)); + zebra_evpn_es_vtep_free(es_vtep); + zebra_evpn_es_remote_info_re_eval(es); + } +} + +static int zebra_evpn_remote_es_add(esi_t *esi, struct in_addr vtep_ip) +{ + char buf[ESI_STR_LEN]; + struct zebra_evpn_es *es; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("remote es %s vtep %s add", + esi_to_str(esi, buf, sizeof(buf)), + inet_ntoa(vtep_ip)); + + es = zebra_evpn_es_find(esi); + if (!es) { + es = zebra_evpn_es_new(esi); + if (!es) { + /* XXX - error log */ + return -1; + } + } + + zebra_evpn_es_vtep_add(es, vtep_ip); + zebra_evpn_es_remote_info_re_eval(es); + + return 0; +} + +void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + struct in_addr vtep_ip; + esi_t esi; + + if (!is_evpn_enabled()) { + zlog_debug( + "%s: EVPN not enabled yet we received a es_add zapi call", + __PRETTY_FUNCTION__); + return; + } + + memset(&esi, 0, sizeof(esi_t)); + s = msg; + + stream_get(&esi, s, sizeof(esi_t)); + vtep_ip.s_addr = stream_get_ipv4(s); + + if (hdr->command == ZEBRA_REMOTE_ES_VTEP_ADD) + zebra_evpn_remote_es_add(&esi, vtep_ip); + else + zebra_evpn_remote_es_del(&esi, vtep_ip); +} + +void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac) +{ + struct zebra_evpn_es *es = mac->es; + + mac->es = NULL; + if (!es) + return; + + list_delete_node(es->mac_list, &mac->es_listnode); + if (!listcount(es->mac_list)) + zebra_evpn_es_free(es); +} + +/* Associate a MAC entry with a local or remote ES. Returns false if there + * was no ES change. + */ +bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac, struct zebra_evpn_es *es) +{ + if (mac->es == es) + return false; + + if (mac->es) + zebra_evpn_es_mac_deref_entry(mac); + + if (!es) + return true; + + mac->es = es; + listnode_init(&mac->es_listnode, mac); + listnode_add(es->mac_list, &mac->es_listnode); + + return true; +} + +bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi) +{ + struct zebra_evpn_es *es; + + es = zebra_evpn_es_find(esi); + if (!es) { + es = zebra_evpn_es_new(esi); + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("auto es %s add on mac ref", es->esi_str); + } + + return zebra_evpn_es_mac_ref_entry(mac, es); +} + +/* Inform BGP about local ES-EVI add or del */ +static int zebra_evpn_es_evi_send_to_client(struct zebra_evpn_es *es, + zebra_evpn_t *zevpn, bool add) +{ + struct zserv *client; + struct stream *s; + + client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); + /* BGP may not be running. */ + if (!client) + return 0; + + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, + add ? ZEBRA_LOCAL_ES_EVI_ADD : ZEBRA_LOCAL_ES_EVI_DEL, + zebra_vrf_get_evpn_id()); + stream_put(s, &es->esi, sizeof(esi_t)); + stream_putl(s, zevpn->vni); + + /* Write packet size. */ + stream_putw_at(s, 0, stream_get_endp(s)); + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("send %s local es %s evi %u to %s", + add ? "add" : "del", + es->esi_str, zevpn->vni, + zebra_route_string(client->proto)); + + client->local_es_add_cnt++; + return zserv_send_message(client, s); +} + +/* sysmac part of a local ESI has changed */ +static int zebra_evpn_es_sys_mac_update(struct zebra_if *zif, + struct ethaddr *sysmac) +{ + int rv; + + rv = zebra_evpn_local_es_update(zif, zif->es_info.lid, sysmac); + if (!rv) + memcpy(&zif->es_info.sysmac, sysmac, sizeof(struct ethaddr)); + + return rv; +} + +/* local-ID part of ESI has changed */ +static int zebra_evpn_es_lid_update(struct zebra_if *zif, uint32_t lid) +{ + int rv; + + rv = zebra_evpn_local_es_update(zif, lid, &zif->es_info.sysmac); + if (!rv) + zif->es_info.lid = lid; + + return rv; +} + +void zebra_evpn_es_cleanup(void) +{ + struct zebra_evpn_es *es; + struct zebra_evpn_es *es_next; + + RB_FOREACH_SAFE(es, zebra_es_rb_head, + &zmh_info->es_rb_tree, es_next) { + zebra_evpn_local_es_del(es); + zebra_evpn_remote_es_flush(es); + } +} + +/* Only certain types of access ports can be setup as an Ethernet Segment */ +bool zebra_evpn_is_if_es_capable(struct zebra_if *zif) +{ + if (zif->zif_type == ZEBRA_IF_BOND) + return true; + + /* XXX: allow swpX i.e. a regular ethernet port to be an ES link too */ + return false; +} + +void zebra_evpn_if_es_print(struct vty *vty, struct zebra_if *zif) +{ + char buf[ETHER_ADDR_STRLEN]; + + if (zif->es_info.lid || !is_zero_mac(&zif->es_info.sysmac)) + vty_out(vty, " EVPN MH: ES id %u ES sysmac %s\n", + zif->es_info.lid, + prefix_mac2str(&zif->es_info.sysmac, + buf, sizeof(buf))); +} + +void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up) +{ + struct zebra_evpn_es *es = zif->es_info.es; + bool old_up = !!(es->flags & ZEBRA_EVPNES_OPER_UP); + + if (old_up == up) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es %s state changed to %s ", + es->esi_str, + up ? "up" : "down"); + if (up) + es->flags |= ZEBRA_EVPNES_OPER_UP; + else + es->flags &= ~ZEBRA_EVPNES_OPER_UP; + + /* inform BGP of the ES oper state change */ + if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) + zebra_evpn_es_send_add_to_client(es); +} + +static char *zebra_evpn_es_vtep_str(char *vtep_str, struct zebra_evpn_es *es, + uint8_t vtep_str_size) +{ + struct zebra_evpn_es_vtep *zvtep; + struct listnode *node; + bool first = true; + + vtep_str[0] = '\0'; + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, zvtep)) { + if (first) { + first = false; + strlcat(vtep_str, inet_ntoa(zvtep->vtep_ip), + vtep_str_size); + } else { + strlcat(vtep_str, ",", vtep_str_size); + strlcat(vtep_str, inet_ntoa(zvtep->vtep_ip), + vtep_str_size); + } + } + return vtep_str; +} + +static void zebra_evpn_es_show_entry(struct vty *vty, + struct zebra_evpn_es *es, json_object *json) +{ + char type_str[4]; + char vtep_str[ES_VTEP_LIST_STR_SZ]; + + if (json) { + /* XXX */ + } else { + type_str[0] = '\0'; + if (es->flags & ZEBRA_EVPNES_LOCAL) + strlcat(type_str, "L", sizeof(type_str)); + if (es->flags & ZEBRA_EVPNES_REMOTE) + strlcat(type_str, "R", sizeof(type_str)); + + zebra_evpn_es_vtep_str(vtep_str, es, sizeof(vtep_str)); + + vty_out(vty, "%-30s %-4s %-21s %s\n", + es->esi_str, type_str, + es->zif ? es->zif->ifp->name : "-", + vtep_str); + } +} + +static void zebra_evpn_es_show_entry_detail(struct vty *vty, + struct zebra_evpn_es *es, json_object *json) +{ + char type_str[80]; + struct zebra_evpn_es_vtep *zvtep; + struct listnode *node; + + if (json) { + /* XXX */ + } else { + type_str[0] = '\0'; + if (es->flags & ZEBRA_EVPNES_LOCAL) + strlcat(type_str, "Local", sizeof(type_str)); + if (es->flags & ZEBRA_EVPNES_REMOTE) { + if (strnlen(type_str, sizeof(type_str))) + strlcat(type_str, ",", sizeof(type_str)); + strlcat(type_str, "Remote", sizeof(type_str)); + } + + vty_out(vty, "ESI: %s\n", es->esi_str); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " Interface: %s\n", + (es->zif) ? + es->zif->ifp->name : "-"); + vty_out(vty, " State: %s\n", + (es->flags & ZEBRA_EVPNES_OPER_UP) ? + "up" : "down"); + vty_out(vty, " Ready for BGP: %s\n", + (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) ? + "yes" : "no"); + vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list)); + vty_out(vty, " MAC Count: %d\n", listcount(es->mac_list)); + vty_out(vty, " Nexthop group: 0x%x\n", es->nhg_id); + vty_out(vty, " VTEPs:\n"); + for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, zvtep)) + vty_out(vty, " %s nh: 0x%x\n", + inet_ntoa(zvtep->vtep_ip), + zvtep->nh_id); + + vty_out(vty, "\n"); + } +} + +void zebra_evpn_es_show(struct vty *vty, bool uj) +{ + struct zebra_evpn_es *es; + json_object *json = NULL; + + if (uj) { + /* XXX */ + } else { + vty_out(vty, "Type: L local, R remote\n"); + vty_out(vty, "%-30s %-4s %-21s %s\n", + "ESI", "Type", "ES-IF", "VTEPs"); + } + + RB_FOREACH(es, zebra_es_rb_head, &zmh_info->es_rb_tree) + zebra_evpn_es_show_entry(vty, es, json); +} + +void zebra_evpn_es_show_detail(struct vty *vty, bool uj) +{ + struct zebra_evpn_es *es; + json_object *json = NULL; + + RB_FOREACH(es, zebra_es_rb_head, &zmh_info->es_rb_tree) + zebra_evpn_es_show_entry_detail(vty, es, json); +} + +void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi) +{ + struct zebra_evpn_es *es; + char esi_str[ESI_STR_LEN]; + json_object *json = NULL; + + es = zebra_evpn_es_find(esi); + + if (!es) { + esi_to_str(esi, esi_str, sizeof(esi_str)); + vty_out(vty, "ESI %s does not exist\n", esi_str); + return; + } + + zebra_evpn_es_show_entry_detail(vty, es, json); +} + +int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp) +{ + struct zebra_if *zif = ifp->info; + char buf[ETHER_ADDR_STRLEN]; + + if (zif->es_info.lid) + vty_out(vty, " evpn mh es-id %u\n", zif->es_info.lid); + + if (!is_zero_mac(&zif->es_info.sysmac)) + vty_out(vty, " evpn mh es-sys-mac %s\n", + prefix_mac2str(&zif->es_info.sysmac, + buf, sizeof(buf))); + return 0; +} + +#ifndef VTYSH_EXTRACT_PL +#include "zebra/zebra_evpn_mh_clippy.c" +#endif +/* CLI for setting up sysmac part of ESI on an access port */ +DEFPY(zebra_evpn_es_sys_mac, + zebra_evpn_es_sys_mac_cmd, + "[no$no] evpn mh es-sys-mac [X:X:X:X:X:X$mac]", + NO_STR + "EVPN\n" + EVPN_MH_VTY_STR + "Ethernet segment system MAC\n" + MAC_STR +) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif; + int ret = 0; + + zif = ifp->info; + + if (no) { + static struct ethaddr zero_mac; + + ret = zebra_evpn_es_sys_mac_update(zif, &zero_mac); + if (ret == -1) { + vty_out(vty, "%%Failed to clear ES sysmac\n"); + return CMD_WARNING; + } + } else { + + if (!zebra_evpn_is_if_es_capable(zif)) { + vty_out(vty, + "%%ESI cannot be associated with this interface type\n"); + return CMD_WARNING; + } + + if (!mac || is_zero_mac(&mac->eth_addr)) { + vty_out(vty, "%%ES sysmac value is invalid\n"); + return CMD_WARNING; + } + + ret = zebra_evpn_es_sys_mac_update(zif, &mac->eth_addr); + if (ret == -1) { + vty_out(vty, "%%ESI already exists on a different interface\n"); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/* CLI for setting up local-ID part of ESI on an access port */ +DEFPY(zebra_evpn_es_id, + zebra_evpn_es_id_cmd, + "[no$no] evpn mh es-id [(1-16777215)$es_lid]", + NO_STR + "EVPN\n" + EVPN_MH_VTY_STR + "Ethernet segment local identifier\n" + "ID\n" +) +{ + VTY_DECLVAR_CONTEXT(interface, ifp); + struct zebra_if *zif; + int ret; + + zif = ifp->info; + + if (no) { + ret = zebra_evpn_es_lid_update(zif, 0); + if (ret == -1) { + vty_out(vty, "%%Failed to clear ES local id\n"); + return CMD_WARNING; + } + } else { + if (!zebra_evpn_is_if_es_capable(zif)) { + vty_out(vty, + "%%ESI cannot be associated with this interface type\n"); + return CMD_WARNING; + } + + if (!es_lid) { + vty_out(vty, "%%Specify local ES ID\n"); + return CMD_WARNING; + } + ret = zebra_evpn_es_lid_update(zif, es_lid); + if (ret == -1) { + vty_out(vty, + "%%ESI already exists on a different interface\n"); + return CMD_WARNING; + } + } + return CMD_SUCCESS; +} + +/*****************************************************************************/ +/* A base L2-VNI is maintained to derive parameters such as ES originator-IP. + * XXX: once single vxlan device model becomes available this will not be + * necessary + */ +/* called when a new vni is added or becomes oper up or becomes a bridge port */ +void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn) +{ + struct listnode *node; + struct zebra_evpn_es *es; + + if (zmh_info->es_base_evpn) { + if (zmh_info->es_base_evpn != zevpn) { + /* unrelated EVPN; ignore it */ + return; + } + /* check if the local vtep-ip has changed */ + } else { + /* check if the EVPN can be used as base EVPN */ + if (!zebra_evpn_send_to_client_ok(zevpn)) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es base vni set to %d", + zevpn->vni); + zmh_info->es_base_evpn = zevpn; + } + + /* update local VTEP-IP */ + if (zmh_info->es_originator_ip.s_addr == + zmh_info->es_base_evpn->local_vtep_ip.s_addr) + return; + + zmh_info->es_originator_ip.s_addr = + zmh_info->es_base_evpn->local_vtep_ip.s_addr; + + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es originator ip set to %s", + inet_ntoa(zmh_info->es_base_evpn->local_vtep_ip)); + + /* if originator ip changes we need to update bgp */ + for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, node, es)) { + if (es->flags & ZEBRA_EVPNES_READY_FOR_BGP) + zebra_evpn_es_send_add_to_client(es); + else + zebra_evpn_es_re_eval_send_to_client(es, + true /* es_evi_re_reval */); + } +} + +/* called when a vni is removed or becomes oper down or is removed from a + * bridge + */ +void zebra_evpn_es_clear_base_evpn(zebra_evpn_t *zevpn) +{ + struct listnode *node; + struct zebra_evpn_es *es; + + if (zmh_info->es_base_evpn != zevpn) + return; + + zmh_info->es_base_evpn = NULL; + /* lost current base EVPN; try to find a new one */ + zebra_evpn_es_get_one_base_evpn(); + + /* couldn't locate an eligible base evpn */ + if (!zmh_info->es_base_evpn && zmh_info->es_originator_ip.s_addr) { + if (IS_ZEBRA_DEBUG_EVPN_MH_ES) + zlog_debug("es originator ip cleared"); + + zmh_info->es_originator_ip.s_addr = 0; + /* lost originator ip */ + for (ALL_LIST_ELEMENTS_RO(zmh_info->local_es_list, node, es)) { + zebra_evpn_es_re_eval_send_to_client(es, + true /* es_evi_re_reval */); + } + } +} + +/* Locate an "eligible" L2-VNI to follow */ +static int zebra_evpn_es_get_one_base_evpn_cb(struct hash_bucket *b, void *data) +{ + zebra_evpn_t *zevpn = b->data; + + zebra_evpn_es_set_base_evpn(zevpn); + + if (zmh_info->es_base_evpn) + return HASHWALK_ABORT; + + return HASHWALK_CONTINUE; +} + +/* locate a base_evpn to follow for the purposes of common params like + * originator IP + */ +static void zebra_evpn_es_get_one_base_evpn(void) +{ + struct zebra_vrf *zvrf; + + zvrf = zebra_vrf_get_evpn(); + hash_walk(zvrf->evpn_table, zebra_evpn_es_get_one_base_evpn_cb, NULL); +} + +/*****************************************************************************/ +void zebra_evpn_mh_config_write(struct vty *vty) +{ + if (zmh_info->mac_hold_time != EVPN_MH_MAC_HOLD_TIME_DEF) + vty_out(vty, "evpn mh mac-holdtime %ld\n", + zmh_info->mac_hold_time); + + if (zmh_info->neigh_hold_time != EVPN_MH_NEIGH_HOLD_TIME_DEF) + vty_out(vty, "evpn mh neigh-holdtime %ld\n", + zmh_info->neigh_hold_time); +} + +int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty, + uint32_t duration, bool set_default) +{ + if (set_default) + zmh_info->neigh_hold_time = EVPN_MH_NEIGH_HOLD_TIME_DEF; + + zmh_info->neigh_hold_time = duration; + + return 0; +} + +int zebra_evpn_mh_mac_holdtime_update(struct vty *vty, + uint32_t duration, bool set_default) +{ + if (set_default) + duration = EVPN_MH_MAC_HOLD_TIME_DEF; + + zmh_info->mac_hold_time = duration; + + return 0; +} + +void zebra_evpn_interface_init(void) +{ + install_element(INTERFACE_NODE, &zebra_evpn_es_id_cmd); + install_element(INTERFACE_NODE, &zebra_evpn_es_sys_mac_cmd); +} + +void zebra_evpn_mh_init(void) +{ + zrouter.mh_info = XCALLOC(MTYPE_ZMH_INFO, sizeof(*zrouter.mh_info)); + + zmh_info->mac_hold_time = EVPN_MH_MAC_HOLD_TIME_DEF; + zmh_info->neigh_hold_time = EVPN_MH_NEIGH_HOLD_TIME_DEF; + /* setup ES tables */ + RB_INIT(zebra_es_rb_head, &zmh_info->es_rb_tree); + zmh_info->local_es_list = list_new(); + listset_app_node_mem(zmh_info->local_es_list); + + bf_init(zmh_info->nh_id_bitmap, EVPN_NH_ID_MAX); + bf_assign_zero_index(zmh_info->nh_id_bitmap); + + /* setup broadcast domain tables */ + zmh_info->evpn_vlan_table = hash_create(zebra_evpn_acc_vl_hash_keymake, + zebra_evpn_acc_vl_cmp, "access VLAN hash table"); +} + +void zebra_evpn_mh_terminate(void) +{ + list_delete(&zmh_info->local_es_list); + + hash_iterate(zmh_info->evpn_vlan_table, + zebra_evpn_acc_vl_cleanup_all, NULL); + hash_free(zmh_info->evpn_vlan_table); +} diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h new file mode 100644 index 0000000000..ed62677e3b --- /dev/null +++ b/zebra/zebra_evpn_mh.h @@ -0,0 +1,239 @@ +/* + * Zebra EVPN MH Data structures and definitions + * + * Copyright (C) 2019 Cumulus Networks, Inc. + * Anuradha Karuppiah + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _ZEBRA_EVPN_MH_H +#define _ZEBRA_EVPN_MH_H + +#include <zebra.h> + +#include "if.h" +#include "linklist.h" +#include "bitfield.h" +#include "zebra_vxlan.h" +#include "zebra_vxlan_private.h" + +#define EVPN_MH_VTY_STR "Multihoming\n" + +/* Ethernet Segment entry - + * - Local and remote ESs are maintained in a global RB tree, + * zmh_info->es_rb_tree using ESI as key + * - Local ESs are added via zebra config (ZEBRA_EVPNES_LOCAL) when an + * access port is associated with an ES-ID + * - Remotes ESs are added by BGP based on received/remote EAD/Type-1 routes + * (ZEBRA_EVPNES_REMOTE) + * - An ES can be simulatenously LOCAL and REMOTE; infact all LOCAL ESs are + * expected to have REMOTE ES peers. + */ +struct zebra_evpn_es { + esi_t esi; + char esi_str[ESI_STR_LEN]; + + /* ES flags */ + uint32_t flags; +#define ZEBRA_EVPNES_LOCAL (1 << 0) /* configured in zebra */ +#define ZEBRA_EVPNES_REMOTE (1 << 1) /* added by bgp */ +#define ZEBRA_EVPNES_OPER_UP (1 << 2) /* es->ifp is oper-up */ +#define ZEBRA_EVPNES_READY_FOR_BGP (1 << 3) /* ready to be sent to BGP */ +#define ZEBRA_EVPNES_NHG_ACTIVE (1 << 4) /* NHG has been installed */ + + /* memory used for adding the es to zmh_info->es_rb_tree */ + RB_ENTRY(zebra_evpn_es) rb_node; + + /* [EVPNES_LOCAL] memory used for linking the es to + * zmh_info->local_es_list + */ + struct listnode local_es_listnode; + + /* [EVPNES_LOCAL] corresponding interface */ + struct zebra_if *zif; + + /* list of ES-EVIs associated with the ES */ + struct list *es_evi_list; + + /* [!EVPNES_LOCAL] List of remote VTEPs (zebra_evpn_es_vtep) */ + struct list *es_vtep_list; + + /* list of zebra_mac entries using this ES as destination */ + struct list *mac_list; + + /* Nexthop group id */ + uint32_t nhg_id; +}; +RB_HEAD(zebra_es_rb_head, zebra_evpn_es); +RB_PROTOTYPE(zebra_es_rb_head, zebra_evpn_es, rb_node, zebra_es_rb_cmp); + +/* ES per-EVI info + * - ES-EVIs are maintained per-EVPN (vni->es_evi_rb_tree) + * - Local ES-EVIs are linked to per-EVPN list for quick access + * - Although some infrastucture is present for remote ES-EVIs, currently + * BGP does NOT send remote ES-EVIs to zebra. This may change in the + * future (but must be changed thoughtfully and only if needed as ES-EVI + * can get prolific and come in the way of rapid failovers) + */ +struct zebra_evpn_es_evi { + struct zebra_evpn_es *es; + zebra_evpn_t *zevpn; + + /* ES-EVI flags */ + uint32_t flags; + /* local ES-EVI */ +#define ZEBRA_EVPNES_EVI_LOCAL (1 << 0) /* created by zebra */ +#define ZEBRA_EVPNES_EVI_READY_FOR_BGP (1 << 1) /* ready to be sent to BGP */ + + /* memory used for adding the es_evi to + * es_evi->zevpn->es_evi_rb_tree + */ + RB_ENTRY(zebra_evpn_es_evi) rb_node; + /* memory used for linking the es_evi to + * es_evi->zevpn->local_es_evi_list + */ + struct listnode l2vni_listnode; + /* memory used for linking the es_evi to + * es_evi->es->es_evi_list + */ + struct listnode es_listnode; +}; + +/* PE attached to an ES */ +struct zebra_evpn_es_vtep { + struct zebra_evpn_es *es; /* parent ES */ + struct in_addr vtep_ip; + + /* memory used for adding the entry to es->es_vtep_list */ + struct listnode es_listnode; + + /* MAC nexthop */ + uint32_t nh_id; + + /* XXX - maintain a backpointer to zebra_vtep_t */ +}; + +/* Local/access-side broadcast domain - zebra_evpn_access_bd is added to - + * zrouter->evpn_vlan_table (for VLAN aware bridges) OR + * zrouter->evpn_bridge_table (for VLAN unaware bridges) + * XXX - support for VLAN unaware bridges is yet to be flushed out + */ +struct zebra_evpn_access_bd { + vlanid_t vid; + + struct zebra_if *vxlan_zif; /* vxlan device */ + /* list of members associated with the BD i.e. (potential) ESs */ + struct list *mbr_zifs; + /* presence of zevpn activates the EVI on all the ESs in mbr_zifs */ + zebra_evpn_t *zevpn; +}; + +/* multihoming information stored in zrouter */ +#define zmh_info (zrouter.mh_info) +struct zebra_evpn_mh_info { + /* RB tree of Ethernet segments (used for EVPN-MH) */ + struct zebra_es_rb_head es_rb_tree; + /* List of local ESs */ + struct list *local_es_list; + + /* EVPN MH broadcast domains indexed by the VID */ + struct hash *evpn_vlan_table; + + /* A base L2-VNI is maintained to derive parameters such as + * ES originator-IP. + * XXX: once single vxlan device model becomes available this will + * not be necessary + */ + zebra_evpn_t *es_base_evpn; + struct in_addr es_originator_ip; + + /* L2 NH and NHG ids - + * Most significant 8 bits is type. Lower 24 bits is the value + * allocated from the nh_id_bitmap. + */ + bitfield_t nh_id_bitmap; +#define EVPN_NH_ID_MAX (16*1024) +#define EVPN_NH_ID_VAL_MASK 0xffffff +#define EVPN_NH_ID_TYPE_POS 24 +/* The purpose of using different types for NHG and NH is NOT to manage the + * id space separately. It is simply to make debugging easier. + */ +#define EVPN_NH_ID_TYPE_BIT (1 << EVPN_NH_ID_TYPE_POS) +#define EVPN_NHG_ID_TYPE_BIT (2 << EVPN_NH_ID_TYPE_POS) + + /* XXX - re-visit the default hold timer value */ +#define EVPN_MH_MAC_HOLD_TIME_DEF (18 * 60) + long mac_hold_time; +#define EVPN_MH_NEIGH_HOLD_TIME_DEF (18 * 60) + long neigh_hold_time; +}; + +static inline bool zebra_evpn_mac_is_es_local(zebra_mac_t *mac) +{ + return mac->es && (mac->es->flags & ZEBRA_EVPNES_LOCAL); +} + +/* Returns true if the id is of L2-NHG or L2-NH type */ +static inline bool zebra_evpn_mh_is_fdb_nh(uint32_t id) +{ + return ((id & EVPN_NHG_ID_TYPE_BIT) || + (id & EVPN_NH_ID_TYPE_BIT)); +} + +/*****************************************************************************/ +extern esi_t *zero_esi; +extern void zebra_evpn_mh_init(void); +extern void zebra_evpn_mh_terminate(void); +extern bool zebra_evpn_is_if_es_capable(struct zebra_if *zif); +extern void zebra_evpn_if_init(struct zebra_if *zif); +extern void zebra_evpn_if_cleanup(struct zebra_if *zif); +extern void zebra_evpn_evpn_es_init(zebra_evpn_t *zevpn); +extern void zebra_evpn_evpn_es_cleanup(zebra_evpn_t *zevpn); +extern void zebra_evpn_vxl_evpn_set(struct zebra_if *zif, zebra_evpn_t *zevpn, + bool set); +extern void zebra_evpn_es_set_base_evpn(zebra_evpn_t *zevpn); +extern void zebra_evpn_es_clear_base_evpn(zebra_evpn_t *zevpn); +extern void zebra_evpn_vl_vxl_ref(uint16_t vid, struct zebra_if *vxlan_zif); +extern void zebra_evpn_vl_vxl_deref(uint16_t vid, struct zebra_if *vxlan_zif); +extern void zebra_evpn_vl_mbr_ref(uint16_t vid, struct zebra_if *zif); +extern void zebra_evpn_vl_mbr_deref(uint16_t vid, struct zebra_if *zif); +extern void zebra_evpn_es_send_all_to_client(bool add); +extern void zebra_evpn_es_if_oper_state_change(struct zebra_if *zif, bool up); +extern void zebra_evpn_es_show(struct vty *vty, bool uj); +extern void zebra_evpn_es_show_detail(struct vty *vty, bool uj); +extern void zebra_evpn_es_show_esi(struct vty *vty, bool uj, esi_t *esi); +extern void zebra_evpn_update_all_es(zebra_evpn_t *zevpn); +extern void zebra_evpn_proc_remote_es(ZAPI_HANDLER_ARGS); +extern void zebra_evpn_es_evi_show(struct vty *vty, bool uj, int detail); +extern void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, + vni_t vni, int detail); +extern void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac); +extern bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac, + struct zebra_evpn_es *es); +extern bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi); +extern struct zebra_evpn_es *zebra_evpn_es_find(esi_t *esi); +extern void zebra_evpn_interface_init(void); +extern int zebra_evpn_mh_if_write(struct vty *vty, struct interface *ifp); +extern void zebra_evpn_acc_vl_show(struct vty *vty, bool uj); +extern void zebra_evpn_acc_vl_show_detail(struct vty *vty, bool uj); +extern void zebra_evpn_acc_vl_show_vid(struct vty *vty, bool uj, vlanid_t vid); +extern void zebra_evpn_if_es_print(struct vty *vty, struct zebra_if *zif); +extern void zebra_evpn_es_cleanup(void); +extern int zebra_evpn_mh_mac_holdtime_update(struct vty *vty, + uint32_t duration, bool set_default); +void zebra_evpn_mh_config_write(struct vty *vty); +int zebra_evpn_mh_neigh_holdtime_update(struct vty *vty, + uint32_t duration, bool set_default); + +#endif /* _ZEBRA_EVPN_MH_H */ diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c new file mode 100644 index 0000000000..492052b1b2 --- /dev/null +++ b/zebra/zebra_evpn_neigh.c @@ -0,0 +1,2453 @@ +/* + * Zebra EVPN Neighbor code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include <zebra.h> + +#include "hash.h" +#include "interface.h" +#include "jhash.h" +#include "memory.h" +#include "prefix.h" +#include "vlan.h" +#include "json.h" + +#include "zebra/zserv.h" +#include "zebra/debug.h" +#include "zebra/zebra_router.h" +#include "zebra/rt.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_errors.h" +#include "zebra/zebra_vrf.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_neigh.h" +#include "zebra/zebra_evpn_mac.h" + +DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "EVI Neighbor"); + +/* + * Make hash key for neighbors. + */ +static unsigned int neigh_hash_keymake(const void *p) +{ + const zebra_neigh_t *n = p; + const struct ipaddr *ip = &n->ip; + + if (IS_IPADDR_V4(ip)) + return jhash_1word(ip->ipaddr_v4.s_addr, 0); + + return jhash2(ip->ipaddr_v6.s6_addr32, + array_size(ip->ipaddr_v6.s6_addr32), 0); +} + +/* + * Compare two neighbor hash structures. + */ +static bool neigh_cmp(const void *p1, const void *p2) +{ + const zebra_neigh_t *n1 = p1; + const zebra_neigh_t *n2 = p2; + + if (n1 == NULL && n2 == NULL) + return true; + + if (n1 == NULL || n2 == NULL) + return false; + + return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0); +} + +int neigh_list_cmp(void *p1, void *p2) +{ + const zebra_neigh_t *n1 = p1; + const zebra_neigh_t *n2 = p2; + + return memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)); +} + +struct hash *zebra_neigh_db_create(const char *desc) +{ + return hash_create(neigh_hash_keymake, neigh_cmp, desc); +} + +uint32_t num_dup_detected_neighs(zebra_evpn_t *zevpn) +{ + unsigned int i; + uint32_t num_neighs = 0; + struct hash *hash; + struct hash_bucket *hb; + zebra_neigh_t *nbr; + + hash = zevpn->neigh_table; + if (!hash) + return num_neighs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + nbr = (zebra_neigh_t *)hb->data; + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + num_neighs++; + } + } + + return num_neighs; +} + +/* + * Helper function to determine maximum width of neighbor IP address for + * display - just because we're dealing with IPv6 addresses that can + * widely vary. + */ +void zebra_evpn_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt) +{ + zebra_neigh_t *n; + char buf[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + int width; + + n = (zebra_neigh_t *)bucket->data; + + ipaddr2str(&n->ip, buf, sizeof(buf)); + width = strlen(buf); + if (width > wctx->addr_width) + wctx->addr_width = width; +} + +/* + * Count of remote neighbors referencing this MAC. + */ +int remote_neigh_count(zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + int count = 0; + + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + count++; + } + + return count; +} + +/* + * Install remote neighbor into the kernel. + */ +int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n, + bool was_static) +{ + struct interface *vlan_if; + int flags; + int ret = 0; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) + return 0; + + vlan_if = zevpn_map_to_svi(zevpn); + if (!vlan_if) + return -1; + + flags = DPLANE_NTF_EXT_LEARNED; + if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) + flags |= DPLANE_NTF_ROUTER; + ZEBRA_NEIGH_SET_ACTIVE(n); + + dplane_rem_neigh_add(vlan_if, &n->ip, &n->emac, flags, was_static); + + return ret; +} + +/* + * Install neighbor hash entry - called upon access VLAN change. + */ +void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_neigh_t *n; + struct neigh_walk_ctx *wctx = ctxt; + + n = (zebra_neigh_t *)bucket->data; + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + zebra_evpn_rem_neigh_install(wctx->zevpn, n, + false /*was_static*/); +} + +/* + * Callback to allocate neighbor hash entry. + */ +static void *zebra_evpn_neigh_alloc(void *p) +{ + const zebra_neigh_t *tmp_n = p; + zebra_neigh_t *n; + + n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); + *n = *tmp_n; + + return ((void *)n); +} + +static void zebra_evpn_local_neigh_ref_mac(zebra_neigh_t *n, + struct ethaddr *macaddr, + zebra_mac_t *mac, + bool send_mac_update) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_static; + bool new_static; + + memcpy(&n->emac, macaddr, ETH_ALEN); + n->mac = mac; + + /* Link to new MAC */ + if (!mac) + return; + + listnode_add_sort(mac->neigh_list, n); + if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) { + old_static = zebra_evpn_mac_is_static(mac); + ++mac->sync_neigh_cnt; + new_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh ref mac vni %u ip %s mac %s ref %d", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + mac->sync_neigh_cnt); + if ((old_static != new_static) && send_mac_update) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install( + mac, false /*set_inactive*/, + false /*force_clear_static*/, __func__); + } +} + +/* sync-path that is active on an ES peer */ +static void zebra_evpn_sync_neigh_dp_install(zebra_neigh_t *n, + bool set_inactive, + bool force_clear_static, + const char *caller) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + struct zebra_ns *zns; + struct interface *ifp; + bool set_static; + bool set_router; + + zns = zebra_ns_lookup(NS_DEFAULT); + ifp = if_lookup_by_index_per_ns(zns, n->ifindex); + if (!ifp) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "%s: dp-install sync-neigh vni %u ip %s mac %s if %d f 0x%x skipped", + caller, n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + n->ifindex, n->flags); + return; + } + + if (force_clear_static) + set_static = false; + else + set_static = zebra_evpn_neigh_is_static(n); + + set_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* XXX - this will change post integration with the new kernel */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + set_inactive = true; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "%s: dp-install sync-neigh vni %u ip %s mac %s if %s(%d) f 0x%x%s%s%s", + caller, n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + ifp->name, n->ifindex, n->flags, + set_router ? " router" : "", + set_static ? " static" : "", + set_inactive ? " inactive" : ""); + dplane_local_neigh_add(ifp, &n->ip, &n->emac, set_router, set_static, + set_inactive); +} + +/* + * Inform BGP about local neighbor addition. + */ +int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, + zebra_mac_t *zmac, uint32_t neigh_flags, + uint32_t seq) +{ + uint8_t flags = 0; + + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { + /* host reachability has not been verified locally */ + + /* if no ES peer is claiming reachability we can't advertise + * the entry + */ + if (!CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + return 0; + + /* ES peers are claiming reachability; we will + * advertise the entry but with a proxy flag + */ + SET_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT); + } + + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); + /* Set router flag (R-bit) based on local neigh entry add */ + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_SVI_IP)) + SET_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP); + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, ip, flags, seq, ZEBRA_NEIGH_ACTIVE, + zmac ? zmac->es : NULL, ZEBRA_MACIP_ADD); +} + +/* + * Inform BGP about local neighbor deletion. + */ +int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, uint32_t flags, + int state, bool force) +{ + if (!force) { + if (CHECK_FLAG(flags, ZEBRA_NEIGH_LOCAL_INACTIVE) + && !CHECK_FLAG(flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + /* the neigh was not advertised - nothing to delete */ + return 0; + } + + return zebra_evpn_macip_send_msg_to_client( + vni, macaddr, ip, flags, 0, state, NULL, ZEBRA_MACIP_DEL); +} + +static void zebra_evpn_neigh_send_add_del_to_client(zebra_neigh_t *n, + bool old_bgp_ready, + bool new_bgp_ready) +{ + if (new_bgp_ready) + zebra_evpn_neigh_send_add_to_client(n->zevpn->vni, &n->ip, + &n->emac, n->mac, n->flags, + n->loc_seq); + else if (old_bgp_ready) + zebra_evpn_neigh_send_del_to_client(n->zevpn->vni, &n->ip, + &n->emac, n->flags, + n->state, true /*force*/); +} + +/* if the static flag associated with the neigh changes we need + * to update the sync-neigh references against the MAC + * and inform the dataplane about the static flag changes. + */ +void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static, + bool new_n_static, bool defer_n_dp, + bool defer_mac_dp, const char *caller) +{ + zebra_mac_t *mac = n->mac; + bool old_mac_static; + bool new_mac_static; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (old_n_static == new_n_static) + return; + + /* update the neigh sync references in the dataplane. if + * the neigh is in the middle of updates the caller can + * request for a defer + */ + if (!defer_n_dp) + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + if (!mac) + return; + + /* update the mac sync ref cnt */ + old_mac_static = zebra_evpn_mac_is_static(mac); + if (new_n_static) { + ++mac->sync_neigh_cnt; + } else if (old_n_static) { + if (mac->sync_neigh_cnt) + --mac->sync_neigh_cnt; + } + new_mac_static = zebra_evpn_mac_is_static(mac); + + /* update the mac sync references in the dataplane */ + if ((old_mac_static != new_mac_static) && !defer_mac_dp) + zebra_evpn_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh ref-chg vni %u ip %s mac %s f 0x%x %d%s%s%s%s by %s", + n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags, mac->sync_neigh_cnt, + old_n_static ? " old_n_static" : "", + new_n_static ? " new_n_static" : "", + old_mac_static ? " old_mac_static" : "", + new_mac_static ? " new_mac_static" : "", caller); +} + +/* Neigh hold timer is used to age out peer-active flag. + * + * During this wait time we expect the dataplane component or an + * external neighmgr daemon to probe existing hosts to independently + * establish their presence on the ES. + */ +static int zebra_evpn_neigh_hold_exp_cb(struct thread *t) +{ + zebra_neigh_t *n; + bool old_bgp_ready; + bool new_bgp_ready; + bool old_n_static; + bool new_n_static; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + n = THREAD_ARG(t); + /* the purpose of the hold timer is to age out the peer-active + * flag + */ + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + return 0; + + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + new_n_static = zebra_evpn_neigh_is_static(n); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold expired", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + + /* re-program the local neigh in the dataplane if the neigh is no + * longer static + */ + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, false /*defer_n_dp*/, + false /*defer_mac_dp*/, __func__); + + /* inform bgp if needed */ + if (old_bgp_ready != new_bgp_ready) + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + return 0; +} + +static inline void zebra_evpn_neigh_start_hold_timer(zebra_neigh_t *n) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (n->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold start", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + thread_add_timer(zrouter.master, zebra_evpn_neigh_hold_exp_cb, n, + zmh_info->neigh_hold_time, &n->hold_timer); +} + +static void zebra_evpn_local_neigh_deref_mac(zebra_neigh_t *n, + bool send_mac_update) +{ + zebra_mac_t *mac = n->mac; + zebra_evpn_t *zevpn = n->zevpn; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_static; + bool new_static; + + n->mac = NULL; + if (!mac) + return; + + if ((n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) && mac->sync_neigh_cnt) { + old_static = zebra_evpn_mac_is_static(mac); + --mac->sync_neigh_cnt; + new_static = zebra_evpn_mac_is_static(mac); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh deref mac vni %u ip %s mac %s ref %d", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + mac->sync_neigh_cnt); + if ((old_static != new_static) && send_mac_update) + /* program the local mac in the kernel */ + zebra_evpn_sync_mac_dp_install( + mac, false /* set_inactive */, + false /* force_clear_static */, __func__); + } + + listnode_delete(mac->neigh_list, n); + zebra_evpn_deref_ip2mac(zevpn, mac); +} + +bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n, + struct ethaddr *macaddr, uint32_t seq) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + uint32_t tmp_seq; + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) + tmp_seq = n->loc_seq; + else + tmp_seq = n->rem_seq; + + if (seq < tmp_seq) { + /* if the neigh was never advertised to bgp we must accept + * whatever sequence number bgp sends + * XXX - check with Vivek + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) + && !zebra_evpn_neigh_is_ready_for_bgp(n)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-macip accept vni %u mac %s IP %s lower seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, + sizeof(macbuf)), + ipaddr2str(&n->ip, ipbuf, + sizeof(ipbuf)), + tmp_seq, n->flags); + return true; + } + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-macip ignore vni %u mac %s IP %s as existing has higher seq %u f 0x%x", + zevpn->vni, + prefix_mac2str(macaddr, macbuf, sizeof(macbuf)), + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + tmp_seq, n->flags); + return false; + } + + return true; +} + +/* + * Add neighbor entry. + */ +static zebra_neigh_t *zebra_evpn_neigh_add(zebra_evpn_t *zevpn, + struct ipaddr *ip, + struct ethaddr *mac, + zebra_mac_t *zmac, uint32_t n_flags) +{ + zebra_neigh_t tmp_n; + zebra_neigh_t *n = NULL; + + memset(&tmp_n, 0, sizeof(zebra_neigh_t)); + memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); + n = hash_get(zevpn->neigh_table, &tmp_n, zebra_evpn_neigh_alloc); + assert(n); + + n->state = ZEBRA_NEIGH_INACTIVE; + n->zevpn = zevpn; + n->dad_ip_auto_recovery_timer = NULL; + n->flags = n_flags; + + if (!zmac) + zmac = zebra_evpn_mac_lookup(zevpn, mac); + zebra_evpn_local_neigh_ref_mac(n, mac, zmac, + false /* send_mac_update */); + + return n; +} + +/* + * Delete neighbor entry. + */ +int zebra_evpn_neigh_del(zebra_evpn_t *zevpn, zebra_neigh_t *n) +{ + zebra_neigh_t *tmp_n; + + if (n->mac) + listnode_delete(n->mac->neigh_list, n); + + /* Cancel auto recovery */ + THREAD_OFF(n->dad_ip_auto_recovery_timer); + + /* Free the VNI hash entry and allocated memory. */ + tmp_n = hash_release(zevpn->neigh_table, n); + XFREE(MTYPE_NEIGH, tmp_n); + + return 0; +} + +void zebra_evpn_sync_neigh_del(zebra_neigh_t *n) +{ + bool old_n_static; + bool new_n_static; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh del vni %u ip %s mac %s f 0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + zebra_evpn_neigh_start_hold_timer(n); + new_n_static = zebra_evpn_neigh_is_static(n); + + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, false /*defer-dp*/, + false /*defer_mac_dp*/, __func__); +} + +zebra_neigh_t * +zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx) +{ + struct interface *ifp = NULL; + bool is_router; + zebra_mac_t *mac = ctx->mac; + uint32_t tmp_seq; + bool old_router = false; + bool old_bgp_ready = false; + bool new_bgp_ready; + bool inform_dataplane = false; + bool inform_bgp = false; + bool old_mac_static; + bool new_mac_static; + bool set_dp_inactive = false; + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool created; + ifindex_t ifindex = 0; + + /* locate l3-svi */ + ifp = zevpn_map_to_svi(zevpn); + if (ifp) + ifindex = ifp->ifindex; + + is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + old_mac_static = zebra_evpn_mac_is_static(mac); + + if (!n) { + uint32_t n_flags = 0; + + /* New neighbor - create */ + SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL); + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_PROXY); + else + SET_FLAG(n_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + SET_FLAG(n_flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr, mac, + n_flags); + n->ifindex = ifindex; + ZEBRA_NEIGH_SET_ACTIVE(n); + + created = true; + inform_dataplane = true; + inform_bgp = true; + set_dp_inactive = true; + } else { + bool mac_change; + uint32_t old_flags = n->flags; + bool old_n_static; + bool new_n_static; + + created = false; + old_n_static = zebra_evpn_neigh_is_static(n); + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + old_router = !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + mac_change = !!memcmp(&n->emac, &mac->macaddr, ETH_ALEN); + + /* deref and clear old info */ + if (mac_change) { + if (old_bgp_ready) { + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + old_bgp_ready = false; + } + if (n->mac) + zebra_evpn_local_neigh_deref_mac( + n, false /*send_mac_update*/); + } + /* clear old fwd info */ + n->rem_seq = 0; + n->r_vtep_ip.s_addr = 0; + + /* setup new flags */ + n->flags = 0; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + /* retain activity flag if the neigh was + * previously local + */ + if (old_flags & ZEBRA_NEIGH_LOCAL) { + n->flags |= (old_flags & ZEBRA_NEIGH_LOCAL_INACTIVE); + } else { + inform_dataplane = true; + set_dp_inactive = true; + n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; + } + + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + else + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT)) { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY); + /* if the neigh was peer-active previously we + * need to keep the flag and start the + * holdtimer on it. the peer-active flag is + * cleared on holdtimer expiry. + */ + if (CHECK_FLAG(old_flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + zebra_evpn_neigh_start_hold_timer(n); + } + } else { + SET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE); + /* stop hold timer if a peer has verified + * reachability + */ + zebra_evpn_neigh_stop_hold_timer(n); + } + ZEBRA_NEIGH_SET_ACTIVE(n); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH && (old_flags != n->flags)) + zlog_debug( + "sync-neigh vni %u ip %s mac %s old_f 0x%x new_f 0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + old_flags, n->flags); + + new_n_static = zebra_evpn_neigh_is_static(n); + if (mac_change) { + set_dp_inactive = true; + n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE; + inform_dataplane = true; + zebra_evpn_local_neigh_ref_mac( + n, &mac->macaddr, mac, + false /*send_mac_update*/); + } else if (old_n_static != new_n_static) { + inform_dataplane = true; + /* if static flags have changed without a mac change + * we need to create the correct sync-refs against + * the existing mac + */ + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, + true /*defer_dp*/, true /*defer_mac_dp*/, + __func__); + } + + /* Update the forwarding info. */ + if (n->ifindex != ifindex) { + n->ifindex = ifindex; + inform_dataplane = true; + } + } + + /* update the neigh seq. we don't bother with the mac seq as + * sync_mac_update already took care of that + */ + tmp_seq = MAX(n->loc_seq, seq); + if (tmp_seq != n->loc_seq) { + n->loc_seq = tmp_seq; + inform_bgp = true; + } + + /* Mark Router flag (R-bit) */ + if (is_router) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + if (old_router != is_router) + inform_dataplane = true; + + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + if (old_bgp_ready != new_bgp_ready) + inform_bgp = true; + + new_mac_static = zebra_evpn_mac_is_static(mac); + if ((old_mac_static != new_mac_static) || ctx->mac_dp_update_deferred) + zebra_evpn_sync_mac_dp_install(mac, ctx->mac_inactive, + false /* force_clear_static */, + __func__); + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync-neigh %s vni %u ip %s mac %s if %s(%d) seq %d f 0x%x%s%s", + created ? "created" : "updated", n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + ifp ? ifp->name : "", ifindex, n->loc_seq, n->flags, + inform_bgp ? " inform_bgp" : "", + inform_dataplane ? " inform_dp" : ""); + + if (inform_dataplane) + zebra_evpn_sync_neigh_dp_install(n, set_dp_inactive, + false /* force_clear_static */, + __func__); + + if (inform_bgp) + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + return n; +} + +/* + * Uninstall remote neighbor from the kernel. + */ +static int zebra_evpn_neigh_uninstall(zebra_evpn_t *zevpn, zebra_neigh_t *n) +{ + struct interface *vlan_if; + + if (!(n->flags & ZEBRA_NEIGH_REMOTE)) + return 0; + + vlan_if = zevpn_map_to_svi(zevpn); + if (!vlan_if) + return -1; + + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + + dplane_rem_neigh_delete(vlan_if, &n->ip); + + return 0; +} + +/* + * Free neighbor hash entry (callback) + */ +static void zebra_evpn_neigh_del_hash_entry(struct hash_bucket *bucket, + void *arg) +{ + struct neigh_walk_ctx *wctx = arg; + zebra_neigh_t *n = bucket->data; + + if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) + || ((wctx->flags & DEL_REMOTE_NEIGH) + && (n->flags & ZEBRA_NEIGH_REMOTE)) + || ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) + && (n->flags & ZEBRA_NEIGH_REMOTE) + && IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) { + if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL)) + zebra_evpn_neigh_send_del_to_client( + wctx->zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + + if (wctx->uninstall) { + if (zebra_evpn_neigh_is_static(n)) + zebra_evpn_sync_neigh_dp_install( + n, false /* set_inactive */, + true /* force_clear_static */, + __func__); + if ((n->flags & ZEBRA_NEIGH_REMOTE)) + zebra_evpn_neigh_uninstall(wctx->zevpn, n); + } + + zebra_evpn_neigh_del(wctx->zevpn, n); + } + + return; +} + +/* + * Delete all neighbor entries for this EVPN. + */ +void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall, + int upd_client, uint32_t flags) +{ + struct neigh_walk_ctx wctx; + + if (!zevpn->neigh_table) + return; + + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zevpn = zevpn; + wctx.uninstall = uninstall; + wctx.upd_client = upd_client; + wctx.flags = flags; + + hash_iterate(zevpn->neigh_table, zebra_evpn_neigh_del_hash_entry, + &wctx); +} + +/* + * Look up neighbor hash entry. + */ +zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip) +{ + zebra_neigh_t tmp; + zebra_neigh_t *n; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); + n = hash_lookup(zevpn->neigh_table, &tmp); + + return n; +} + +/* + * Process all neighbors associated with a MAC upon the MAC being learnt + * locally or undergoing any other change (such as sequence number). + */ +void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn, + zebra_mac_t *zmac, + bool seq_change, + bool es_change) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + struct zebra_vrf *zvrf = NULL; + char buf[ETHER_ADDR_STRLEN]; + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on local MAC %s %s, VNI %u", + prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), + seq_change ? "CHANGE" : "ADD", zevpn->vni); + + /* Walk all neighbors and mark any inactive local neighbors as + * active and/or update sequence number upon a move, and inform BGP. + * The action for remote neighbors is TBD. + * NOTE: We can't simply uninstall remote neighbors as the kernel may + * accidentally end up deleting a just-learnt local neighbor. + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change + || es_change) { + ZEBRA_NEIGH_SET_ACTIVE(n); + n->loc_seq = zmac->loc_seq; + if (!(zvrf->dup_addr_detect && zvrf->dad_freeze + && !!CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE))) + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, &n->ip, &n->emac, + n->mac, n->flags, n->loc_seq); + } + } + } +} + +/* + * Process all neighbors associated with a local MAC upon the MAC being + * deleted. + */ +void zebra_evpn_process_neigh_on_local_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u", + prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), + zevpn->vni); + + /* Walk all local neighbors and mark as inactive and inform + * BGP, if needed. + * TBD: There is currently no handling for remote neighbors. We + * don't expect them to exist, if they do, do we install the MAC + * as a remote MAC and the neighbor as remote? + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + ZEBRA_NEIGH_ACTIVE, false /*force*/); + } + } + } +} + +/* + * Process all neighbors associated with a MAC upon the MAC being remotely + * learnt. + */ +void zebra_evpn_process_neigh_on_remote_mac_add(zebra_evpn_t *zevpn, + zebra_mac_t *zmac) +{ + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + char buf[ETHER_ADDR_STRLEN]; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u", + prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), + zevpn->vni); + + /* Walk all local neighbors and mark as inactive and inform + * BGP, if needed. + */ + for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (IS_ZEBRA_NEIGH_ACTIVE(n)) { + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + ZEBRA_NEIGH_ACTIVE, false /* force */); + } + } + } +} + +/* + * Process all neighbors associated with a remote MAC upon the MAC being + * deleted. + */ +void zebra_evpn_process_neigh_on_remote_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac) +{ + /* NOTE: Currently a NO-OP. */ +} + +static inline void zebra_evpn_local_neigh_update_log( + const char *pfx, zebra_neigh_t *n, bool is_router, bool local_inactive, + bool old_bgp_ready, bool new_bgp_ready, bool inform_dataplane, + bool inform_bgp, const char *sfx) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (!IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + return; + + zlog_debug("%s neigh vni %u ip %s mac %s f 0x%x%s%s%s%s%s%s %s", pfx, + n->zevpn->vni, ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), n->flags, + is_router ? " router" : "", + local_inactive ? " local-inactive" : "", + old_bgp_ready ? " old_bgp_ready" : "", + new_bgp_ready ? " new_bgp_ready" : "", + inform_dataplane ? " inform_dp" : "", + inform_bgp ? " inform_bgp" : "", sfx); +} + +/* As part Duplicate Address Detection (DAD) for IP mobility + * MAC binding changes, ensure to inherit duplicate flag + * from MAC. + */ +static int zebra_evpn_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, + zebra_mac_t *old_zmac, + zebra_mac_t *new_zmac, + zebra_neigh_t *nbr) +{ + bool is_old_mac_dup = false; + bool is_new_mac_dup = false; + + if (!zvrf->dup_addr_detect) + return 0; + /* Check old or new MAC is detected as duplicate + * mark this neigh as duplicate + */ + if (old_zmac) + is_old_mac_dup = + CHECK_FLAG(old_zmac->flags, ZEBRA_MAC_DUPLICATE); + if (new_zmac) + is_new_mac_dup = + CHECK_FLAG(new_zmac->flags, ZEBRA_MAC_DUPLICATE); + /* Old and/or new MAC can be in duplicate state, + * based on that IP/Neigh Inherits the flag. + * If New MAC is marked duplicate, inherit to the IP. + * If old MAC is duplicate but new MAC is not, clear + * duplicate flag for IP and reset detection params + * and let IP DAD retrigger. + */ + if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + /* Mark neigh inactive */ + ZEBRA_NEIGH_SET_INACTIVE(nbr); + + return 1; + } else if (is_old_mac_dup && !is_new_mac_dup) { + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + } + return 0; +} + +static int zebra_evpn_dad_ip_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_neigh_t *nbr = NULL; + zebra_evpn_t *zevpn = NULL; + char buf1[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + + nbr = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + zvrf = vrf_info_lookup(nbr->zevpn->vrf_id); + if (!zvrf) + return 0; + + zevpn = zebra_evpn_lookup(nbr->zevpn->vni); + if (!zevpn) + return 0; + + nbr = zebra_evpn_neigh_lookup(zevpn, &nbr->ip); + if (!nbr) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired", + __func__, + prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), nbr->flags, + nbr->dad_count, zevpn->vni); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + nbr->dad_ip_auto_recovery_timer = NULL; + ZEBRA_NEIGH_SET_ACTIVE(nbr); + + /* Send to BGP */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip, + &nbr->emac, nbr->mac, + nbr->flags, nbr->loc_seq); + } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); + } + + return 0; +} + +static void +zebra_evpn_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, zebra_neigh_t *nbr, + struct in_addr vtep_ip, bool do_dad, + bool *is_dup_detect, bool is_local) +{ + + struct timeval elapsed = {0, 0}; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + bool reset_params = false; + + if (!zvrf->dup_addr_detect) + return; + + /* IP is detected as duplicate or inherit dup + * state, hold on to install as remote entry + * only if freeze is enabled. + */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u", + __func__, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + nbr->flags, nbr->dad_count, + zvrf->dad_freeze_time); + + if (zvrf->dad_freeze) + *is_dup_detect = true; + + /* warn-only action, neigh will be installed. + * freeze action, it wil not be installed. + */ + return; + } + + if (!do_dad) + return; + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + * During remote mac add, count should already be 1 + * via local learning. + */ + monotime_since(&nbr->detect_start_time, &elapsed); + reset_params = (elapsed.tv_sec > zvrf->dad_time); + + if (is_local && !reset_params) { + /* RFC-7432: A PE/VTEP that detects a MAC mobility + * event via LOCAL learning starts an M-second timer. + * + * NOTE: This is the START of the probe with count is + * 0 during LOCAL learn event. + */ + reset_params = !nbr->dad_count; + } + + if (reset_params) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u", + __func__, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + nbr->flags, nbr->dad_count); + /* Reset learn count but do not start detection + * during REMOTE learn event. + */ + nbr->dad_count = 0; + /* Start dup. addr detection (DAD) start time, + * ONLY during LOCAL learn. + */ + if (is_local) + monotime(&nbr->detect_start_time); + + } else if (!is_local) { + /* For REMOTE IP/Neigh, increment detection count + * ONLY while in probe window, once window passed, + * next local learn event should trigger DAD. + */ + nbr->dad_count++; + } + + /* For LOCAL IP/Neigh learn event, once count is reset above via either + * initial/start detection time or passed the probe time, the count + * needs to be incremented. + */ + if (is_local) + nbr->dad_count++; + + if (nbr->dad_count >= zvrf->dad_max_moves) { + flog_warn( + EC_ZEBRA_DUP_IP_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s", + nbr->zevpn->vni, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + is_local ? "local update, last" : "remote update, from", + inet_ntoa(vtep_ip)); + + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + + /* Start auto recovery timer for this IP */ + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + if (zvrf->dad_freeze && zvrf->dad_freeze_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start", + __func__, + prefix_mac2str(&nbr->emac, buf, + sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, + sizeof(buf1)), + nbr->flags, zvrf->dad_freeze_time); + + thread_add_timer(zrouter.master, + zebra_evpn_dad_ip_auto_recovery_exp, + nbr, zvrf->dad_freeze_time, + &nbr->dad_ip_auto_recovery_timer); + } + if (zvrf->dad_freeze) + *is_dup_detect = true; + } +} + +int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + bool is_router, bool local_inactive, + bool dp_static) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct zebra_vrf *zvrf; + zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL, *old_zmac = NULL; + uint32_t old_mac_seq = 0, mac_new_seq = 0; + bool upd_mac_seq = false; + bool neigh_mac_change = false; + bool neigh_on_hold = false; + bool neigh_was_remote = false; + bool do_dad = false; + struct in_addr vtep_ip = {.s_addr = 0}; + bool inform_dataplane = false; + bool created = false; + bool new_static = false; + bool old_bgp_ready = false; + bool new_bgp_ready; + + /* Check if the MAC exists. */ + zmac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!zmac) { + /* create a dummy MAC if the MAC is not already present */ + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("AUTO MAC %s created for neigh %s on VNI %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), + zevpn->vni); + + zmac = zebra_evpn_mac_add(zevpn, macaddr); + if (!zmac) { + zlog_debug("Failed to add MAC %s VNI %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + zevpn->vni); + return -1; + } + + memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info)); + memset(&zmac->flags, 0, sizeof(uint32_t)); + SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO); + } else { + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { + /* + * We don't change the MAC to local upon a neighbor + * learn event, we wait for the explicit local MAC + * learn. However, we have to compute its sequence + * number in preparation for when it actually turns + * local. + */ + upd_mac_seq = true; + } + } + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + if (!zvrf) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug(" Unable to find vrf for: %d", + zevpn->vxlan_if->vrf_id); + return -1; + } + + /* Check if the neighbor exists. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) { + /* New neighbor - create */ + n = zebra_evpn_neigh_add(zevpn, ip, macaddr, zmac, 0); + if (!n) { + flog_err( + EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", + ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, zevpn->vni); + return -1; + } + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + created = true; + } else { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + bool mac_different; + bool cur_is_router; + bool old_local_inactive; + + old_local_inactive = !!CHECK_FLAG( + n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + + /* Note any changes and see if of interest to BGP. */ + mac_different = !!memcmp(&n->emac, macaddr, ETH_ALEN); + cur_is_router = + !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + new_static = zebra_evpn_neigh_is_static(n); + if (!mac_different && is_router == cur_is_router + && old_local_inactive == local_inactive + && dp_static != new_static) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + " Ignoring entry mac is the same and is_router == cur_is_router"); + n->ifindex = ifp->ifindex; + return 0; + } + + old_zmac = n->mac; + if (!mac_different) { + /* XXX - cleanup this code duplication */ + bool is_neigh_freezed = false; + + /* Only the router flag has changed. */ + if (is_router) + SET_FLAG(n->flags, + ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, + ZEBRA_NEIGH_ROUTER_FLAG); + + if (local_inactive) + SET_FLAG(n->flags, + ZEBRA_NEIGH_LOCAL_INACTIVE); + else + UNSET_FLAG(n->flags, + ZEBRA_NEIGH_LOCAL_INACTIVE); + new_bgp_ready = + zebra_evpn_neigh_is_ready_for_bgp(n); + + /* Neigh is in freeze state and freeze action + * is enabled, do not send update to client. + */ + is_neigh_freezed = + (zvrf->dup_addr_detect + && zvrf->dad_freeze + && CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE)); + + zebra_evpn_local_neigh_update_log( + "local", n, is_router, local_inactive, + old_bgp_ready, new_bgp_ready, false, + false, "flag-update"); + + /* if the neigh can no longer be advertised + * remove it from bgp + */ + if (!is_neigh_freezed) { + zebra_evpn_neigh_send_add_del_to_client( + n, old_bgp_ready, + new_bgp_ready); + } else { + if (IS_ZEBRA_DEBUG_VXLAN + && IS_ZEBRA_NEIGH_ACTIVE(n)) + zlog_debug( + " Neighbor active and frozen"); + } + return 0; + } + + /* The MAC has changed, need to issue a delete + * first as this means a different MACIP route. + * Also, need to do some unlinking/relinking. + * We also need to update the MAC's sequence number + * in different situations. + */ + if (old_bgp_ready) { + zebra_evpn_neigh_send_del_to_client( + zevpn->vni, &n->ip, &n->emac, n->flags, + n->state, false /*force*/); + old_bgp_ready = false; + } + if (old_zmac) { + old_mac_seq = CHECK_FLAG(old_zmac->flags, + ZEBRA_MAC_REMOTE) + ? old_zmac->rem_seq + : old_zmac->loc_seq; + neigh_mac_change = upd_mac_seq = true; + zebra_evpn_local_neigh_deref_mac( + n, true /* send_mac_update */); + } + + /* if mac changes abandon peer flags and tell + * dataplane to clear the static flag + */ + if (zebra_evpn_neigh_clear_sync_info(n)) + inform_dataplane = true; + /* Update the forwarding info. */ + n->ifindex = ifp->ifindex; + + /* Link to new MAC */ + zebra_evpn_local_neigh_ref_mac( + n, macaddr, zmac, true /* send_mac_update */); + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + /* + * Neighbor has moved from remote to local. Its + * MAC could have also changed as part of the move. + */ + if (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) + != 0) { + old_zmac = n->mac; + if (old_zmac) { + old_mac_seq = + CHECK_FLAG(old_zmac->flags, + ZEBRA_MAC_REMOTE) + ? old_zmac->rem_seq + : old_zmac->loc_seq; + neigh_mac_change = upd_mac_seq = true; + zebra_evpn_local_neigh_deref_mac( + n, true /* send_update */); + } + + /* Link to new MAC */ + zebra_evpn_local_neigh_ref_mac( + n, macaddr, zmac, true /*send_update*/); + } + /* Based on Mobility event Scenario-B from the + * draft, neigh's previous state was remote treat this + * event for DAD. + */ + neigh_was_remote = true; + vtep_ip = n->r_vtep_ip; + /* Mark appropriately */ + UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + n->r_vtep_ip.s_addr = INADDR_ANY; + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + n->ifindex = ifp->ifindex; + } + } + + /* If MAC was previously remote, or the neighbor had a different + * MAC earlier, recompute the sequence number. + */ + if (upd_mac_seq) { + uint32_t seq1, seq2; + + seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE) + ? zmac->rem_seq + 1 + : zmac->loc_seq; + seq2 = neigh_mac_change ? old_mac_seq + 1 : 0; + mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ? MAX(seq1, seq2) + : zmac->loc_seq; + } + + if (local_inactive) + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + + /* Mark Router flag (R-bit) */ + if (is_router) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* if the dataplane thinks that this is a sync entry but + * zebra doesn't we need to re-concile the diff + * by re-installing the dataplane entry + */ + if (dp_static) { + new_static = zebra_evpn_neigh_is_static(n); + if (!new_static) + inform_dataplane = true; + } + + /* Check old and/or new MAC detected as duplicate mark + * the neigh as duplicate + */ + if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) { + flog_warn( + EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC", + zevpn->vni, prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + } + + /* For IP Duplicate Address Detection (DAD) is trigger, + * when the event is extended mobility based on scenario-B + * from the draft, IP/Neigh's MAC binding changed and + * neigh's previous state was remote. + */ + if (neigh_mac_change && neigh_was_remote) + do_dad = true; + + zebra_evpn_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad, + &neigh_on_hold, true); + + if (inform_dataplane) + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + /* Before we program this in BGP, we need to check if MAC is locally + * learnt. If not, force neighbor to be inactive and reset its seq. + */ + if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { + zebra_evpn_local_neigh_update_log( + "local", n, is_router, local_inactive, false, false, + inform_dataplane, false, "auto-mac"); + ZEBRA_NEIGH_SET_INACTIVE(n); + n->loc_seq = 0; + zmac->loc_seq = mac_new_seq; + return 0; + } + + zebra_evpn_local_neigh_update_log("local", n, is_router, local_inactive, + false, false, inform_dataplane, true, + created ? "created" : "updated"); + + /* If the MAC's sequence number has changed, inform the MAC and all + * neighbors associated with the MAC to BGP, else just inform this + * neighbor. + */ + if (upd_mac_seq && zmac->loc_seq != mac_new_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Seq changed for MAC %s VNI %u - old %u new %u", + prefix_mac2str(macaddr, buf, sizeof(buf)), + zevpn->vni, zmac->loc_seq, mac_new_seq); + zmac->loc_seq = mac_new_seq; + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, macaddr, + zmac->flags, + zmac->loc_seq, zmac->es)) + return -1; + zebra_evpn_process_neigh_on_local_mac_change(zevpn, zmac, 1, + 0 /*es_change*/); + return 0; + } + + n->loc_seq = zmac->loc_seq; + + if (!neigh_on_hold) { + ZEBRA_NEIGH_SET_ACTIVE(n); + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + } else { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug(" Neighbor on hold not sending"); + } + return 0; +} + +int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + uint16_t state) +{ + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + zebra_neigh_t *n = NULL; + zebra_mac_t *zmac = NULL; + + /* If the neighbor is unknown, there is no further action. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + /* If a remote entry, see if it needs to be refreshed */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { +#ifdef GNU_LINUX + if (state & NUD_STALE) + zebra_evpn_rem_neigh_install(zevpn, n, + false /*was_static*/); +#endif + } else { + /* We got a "remote" neighbor notification for an entry + * we think is local. This can happen in a multihoming + * scenario - but only if the MAC is already "remote". + * Just mark our entry as "remote". + */ + zmac = zebra_evpn_mac_lookup(zevpn, macaddr); + if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { + zlog_debug( + "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local", + ipaddr2str(&n->ip, buf2, sizeof(buf2)), + prefix_mac2str(macaddr, buf, sizeof(buf)), + zevpn->vni); + return -1; + } + + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + ZEBRA_NEIGH_SET_ACTIVE(n); + n->r_vtep_ip = zmac->fwd_info.r_vtep_ip; + } + + return 0; +} + +/* Notify Neighbor entries to the Client, skips the GW entry */ +static void +zebra_evpn_send_neigh_hash_entry_to_client(struct hash_bucket *bucket, + void *arg) +{ + struct mac_walk_ctx *wctx = arg; + zebra_neigh_t *zn = bucket->data; + zebra_mac_t *zmac = NULL; + + if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_DEF_GW)) + return; + + if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_LOCAL) + && IS_ZEBRA_NEIGH_ACTIVE(zn)) { + zmac = zebra_evpn_mac_lookup(wctx->zevpn, &zn->emac); + if (!zmac) + return; + + zebra_evpn_neigh_send_add_to_client(wctx->zevpn->vni, &zn->ip, + &zn->emac, zn->mac, + zn->flags, zn->loc_seq); + } +} + +/* Iterator of a specific EVPN */ +void zebra_evpn_send_neigh_to_client(zebra_evpn_t *zevpn) +{ + struct neigh_walk_ctx wctx; + + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zevpn = zevpn; + + hash_iterate(zevpn->neigh_table, + zebra_evpn_send_neigh_hash_entry_to_client, &wctx); +} + +void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct neigh_walk_ctx *wctx = ctxt; + zebra_neigh_t *nbr; + zebra_evpn_t *zevpn; + char buf[INET6_ADDRSTRLEN]; + + nbr = (zebra_neigh_t *)bucket->data; + if (!nbr) + return; + + zevpn = wctx->zevpn; + + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + return; + + if (IS_ZEBRA_DEBUG_VXLAN) { + ipaddr2str(&nbr->ip, buf, sizeof(buf)); + zlog_debug("%s: clear neigh %s dup state, flags 0x%x seq %u", + __func__, buf, nbr->flags, nbr->loc_seq); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_neigh_send_add_to_client(zevpn->vni, &nbr->ip, + &nbr->emac, nbr->mac, + nbr->flags, nbr->loc_seq); + } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); + } +} + +/* + * Print a specific neighbor entry. + */ +void zebra_evpn_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) +{ + struct vty *vty; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + const char *type_str; + const char *state_str; + bool flags_present = false; + struct zebra_vrf *zvrf = NULL; + struct timeval detect_start_time = {0, 0}; + char timebuf[MONOTIME_STRLEN]; + char thread_buf[THREAD_TIMER_STRLEN]; + + zvrf = zebra_vrf_get_evpn(); + if (!zvrf) + return; + + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ? "local" : "remote"; + state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; + vty = (struct vty *)ctxt; + if (json == NULL) { + bool sync_info = false; + + vty_out(vty, "IP: %s\n", + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + vty_out(vty, " Type: %s\n", type_str); + vty_out(vty, " State: %s\n", state_str); + vty_out(vty, " MAC: %s\n", + prefix_mac2str(&n->emac, buf1, sizeof(buf1))); + vty_out(vty, " Sync-info:"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) { + vty_out(vty, " local-inactive"); + sync_info = true; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) { + vty_out(vty, " peer-proxy"); + sync_info = true; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) { + vty_out(vty, " peer-active"); + sync_info = true; + } + if (n->hold_timer) { + vty_out(vty, " (ht: %s)", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); + sync_info = true; + } + if (!sync_info) + vty_out(vty, " -"); + vty_out(vty, "\n"); + } else { + json_object_string_add(json, "ip", buf2); + json_object_string_add(json, "type", type_str); + json_object_string_add(json, "state", state_str); + json_object_string_add(json, "mac", buf1); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + json_object_boolean_true_add(json, "localInactive"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY)) + json_object_boolean_true_add(json, "peerProxy"); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE)) + json_object_boolean_true_add(json, "peerActive"); + if (n->hold_timer) + json_object_string_add( + json, "peerActiveHold", + thread_timer_to_hhmmss(thread_buf, + sizeof(thread_buf), + n->hold_timer)); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if (n->mac->es) { + if (json) + json_object_string_add(json, "remoteEs", + n->mac->es->esi_str); + else + vty_out(vty, " Remote ES: %s\n", + n->mac->es->esi_str); + } else { + if (json) + json_object_string_add(json, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + else + vty_out(vty, " Remote VTEP: %s\n", + inet_ntoa(n->r_vtep_ip)); + } + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) { + if (!json) { + vty_out(vty, " Flags: Default-gateway"); + flags_present = true; + } else + json_object_boolean_true_add(json, "defaultGateway"); + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) { + if (!json) { + vty_out(vty, + flags_present ? " ,Router" : " Flags: Router"); + flags_present = true; + } + } + if (json == NULL) { + if (flags_present) + vty_out(vty, "\n"); + vty_out(vty, " Local Seq: %u Remote Seq: %u\n", n->loc_seq, + n->rem_seq); + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { + vty_out(vty, " Duplicate, detected at %s", + time_to_string(n->dad_dup_detect_time, + timebuf)); + } else if (n->dad_count) { + monotime_since(&n->detect_start_time, + &detect_start_time); + if (detect_start_time.tv_sec <= zvrf->dad_time) { + time_to_string(n->detect_start_time.tv_sec, + timebuf); + vty_out(vty, + " Duplicate detection started at %s, detection count %u\n", + timebuf, n->dad_count); + } + } + } else { + json_object_int_add(json, "localSequence", n->loc_seq); + json_object_int_add(json, "remoteSequence", n->rem_seq); + json_object_int_add(json, "detectionCount", n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json, "isDuplicate"); + else + json_object_boolean_false_add(json, "isDuplicate"); + } +} + +void zebra_evpn_print_neigh_hdr(struct vty *vty, struct neigh_walk_ctx *wctx) +{ + vty_out(vty, "Flags: I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %s\n", -wctx->addr_width, + "Neighbor", "Type", "Flags", "State", "MAC", "Remote ES/VTEP", + "Seq #'s"); +} + +static char *zebra_evpn_print_neigh_flags(zebra_neigh_t *n, char *flags_buf, + uint32_t flags_buf_sz) +{ + snprintf(flags_buf, flags_buf_sz, "%s%s%s", + (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE) ? + "P" : "", + (n->flags & ZEBRA_NEIGH_ES_PEER_PROXY) ? + "X" : "", + (n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) ? + "I" : ""); + + return flags_buf; +} + +/* + * Print neighbor hash entry - called for display of all neighbors. + */ +void zebra_evpn_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_evpn = NULL, *json_row = NULL; + zebra_neigh_t *n; + char buf1[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + const char *state_str; + char flags_buf[6]; + + vty = wctx->vty; + json_evpn = wctx->json; + n = (zebra_neigh_t *)bucket->data; + + if (json_evpn) + json_row = json_object_new_object(); + + prefix_mac2str(&n->emac, buf1, sizeof(buf1)); + ipaddr2str(&n->ip, buf2, sizeof(buf2)); + state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + return; + + if (json_evpn == NULL) { + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", + -wctx->addr_width, buf2, "local", + zebra_evpn_print_neigh_flags(n, flags_buf, + sizeof(flags_buf)), state_str, buf1, + "", n->loc_seq, n->rem_seq); + } else { + json_object_string_add(json_row, "type", "local"); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "mac", buf1); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) + json_object_boolean_true_add(json_row, + "defaultGateway"); + json_object_int_add(json_row, "localSequence", + n->loc_seq); + json_object_int_add(json_row, "remoteSequence", + n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); + } + wctx->count++; + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + && !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) + return; + + if (json_evpn == NULL) { + if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) + && (wctx->count == 0)) + zebra_evpn_print_neigh_hdr(vty, wctx); + vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n", + -wctx->addr_width, buf2, "remote", + zebra_evpn_print_neigh_flags(n, flags_buf, + sizeof(flags_buf)), state_str, buf1, + n->mac->es ? n->mac->es->esi_str + : inet_ntoa(n->r_vtep_ip), + n->loc_seq, n->rem_seq); + } else { + json_object_string_add(json_row, "type", "remote"); + json_object_string_add(json_row, "state", state_str); + json_object_string_add(json_row, "mac", buf1); + if (n->mac->es) + json_object_string_add(json_row, "remoteEs", + n->mac->es->esi_str); + else + json_object_string_add(json_row, "remoteVtep", + inet_ntoa(n->r_vtep_ip)); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) + json_object_boolean_true_add(json_row, + "defaultGateway"); + json_object_int_add(json_row, "localSequence", + n->loc_seq); + json_object_int_add(json_row, "remoteSequence", + n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); + } + wctx->count++; + } + + if (json_evpn) + json_object_object_add(json_evpn, buf2, json_row); +} + +/* + * Print neighbor hash entry in detail - called for display of all neighbors. + */ +void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt) +{ + struct vty *vty; + json_object *json_evpn = NULL, *json_row = NULL; + zebra_neigh_t *n; + char buf[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + + vty = wctx->vty; + json_evpn = wctx->json; + n = (zebra_neigh_t *)bucket->data; + if (!n) + return; + + ipaddr2str(&n->ip, buf, sizeof(buf)); + if (json_evpn) + json_row = json_object_new_object(); + + zebra_evpn_print_neigh(n, vty, json_row); + + if (json_evpn) + json_object_object_add(json_evpn, buf, json_row); +} + +void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt) +{ + zebra_neigh_t *nbr; + + nbr = (zebra_neigh_t *)bucket->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zebra_evpn_print_neigh_hash(bucket, ctxt); +} + +void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket, + void *ctxt) +{ + zebra_neigh_t *nbr; + + nbr = (zebra_neigh_t *)bucket->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zebra_evpn_print_neigh_hash_detail(bucket, ctxt); +} + +void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ipaddr *ipaddr, zebra_mac_t *mac, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq) +{ + zebra_neigh_t *n; + int update_neigh = 0; + uint32_t tmp_seq; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + zebra_mac_t *old_mac = NULL; + bool old_static = false; + bool do_dad = false; + bool is_dup_detect = false; + bool is_router; + + assert(mac); + is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); + + /* Check if the remote neighbor itself is unknown or has a + * change. If so, create or update and then install the entry. + */ + n = zebra_evpn_neigh_lookup(zevpn, ipaddr); + if (!n || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) + || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG) + || (memcmp(&n->emac, &mac->macaddr, sizeof(struct ethaddr)) != 0) + || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip) || seq != n->rem_seq) + update_neigh = 1; + + if (update_neigh) { + if (!n) { + n = zebra_evpn_neigh_add(zevpn, ipaddr, &mac->macaddr, + mac, 0); + if (!n) { + zlog_warn( + "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s", + ipaddr2str(ipaddr, buf1, sizeof(buf1)), + prefix_mac2str(&mac->macaddr, buf, + sizeof(buf)), + zevpn->vni, inet_ntoa(vtep_ip)); + return; + } + + } else { + const char *n_type; + + /* When host moves but changes its (MAC,IP) + * binding, BGP may install a MACIP entry that + * corresponds to "older" location of the host + * in transient situations (because {IP1,M1} + * is a different route from {IP1,M2}). Check + * the sequence number and ignore this update + * if appropriate. + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + tmp_seq = n->loc_seq; + n_type = "local"; + } else { + tmp_seq = n->rem_seq; + n_type = "remote"; + } + if (seq < tmp_seq) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s Neigh has higher seq %u", + zevpn->vni, + prefix_mac2str(&mac->macaddr, + buf, + sizeof(buf)), + " IP ", + ipaddr2str(ipaddr, buf1, + sizeof(buf1)), + n_type, tmp_seq); + return; + } + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + old_static = zebra_evpn_neigh_is_static(n); + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug( + "sync->remote neigh vni %u ip %s mac %s seq %d f0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, buf1, + sizeof(buf1)), + prefix_mac2str(&n->emac, buf, + sizeof(buf)), + seq, n->flags); + zebra_evpn_neigh_clear_sync_info(n); + if (IS_ZEBRA_NEIGH_ACTIVE(n)) + zebra_evpn_mac_send_del_to_client( + zevpn->vni, &mac->macaddr, + mac->flags, false /*force*/); + } + if (memcmp(&n->emac, &mac->macaddr, + sizeof(struct ethaddr)) + != 0) { + /* update neigh list for macs */ + old_mac = + zebra_evpn_mac_lookup(zevpn, &n->emac); + if (old_mac) { + listnode_delete(old_mac->neigh_list, n); + n->mac = NULL; + zebra_evpn_deref_ip2mac(zevpn, old_mac); + } + n->mac = mac; + listnode_add_sort(mac->neigh_list, n); + memcpy(&n->emac, &mac->macaddr, ETH_ALEN); + + /* Check Neigh's curent state is local + * (this is the case where neigh/host has moved + * from L->R) and check previous detction + * started via local learning. + * + * RFC-7432: A PE/VTEP that detects a MAC + * mobilit event via local learning starts + * an M-second timer. + * VTEP-IP or seq. change along is not + * considered for dup. detection. + * + * Mobilty event scenario-B IP-MAC binding + * changed. + */ + if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + && n->dad_count) + do_dad = true; + } + } + + /* Set "remote" forwarding info. */ + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS); + n->r_vtep_ip = vtep_ip; + SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); + + /* Set router flag (R-bit) to this Neighbor entry */ + if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG)) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + else + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + /* Check old or new MAC detected as duplicate, + * inherit duplicate flag to this neigh. + */ + if (zebra_evpn_ip_inherit_dad_from_mac(zvrf, old_mac, mac, n)) { + flog_warn( + EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC", + zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(&n->ip, buf1, sizeof(buf1))); + } + + /* Check duplicate address detection for IP */ + zebra_evpn_dup_addr_detect_for_neigh( + zvrf, n, n->r_vtep_ip, do_dad, &is_dup_detect, false); + /* Install the entry. */ + if (!is_dup_detect) + zebra_evpn_rem_neigh_install(zevpn, n, old_static); + } + + /* Update seq number. */ + n->rem_seq = seq; +} + +int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t *mac) +{ + zebra_neigh_t *n; + char buf[ETHER_ADDR_STRLEN]; + char buf2[INET6_ADDRSTRLEN]; + + assert(mac); + + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) { + n = zebra_evpn_neigh_add(zevpn, ip, &mac->macaddr, mac, 0); + if (!n) { + flog_err( + EC_ZEBRA_MAC_ADD_FAILED, + "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", + ipaddr2str(ip, buf2, sizeof(buf2)), + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ifp->name, ifp->ifindex, zevpn->vni); + return -1; + } + } + + /* Set "local" forwarding info. */ + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); + ZEBRA_NEIGH_SET_ACTIVE(n); + memcpy(&n->emac, &mac->macaddr, ETH_ALEN); + n->ifindex = ifp->ifindex; + + /* Only advertise in BGP if the knob is enabled */ + if (advertise_gw_macip_enabled(zevpn)) { + + SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); + SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW); + /* Set Router flag (R-bit) */ + if (ip->ipa_type == IPADDR_V6) + SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x", + ifp->name, ifp->ifindex, zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); + + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq); + } else if (advertise_svi_macip_enabled(zevpn)) { + + SET_FLAG(n->flags, ZEBRA_NEIGH_SVI_IP); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x", + ifp->name, ifp->ifindex, zevpn->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); + + zebra_evpn_neigh_send_add_to_client( + zevpn->vni, ip, &n->emac, n->mac, n->flags, n->loc_seq); + } + + return 0; +} + +void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn, + struct zebra_vrf *zvrf, zebra_neigh_t *n, + zebra_mac_t *mac, struct ipaddr *ipaddr) +{ + char buf1[INET6_ADDRSTRLEN]; + + if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE) + && CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) + && (memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN) == 0)) { + struct interface *vlan_if; + + vlan_if = zevpn_map_to_svi(zevpn); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry", + __func__, + ipaddr2str(ipaddr, buf1, sizeof(buf1)), + n->flags, vlan_if ? vlan_if->name : "Unknown"); + if (vlan_if) + neigh_read_specific_ip(ipaddr, vlan_if); + } + + /* When the MAC changes for an IP, it is possible the + * client may update the new MAC before trying to delete the + * "old" neighbor (as these are two different MACIP routes). + * Do the delete only if the MAC matches. + */ + if (!memcmp(n->emac.octet, mac->macaddr.octet, ETH_ALEN)) { + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { + zebra_evpn_sync_neigh_del(n); + } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_neigh_uninstall(zevpn, n); + zebra_evpn_neigh_del(zevpn, n); + zebra_evpn_deref_ip2mac(zevpn, mac); + } + } +} + +int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip) +{ + zebra_neigh_t *n; + zebra_mac_t *zmac; + bool old_bgp_ready; + bool new_bgp_ready; + char buf[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + struct zebra_vrf *zvrf; + + /* If entry doesn't exist, nothing to do. */ + n = zebra_evpn_neigh_lookup(zevpn, ip); + if (!n) + return 0; + + zmac = zebra_evpn_mac_lookup(zevpn, &n->emac); + if (!zmac) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "Trying to del a neigh %s without a mac %s on VNI %u", + ipaddr2str(ip, buf, sizeof(buf)), + prefix_mac2str(&n->emac, buf2, sizeof(buf2)), + zevpn->vni); + + return 0; + } + + /* If it is a remote entry, the kernel has aged this out or someone has + * deleted it, it needs to be re-installed as Quagga is the owner. + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { + zebra_evpn_rem_neigh_install(zevpn, n, false /*was_static*/); + return 0; + } + + /* if this is a sync entry it cannot be dropped re-install it in + * the dataplane + */ + old_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + if (zebra_evpn_neigh_is_static(n)) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("re-add sync neigh vni %u ip %s mac %s 0x%x", + n->zevpn->vni, + ipaddr2str(&n->ip, buf, sizeof(buf)), + prefix_mac2str(&n->emac, buf2, sizeof(buf2)), + n->flags); + + if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE)) + SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL_INACTIVE); + /* inform-bgp about change in local-activity if any */ + new_bgp_ready = zebra_evpn_neigh_is_ready_for_bgp(n); + zebra_evpn_neigh_send_add_del_to_client(n, old_bgp_ready, + new_bgp_ready); + + /* re-install the entry in the kernel */ + zebra_evpn_sync_neigh_dp_install(n, false /* set_inactive */, + false /* force_clear_static */, + __func__); + + return 0; + } + + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); + if (!zvrf) { + zlog_debug("%s: VNI %u vrf lookup failed.", __func__, + zevpn->vni); + return -1; + } + + /* In case of feeze action, if local neigh is in duplicate state, + * Mark the Neigh as inactive before sending delete request to BGPd, + * If BGPd has remote entry, it will re-install + */ + if (zvrf->dad_freeze && CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + ZEBRA_NEIGH_SET_INACTIVE(n); + + /* Remove neighbor from BGP. */ + zebra_evpn_neigh_send_del_to_client(zevpn->vni, &n->ip, &n->emac, + n->flags, n->state, + false /* force */); + + /* Delete this neighbor entry. */ + zebra_evpn_neigh_del(zevpn, n); + + /* see if the AUTO mac needs to be deleted */ + if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) + && !listcount(zmac->neigh_list)) + zebra_evpn_mac_del(zevpn, zmac); + + return 0; +} diff --git a/zebra/zebra_evpn_neigh.h b/zebra/zebra_evpn_neigh.h new file mode 100644 index 0000000000..4b98266c86 --- /dev/null +++ b/zebra/zebra_evpn_neigh.h @@ -0,0 +1,294 @@ +/* + * Zebra EVPN Neighbor Data structures and definitions + * These are "internal" to this function. + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * Copyright (C) 2020 Volta Networks. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef _ZEBRA_EVPN_NEIGH_H +#define _ZEBRA_EVPN_NEIGH_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct zebra_neigh_t_ zebra_neigh_t; + +#define IS_ZEBRA_NEIGH_ACTIVE(n) (n->state == ZEBRA_NEIGH_ACTIVE) + +#define IS_ZEBRA_NEIGH_INACTIVE(n) (n->state == ZEBRA_NEIGH_INACTIVE) + +#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE + +#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE + +/* + * Neighbor hash table. + * + * This table contains the neighbors (IP to MAC bindings) pertaining to + * this VNI. This includes local neighbors learnt on the attached VLAN + * device that maps to this VNI as well as remote neighbors learnt and + * installed by BGP. + * Local neighbors will be known against the VLAN device (SVI); however, + * it is sufficient for zebra to maintain against the VNI. The correct + * VNI will be obtained as zebra maintains the mapping (of VLAN to VNI). + */ +struct zebra_neigh_t_ { + /* IP address. */ + struct ipaddr ip; + + /* MAC address. */ + struct ethaddr emac; + + /* Back pointer to MAC. Only applicable to hosts in a L2-VNI. */ + zebra_mac_t *mac; + + /* Underlying interface. */ + ifindex_t ifindex; + + zebra_evpn_t *zevpn; + + uint32_t flags; +#define ZEBRA_NEIGH_LOCAL 0x01 +#define ZEBRA_NEIGH_REMOTE 0x02 +#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ +#define ZEBRA_NEIGH_DEF_GW 0x08 +#define ZEBRA_NEIGH_ROUTER_FLAG 0x10 +#define ZEBRA_NEIGH_DUPLICATE 0x20 +#define ZEBRA_NEIGH_SVI_IP 0x40 +/* rxed from an ES peer */ +#define ZEBRA_NEIGH_ES_PEER_ACTIVE 0x80 +/* rxed from an ES peer as a proxy advertisement */ +#define ZEBRA_NEIGH_ES_PEER_PROXY 0x100 +/* We have not been able to independently establish that the host + * is local connected + */ +#define ZEBRA_NEIGH_LOCAL_INACTIVE 0x200 +#define ZEBRA_NEIGH_ALL_LOCAL_FLAGS \ + (ZEBRA_NEIGH_LOCAL | ZEBRA_NEIGH_LOCAL_INACTIVE) +#define ZEBRA_NEIGH_ALL_PEER_FLAGS \ + (ZEBRA_NEIGH_ES_PEER_PROXY | ZEBRA_NEIGH_ES_PEER_ACTIVE) + + enum zebra_neigh_state state; + + /* Remote VTEP IP - applicable only for remote neighbors. */ + struct in_addr r_vtep_ip; + + /* + * Mobility sequence numbers associated with this entry. The rem_seq + * represents the sequence number from the client (BGP) for the most + * recent add or update of this entry while the loc_seq represents + * the sequence number informed (or to be informed) by zebra to BGP + * for this entry. + */ + uint32_t rem_seq; + uint32_t loc_seq; + + /* list of hosts pointing to this remote NH entry */ + struct host_rb_tree_entry host_rb; + + /* Duplicate ip detection */ + uint32_t dad_count; + + struct thread *dad_ip_auto_recovery_timer; + + struct timeval detect_start_time; + + time_t dad_dup_detect_time; + + /* used for ageing out the PEER_ACTIVE flag */ + struct thread *hold_timer; +}; + +/* + * Context for neighbor hash walk - used by callbacks. + */ +struct neigh_walk_ctx { + zebra_evpn_t *zevpn; /* VNI hash */ + struct zebra_vrf *zvrf; /* VRF - for client notification. */ + int uninstall; /* uninstall from kernel? */ + int upd_client; /* uninstall from client? */ + + uint32_t flags; +#define DEL_LOCAL_NEIGH 0x1 +#define DEL_REMOTE_NEIGH 0x2 +#define DEL_ALL_NEIGH (DEL_LOCAL_NEIGH | DEL_REMOTE_NEIGH) +#define DEL_REMOTE_NEIGH_FROM_VTEP 0x4 +#define SHOW_REMOTE_NEIGH_FROM_VTEP 0x8 + + struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */ + + struct vty *vty; /* Used by VTY handlers */ + uint32_t count; /* Used by VTY handlers */ + uint8_t addr_width; /* Used by VTY handlers */ + struct json_object *json; /* Used for JSON Output */ +}; + +/**************************** SYNC neigh handling **************************/ +static inline bool zebra_evpn_neigh_is_static(zebra_neigh_t *neigh) +{ + return !!(neigh->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS); +} + +static inline bool zebra_evpn_neigh_is_ready_for_bgp(zebra_neigh_t *n) +{ + bool mac_ready; + bool neigh_ready; + + mac_ready = !!(n->mac->flags & ZEBRA_MAC_LOCAL); + neigh_ready = + ((n->flags & ZEBRA_NEIGH_LOCAL) && IS_ZEBRA_NEIGH_ACTIVE(n) + && (!(n->flags & ZEBRA_NEIGH_LOCAL_INACTIVE) + || (n->flags & ZEBRA_NEIGH_ES_PEER_ACTIVE))) + ? true + : false; + + return mac_ready && neigh_ready; +} + +static inline void zebra_evpn_neigh_stop_hold_timer(zebra_neigh_t *n) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + + if (!n->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x hold stop", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, sizeof(macbuf)), + n->flags); + THREAD_OFF(n->hold_timer); +} + +void zebra_evpn_sync_neigh_static_chg(zebra_neigh_t *n, bool old_n_static, + bool new_n_static, bool defer_n_dp, + bool defer_mac_dp, const char *caller); + +static inline bool zebra_evpn_neigh_clear_sync_info(zebra_neigh_t *n) +{ + char macbuf[ETHER_ADDR_STRLEN]; + char ipbuf[INET6_ADDRSTRLEN]; + bool old_n_static = false; + bool new_n_static = false; + + if (n->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS) { + if (IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) + zlog_debug("sync-neigh vni %u ip %s mac %s 0x%x clear", + n->zevpn->vni, + ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)), + prefix_mac2str(&n->emac, macbuf, + sizeof(macbuf)), + n->flags); + + old_n_static = zebra_evpn_neigh_is_static(n); + UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_PEER_FLAGS); + new_n_static = zebra_evpn_neigh_is_static(n); + if (old_n_static != new_n_static) + zebra_evpn_sync_neigh_static_chg( + n, old_n_static, new_n_static, + true /*defer_dp)*/, false /*defer_mac_dp*/, + __func__); + } + zebra_evpn_neigh_stop_hold_timer(n); + + /* if the neigh static flag changed inform that a dp + * re-install maybe needed + */ + return old_n_static != new_n_static; +} + +int remote_neigh_count(zebra_mac_t *zmac); + +int neigh_list_cmp(void *p1, void *p2); +struct hash *zebra_neigh_db_create(const char *desc); +uint32_t num_dup_detected_neighs(zebra_evpn_t *zevpn); +void zebra_evpn_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt); +int remote_neigh_count(zebra_mac_t *zmac); +int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n, + bool was_static); +void zebra_evpn_install_neigh_hash(struct hash_bucket *bucket, void *ctxt); +int zebra_evpn_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, + zebra_mac_t *zmac, uint32_t neigh_flags, + uint32_t seq); +int zebra_evpn_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, + struct ethaddr *macaddr, uint32_t flags, + int state, bool force); +bool zebra_evpn_neigh_is_bgp_seq_ok(zebra_evpn_t *zevpn, zebra_neigh_t *n, + struct ethaddr *macaddr, uint32_t seq); +int zebra_evpn_neigh_del(zebra_evpn_t *zevpn, zebra_neigh_t *n); +void zebra_evpn_sync_neigh_del(zebra_neigh_t *n); +zebra_neigh_t * +zebra_evpn_proc_sync_neigh_update(zebra_evpn_t *zevpn, zebra_neigh_t *n, + uint16_t ipa_len, struct ipaddr *ipaddr, + uint8_t flags, uint32_t seq, esi_t *esi, + struct sync_mac_ip_ctx *ctx); +void zebra_evpn_neigh_del_all(zebra_evpn_t *zevpn, int uninstall, + int upd_client, uint32_t flags); +zebra_neigh_t *zebra_evpn_neigh_lookup(zebra_evpn_t *zevpn, struct ipaddr *ip); + +int zebra_evpn_rem_neigh_install(zebra_evpn_t *zevpn, zebra_neigh_t *n, + bool was_static); +void zebra_evpn_process_neigh_on_remote_mac_add(zebra_evpn_t *zevpn, + zebra_mac_t *zmac); +void zebra_evpn_process_neigh_on_local_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac); +void zebra_evpn_process_neigh_on_local_mac_change(zebra_evpn_t *zevpn, + zebra_mac_t *zmac, + bool seq_change, + bool es_change); +void zebra_evpn_process_neigh_on_remote_mac_del(zebra_evpn_t *zevpn, + zebra_mac_t *zmac); +int zebra_evpn_local_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + bool is_router, bool local_inactive, + bool dp_static); +int zebra_evpn_remote_neigh_update(zebra_evpn_t *zevpn, struct interface *ifp, + struct ipaddr *ip, struct ethaddr *macaddr, + uint16_t state); +void zebra_evpn_send_neigh_to_client(zebra_evpn_t *zevpn); +void zebra_evpn_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); +void zebra_evpn_print_neigh_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_neigh_hdr(struct vty *vty, struct neigh_walk_ctx *wctx); +void zebra_evpn_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt); +void zebra_evpn_print_dad_neigh_hash_detail(struct hash_bucket *bucket, + void *ctxt); +void process_neigh_remote_macip_add(zebra_evpn_t *zevpn, struct zebra_vrf *zvrf, + struct ipaddr *ipaddr, zebra_mac_t *mac, + struct in_addr vtep_ip, uint8_t flags, + uint32_t seq); +int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, zebra_evpn_t *zevpn, + struct ipaddr *ip, zebra_mac_t *mac); +void zebra_evpn_neigh_remote_uninstall(zebra_evpn_t *zevpn, + struct zebra_vrf *zvrf, zebra_neigh_t *n, + zebra_mac_t *mac, struct ipaddr *ipaddr); +int zebra_evpn_neigh_del_ip(zebra_evpn_t *zevpn, struct ipaddr *ip); + + +#ifdef __cplusplus +} +#endif + +#endif /*_ZEBRA_EVPN_NEIGH_H */ diff --git a/zebra/zebra_evpn_vxlan.h b/zebra/zebra_evpn_vxlan.h new file mode 100644 index 0000000000..bf8904d492 --- /dev/null +++ b/zebra/zebra_evpn_vxlan.h @@ -0,0 +1,71 @@ +/* + * Zebra EVPN for VxLAN code + * Copyright (C) 2016, 2017 Cumulus Networks, Inc. + * + * This file is part of FRR. + * + * FRR is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * FRR is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with FRR; see the file COPYING. If not, write to the Free + * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +/* Get the VRR interface for SVI if any */ +static inline struct interface * +zebra_get_vrr_intf_for_svi(struct interface *ifp) +{ + struct zebra_vrf *zvrf = NULL; + struct interface *tmp_if = NULL; + struct zebra_if *zif = NULL; + + zvrf = vrf_info_lookup(ifp->vrf_id); + assert(zvrf); + + FOR_ALL_INTERFACES (zvrf->vrf, tmp_if) { + zif = tmp_if->info; + if (!zif) + continue; + + if (!IS_ZEBRA_IF_MACVLAN(tmp_if)) + continue; + + if (zif->link == ifp) + return tmp_if; + } + + return NULL; +} + +/* EVPN<=>vxlan_zif association */ +static inline void zevpn_vxlan_if_set(zebra_evpn_t *zevpn, + struct interface *ifp, bool set) +{ + struct zebra_if *zif; + + if (set) { + if (zevpn->vxlan_if == ifp) + return; + zevpn->vxlan_if = ifp; + } else { + if (!zevpn->vxlan_if) + return; + zevpn->vxlan_if = NULL; + } + + if (ifp) + zif = ifp->info; + else + zif = NULL; + + zebra_evpn_vxl_evpn_set(zif, zevpn, set); +} diff --git a/zebra/zebra_fpm.c b/zebra/zebra_fpm.c index 47b4965396..f84c8c1fcc 100644 --- a/zebra/zebra_fpm.c +++ b/zebra/zebra_fpm.c @@ -1908,8 +1908,7 @@ static inline void zfpm_init_message_format(const char *format) return; } flog_warn(EC_ZEBRA_PROTOBUF_NOT_AVAILABLE, - "FPM protobuf message format is deprecated and scheduled to be removed. " - "Please convert to using netlink format or contact dev@lists.frrouting.org with your use case."); + "FPM protobuf message format is deprecated and scheduled to be removed. Please convert to using netlink format or contact dev@lists.frrouting.org with your use case."); zfpm_g->message_format = ZFPM_MSG_FORMAT_PROTOBUF; return; } diff --git a/zebra/zebra_fpm_netlink.c b/zebra/zebra_fpm_netlink.c index a18885ddb7..3e11d53b16 100644 --- a/zebra/zebra_fpm_netlink.c +++ b/zebra/zebra_fpm_netlink.c @@ -279,7 +279,6 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd, rib_dest_t *dest, struct route_entry *re) { struct nexthop *nexthop; - struct zebra_vrf *zvrf; memset(ri, 0, sizeof(*ri)); @@ -287,9 +286,7 @@ static int netlink_route_info_fill(struct netlink_route_info *ri, int cmd, ri->af = rib_dest_af(dest); ri->nlmsg_type = cmd; - zvrf = rib_dest_vrf(dest); - if (zvrf) - ri->rtm_table = zvrf->table_id; + ri->rtm_table = rib_table_info(rib_dest_table(dest))->table_id; ri->rtm_protocol = RTPROT_UNSPEC; /* @@ -364,6 +361,7 @@ static int netlink_route_info_encode(struct netlink_route_info *ri, struct rtattr *nest, *inner_nest; struct rtnexthop *rtnh; struct vxlan_encap_info_t *vxlan; + struct in6_addr ipv6; struct { struct nlmsghdr n; @@ -423,8 +421,15 @@ static int netlink_route_info_encode(struct netlink_route_info *ri, nhi = &ri->nhs[0]; if (nhi->gateway) { - nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY, - nhi->gateway, bytelen); + if (nhi->type == NEXTHOP_TYPE_IPV4_IFINDEX + && ri->af == AF_INET6) { + ipv4_to_ipv4_mapped_ipv6(&ipv6, + nhi->gateway->ipv4); + nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY, + &ipv6, bytelen); + } else + nl_attr_put(&req->n, in_buf_len, RTA_GATEWAY, + nhi->gateway, bytelen); } if (nhi->if_index) { diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c index 4c0cc62fbf..1758c8f96a 100644 --- a/zebra/zebra_l2.c +++ b/zebra/zebra_l2.c @@ -43,6 +43,7 @@ #include "zebra/rt_netlink.h" #include "zebra/zebra_l2.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" /* definitions */ @@ -53,13 +54,7 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) { struct vrf *vrf; struct interface *ifp; - struct zebra_vrf *zvrf; - struct zebra_ns *zns; - zvrf = zebra_vrf_lookup_by_id(br_if->vrf_id); - assert(zvrf); - zns = zvrf->zns; - assert(zns); RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { FOR_ALL_INTERFACES (vrf, ifp) { struct zebra_if *zif; @@ -78,8 +73,7 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) br_slave = &zif->brslave_info; if (link) { - if (br_slave->bridge_ifindex == br_if->ifindex && - br_slave->ns_id == zns->ns_id) + if (br_slave->bridge_ifindex == br_if->ifindex) br_slave->br_if = br_if; } else { if (br_slave->br_if == br_if) @@ -90,14 +84,12 @@ static void map_slaves_to_bridge(struct interface *br_if, int link) } /* Public functions */ -void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, - struct zebra_ns *zns) +void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave) { struct interface *br_if; /* TODO: Handle change of master */ - assert(zns); - br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(zns->ns_id), + br_if = if_lookup_by_index_per_ns(zebra_ns_lookup(NS_DEFAULT), br_slave->bridge_ifindex); if (br_if) br_slave->br_if = br_if; @@ -119,7 +111,7 @@ void zebra_l2_map_slave_to_bond(struct zebra_l2info_bondslave *bond_slave, bond_slave->bond_if = bond_if; else bond_slave->bond_if = if_create_ifindex(bond_slave->bond_ifindex, - vrf_id, NULL); + vrf_id); } void zebra_l2_unmap_slave_from_bond(struct zebra_l2info_bondslave *bond_slave) @@ -191,6 +183,7 @@ void zebra_l2_vxlanif_add_update(struct interface *ifp, if (add) { memcpy(&zif->l2info.vxl, vxlan_info, sizeof(*vxlan_info)); + zebra_evpn_vl_vxl_ref(zif->l2info.vxl.access_vlan, zif); zebra_vxlan_if_add(ifp); return; } @@ -229,6 +222,9 @@ void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, return; zif->l2info.vxl.access_vlan = access_vlan; + + zebra_evpn_vl_vxl_deref(old_access_vlan, zif); + zebra_evpn_vl_vxl_ref(zif->l2info.vxl.access_vlan, zif); zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_VLAN_CHANGE); } @@ -237,6 +233,12 @@ void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, */ void zebra_l2_vxlanif_del(struct interface *ifp) { + struct zebra_if *zif; + + zif = ifp->info; + assert(zif); + + zebra_evpn_vl_vxl_deref(zif->l2info.vxl.access_vlan, zif); zebra_vxlan_if_del(ifp); } @@ -246,32 +248,23 @@ void zebra_l2_vxlanif_del(struct interface *ifp) * from a bridge before it can be mapped to another bridge. */ void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex, - ns_id_t ns_id) + ifindex_t bridge_ifindex) { struct zebra_if *zif; ifindex_t old_bridge_ifindex; - ns_id_t old_ns_id; - struct zebra_vrf *zvrf; zif = ifp->info; assert(zif); - zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - if (!zvrf) - return; - old_bridge_ifindex = zif->brslave_info.bridge_ifindex; - old_ns_id = zif->brslave_info.ns_id; - if (old_bridge_ifindex == bridge_ifindex && - old_ns_id == zif->brslave_info.ns_id) + if (old_bridge_ifindex == bridge_ifindex) return; - zif->brslave_info.ns_id = ns_id; zif->brslave_info.bridge_ifindex = bridge_ifindex; + /* Set up or remove link with master */ if (bridge_ifindex != IFINDEX_INTERNAL) { - zebra_l2_map_slave_to_bridge(&zif->brslave_info, zvrf->zns); + zebra_l2_map_slave_to_bridge(&zif->brslave_info); /* In the case of VxLAN, invoke the handler for EVPN. */ if (zif->zif_type == ZEBRA_IF_VXLAN) zebra_vxlan_if_update(ifp, ZEBRA_VXLIF_MASTER_CHANGE); @@ -307,3 +300,43 @@ void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex) else if (old_bond_ifindex != IFINDEX_INTERNAL) zebra_l2_unmap_slave_from_bond(&zif->bondslave_info); } + +void zebra_vlan_bitmap_compute(struct interface *ifp, + uint32_t vid_start, uint16_t vid_end) +{ + uint32_t vid; + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + assert(zif); + + for (vid = vid_start; vid <= vid_end; ++vid) + bf_set_bit(zif->vlan_bitmap, vid); +} + +void zebra_vlan_mbr_re_eval(struct interface *ifp, bitfield_t old_vlan_bitmap) +{ + uint32_t vid; + struct zebra_if *zif; + + zif = (struct zebra_if *)ifp->info; + assert(zif); + + if (!bf_cmp(zif->vlan_bitmap, old_vlan_bitmap)) + /* no change */ + return; + + bf_for_each_set_bit(zif->vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { + /* if not already set create new reference */ + if (!bf_test_index(old_vlan_bitmap, vid)) + zebra_evpn_vl_mbr_ref(vid, zif); + + /* also clear from the old vlan bitmap */ + bf_release_index(old_vlan_bitmap, vid); + } + + /* any bits remaining in the old vlan bitmap are stale references */ + bf_for_each_set_bit(old_vlan_bitmap, vid, IF_VLAN_BITMAP_MAX) { + zebra_evpn_vl_mbr_deref(vid, zif); + } +} diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h index a3c780ee09..2735d915ec 100644 --- a/zebra/zebra_l2.h +++ b/zebra/zebra_l2.h @@ -37,7 +37,6 @@ extern "C" { struct zebra_l2info_brslave { ifindex_t bridge_ifindex; /* Bridge Master */ struct interface *br_if; /* Pointer to master */ - ns_id_t ns_id; /* network namespace where bridge is */ }; /* zebra L2 interface information - bridge interface */ @@ -82,8 +81,7 @@ union zebra_l2if_info { #define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) ((zif)->l2info.br.vlan_aware == 1) -extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave, - struct zebra_ns *zns); +extern void zebra_l2_map_slave_to_bridge(struct zebra_l2info_brslave *br_slave); extern void zebra_l2_unmap_slave_from_bridge(struct zebra_l2info_brslave *br_slave); extern void @@ -103,11 +101,14 @@ extern void zebra_l2_vxlanif_update_access_vlan(struct interface *ifp, vlanid_t access_vlan); extern void zebra_l2_vxlanif_del(struct interface *ifp); extern void zebra_l2if_update_bridge_slave(struct interface *ifp, - ifindex_t bridge_ifindex, - ns_id_t ns_id); + ifindex_t bridge_ifindex); extern void zebra_l2if_update_bond_slave(struct interface *ifp, ifindex_t bond_ifindex); +extern void zebra_vlan_bitmap_compute(struct interface *ifp, + uint32_t vid_start, uint16_t vid_end); +extern void zebra_vlan_mbr_re_eval(struct interface *ifp, + bitfield_t vlan_bitmap); #ifdef __cplusplus } diff --git a/zebra/zebra_memory.c b/zebra/zebra_memory.c index a9c2c5fe58..da8121774e 100644 --- a/zebra/zebra_memory.c +++ b/zebra/zebra_memory.c @@ -28,3 +28,5 @@ DEFINE_MGROUP(ZEBRA, "zebra") DEFINE_MTYPE(ZEBRA, RE, "Route Entry") DEFINE_MTYPE(ZEBRA, RIB_DEST, "RIB destination") +DEFINE_MTYPE(ZEBRA, ZVLAN, "VLAN") +DEFINE_MTYPE(ZEBRA, ZVLAN_BITMAP, "VLAN bitmap") diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index e741268ebb..03b8c8de1f 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -47,6 +47,7 @@ #include "zebra/zebra_memory.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_mpls.h" +#include "zebra/zebra_srte.h" #include "zebra/zebra_errors.h" DEFINE_MTYPE_STATIC(ZEBRA, LSP, "MPLS LSP object") @@ -120,7 +121,8 @@ static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp, enum lsp_types_t type); static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf, mpls_label_t in_label); -static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty); +static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty, + const char *indent); static void lsp_print(struct vty *vty, zebra_lsp_t *lsp); static void *slsp_alloc(void *p); static int snhlfe_match(zebra_snhlfe_t *snhlfe, enum nexthop_types_t gtype, @@ -232,8 +234,7 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, if (IS_ZEBRA_DEBUG_MPLS) { nhlfe2str(nhlfe, buf, BUFSIZ); zlog_debug( - "LSP in-label %u type %d nexthop %s " - "out-label changed", + "LSP in-label %u type %d nexthop %s out-label changed", lsp->ile.in_label, lsp_type, buf); } @@ -254,8 +255,7 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label, if (IS_ZEBRA_DEBUG_MPLS) { nhlfe2str(nhlfe, buf, BUFSIZ); zlog_debug( - "Add LSP in-label %u type %d nexthop %s " - "out-label %u", + "Add LSP in-label %u type %d nexthop %s out-label %u", lsp->ile.in_label, lsp_type, buf, nexthop->nh_label->label[0]); } @@ -928,8 +928,7 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) if (newbest) nhlfe2str(newbest, buf2, sizeof(buf2)); zlog_debug( - "Process LSP in-label %u oldbest %s newbest %s " - "flags 0x%x ecmp# %d", + "Process LSP in-label %u oldbest %s newbest %s flags 0x%x ecmp# %d", lsp->ile.in_label, oldbest ? buf : "NULL", newbest ? buf2 : "NULL", lsp->flags, lsp->num_ecmp); } @@ -957,9 +956,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) case ZEBRA_DPLANE_REQUEST_SUCCESS: zvrf->lsp_installs++; break; - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } } } else { @@ -985,10 +981,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) case ZEBRA_DPLANE_REQUEST_SUCCESS: zvrf->lsp_removals++; break; - - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } } else if (CHECK_FLAG(lsp->flags, LSP_FLAG_CHANGED)) { zebra_nhlfe_t *nhlfe; @@ -1032,10 +1024,6 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data) case ZEBRA_DPLANE_REQUEST_SUCCESS: zvrf->lsp_installs++; break; - - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } } } @@ -1506,12 +1494,23 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) { char buf[BUFSIZ]; json_object *json_nhlfe = NULL; + json_object *json_backups = NULL; + json_object *json_label_stack; struct nexthop *nexthop = nhlfe->nexthop; + int i; json_nhlfe = json_object_new_object(); json_object_string_add(json_nhlfe, "type", nhlfe_type2str(nhlfe->type)); json_object_int_add(json_nhlfe, "outLabel", nexthop->nh_label->label[0]); + + json_label_stack = json_object_new_array(); + json_object_object_add(json_nhlfe, "outLabelStack", json_label_stack); + for (i = 0; i < nexthop->nh_label->num_labels; i++) + json_object_array_add( + json_label_stack, + json_object_new_int(nexthop->nh_label->label[i])); + json_object_int_add(json_nhlfe, "distance", nhlfe->distance); if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) @@ -1537,13 +1536,27 @@ static json_object *nhlfe_json(zebra_nhlfe_t *nhlfe) default: break; } + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + json_backups = json_object_new_array(); + for (i = 0; i < nexthop->backup_num; i++) { + json_object_array_add( + json_backups, + json_object_new_int(nexthop->backup_idx[i])); + } + + json_object_object_add(json_nhlfe, "backupIndex", + json_backups); + } + return json_nhlfe; } /* * Print the NHLFE for a LSP forwarding entry. */ -static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) +static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty, + const char *indent) { struct nexthop *nexthop; char buf[MPLS_LABEL_STRLEN]; @@ -1558,6 +1571,10 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) nexthop->nh_label->label, buf, sizeof(buf), 0), nhlfe->distance); + + if (indent) + vty_out(vty, "%s", indent); + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: @@ -1595,31 +1612,34 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty) static void lsp_print(struct vty *vty, zebra_lsp_t *lsp) { zebra_nhlfe_t *nhlfe, *backup; - int i; + int i, j; vty_out(vty, "Local label: %u%s\n", lsp->ile.in_label, CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) ? " (installed)" : ""); frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { - nhlfe_print(nhlfe, vty); + nhlfe_print(nhlfe, vty, NULL); - if (nhlfe->nexthop && - CHECK_FLAG(nhlfe->nexthop->flags, - NEXTHOP_FLAG_HAS_BACKUP)) { - /* Find backup in backup list */ + if (nhlfe->nexthop == NULL || + !CHECK_FLAG(nhlfe->nexthop->flags, + NEXTHOP_FLAG_HAS_BACKUP)) + continue; + /* Backup nhlfes: find backups in backup list */ + + for (j = 0; j < nhlfe->nexthop->backup_num; j++) { i = 0; backup = NULL; frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) { - if (i == nhlfe->nexthop->backup_idx) + if (i == nhlfe->nexthop->backup_idx[j]) break; i++; } if (backup) { vty_out(vty, " [backup %d]", i); - nhlfe_print(backup, vty); + nhlfe_print(backup, vty, " "); } } } @@ -1643,6 +1663,19 @@ static json_object *lsp_json(zebra_lsp_t *lsp) json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe)); json_object_object_add(json, "nexthops", json_nhlfe_list); + json_nhlfe_list = NULL; + + + frr_each(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { + if (json_nhlfe_list == NULL) + json_nhlfe_list = json_object_new_array(); + + json_object_array_add(json_nhlfe_list, nhlfe_json(nhlfe)); + } + + if (json_nhlfe_list) + json_object_object_add(json, "backupNexthops", json_nhlfe_list); + return json; } @@ -1882,14 +1915,13 @@ static int mpls_processq_init(void) } -/* Public functions */ - /* * Process LSP update results from zebra dataplane. */ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) { struct zebra_vrf *zvrf; + mpls_label_t label; zebra_ile_t tmp_ile; struct hash *lsp_table; zebra_lsp_t *lsp; @@ -1897,6 +1929,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) struct nexthop *nexthop; enum dplane_op_e op; enum zebra_dplane_result status; + enum zebra_sr_policy_update_label_mode update_mode; op = dplane_ctx_get_op(ctx); status = dplane_ctx_get_status(ctx); @@ -1907,6 +1940,8 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) dplane_ctx_get_in_label(ctx), dplane_res2str(status)); + label = dplane_ctx_get_in_label(ctx); + switch (op) { case DPLANE_OP_LSP_INSTALL: case DPLANE_OP_LSP_UPDATE: @@ -1917,7 +1952,7 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) lsp_table = zvrf->lsp_table; - tmp_ile.in_label = dplane_ctx_get_in_label(ctx); + tmp_ile.in_label = label; lsp = hash_lookup(lsp_table, &tmp_ile); if (lsp == NULL) { if (IS_ZEBRA_DEBUG_DPLANE) @@ -1951,13 +1986,21 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx) } } + update_mode = (op == DPLANE_OP_LSP_INSTALL) + ? ZEBRA_SR_POLICY_LABEL_CREATED + : ZEBRA_SR_POLICY_LABEL_UPDATED; + zebra_sr_policy_label_update(label, update_mode); break; case DPLANE_OP_LSP_DELETE: - if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) + if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) { flog_warn(EC_ZEBRA_LSP_DELETE_FAILURE, "LSP Deletion Failure: in-label %u", dplane_ctx_get_in_label(ctx)); + break; + } + zebra_sr_policy_label_update(label, + ZEBRA_SR_POLICY_LABEL_REMOVED); break; default: @@ -2112,6 +2155,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, __func__, buf); SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); } else { if (is_debug) @@ -2119,6 +2163,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, __func__, buf); UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); } if (CHECK_FLAG(ctx_nhlfe->nexthop->flags, @@ -2140,6 +2185,7 @@ static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, zlog_debug("%s: no match for lsp nhlfe %s", __func__, buf); UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); } @@ -3284,7 +3330,7 @@ lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, /* * Install an LSP and forwarding entry; used primarily - * from zapi message processing. + * from vrf zapi message processing. */ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, mpls_label_t in_label, uint8_t num_out_labels, @@ -3335,8 +3381,19 @@ static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, /* Update backup info if present */ if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) { - nhlfe->nexthop->backup_idx = znh->backup_idx; + if (znh->backup_num > NEXTHOP_MAX_BACKUPS) { + nhlfe_del(nhlfe); + return -1; + } + + nhlfe->nexthop->backup_num = znh->backup_num; + memcpy(nhlfe->nexthop->backup_idx, znh->backup_idx, + znh->backup_num); SET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + } else { + /* Ensure there's no stale backup info */ + UNSET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP); + nhlfe->nexthop->backup_num = 0; } /* Queue LSP for processing. */ @@ -3371,6 +3428,21 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type, return 0; } +zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label) +{ + struct hash *lsp_table; + zebra_ile_t tmp_ile; + + /* Lookup table. */ + lsp_table = zvrf->lsp_table; + if (!lsp_table) + return NULL; + + /* If entry is not present, exit. */ + tmp_ile.in_label = in_label; + return hash_lookup(lsp_table, &tmp_ile); +} + /* * Uninstall a particular NHLFE in the forwarding table. If this is * the only NHLFE, the entire LSP forwarding entry has to be deleted. @@ -3408,7 +3480,7 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, return 0; if (IS_ZEBRA_DEBUG_MPLS) { - nhlfe2str(nhlfe, buf, BUFSIZ); + nhlfe2str(nhlfe, buf, sizeof(buf)); zlog_debug("Del LSP in-label %u type %d nexthop %s flags 0x%x", in_label, type, buf, nhlfe->flags); } @@ -3639,10 +3711,9 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label, return 0; if (IS_ZEBRA_DEBUG_MPLS) { - snhlfe2str(snhlfe, buf, BUFSIZ); + snhlfe2str(snhlfe, buf, sizeof(buf)); zlog_debug( - "Upd static LSP in-label %u nexthop %s " - "out-label %u (old %u)", + "Upd static LSP in-label %u nexthop %s out-label %u (old %u)", in_label, buf, out_label, snhlfe->out_label); } snhlfe->out_label = out_label; @@ -3653,7 +3724,7 @@ int zebra_mpls_static_lsp_add(struct zebra_vrf *zvrf, mpls_label_t in_label, return -1; if (IS_ZEBRA_DEBUG_MPLS) { - snhlfe2str(snhlfe, buf, BUFSIZ); + snhlfe2str(snhlfe, buf, sizeof(buf)); zlog_debug( "Add static LSP in-label %u nexthop %s out-label %u", in_label, buf, out_label); @@ -3798,7 +3869,8 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, for (ALL_LIST_ELEMENTS_RO(lsp_list, node, lsp)) json_object_object_add( - json, label2str(lsp->ile.in_label, buf, BUFSIZ), + json, label2str(lsp->ile.in_label, buf, + sizeof(buf)), lsp_json(lsp)); vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -3853,7 +3925,7 @@ void zebra_mpls_print_lsp_table(struct vty *vty, struct zebra_vrf *zvrf, out_label_str = mpls_label2str( nexthop->nh_label->num_labels, &nexthop->nh_label->label[0], - buf, BUFSIZ, 1); + buf, sizeof(buf), 1); else out_label_str = "-"; diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 07a8efeb8b..c0e58c44e3 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -33,6 +33,7 @@ #include "mpls.h" #include "zebra/zserv.h" #include "zebra/zebra_vrf.h" +#include "hook.h" #ifdef __cplusplus extern "C" { @@ -326,6 +327,11 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type, const union g_addr *gate, ifindex_t ifindex); /* + * Lookup LSP by its input label. + */ +zebra_lsp_t *mpls_lsp_find(struct zebra_vrf *zvrf, mpls_label_t in_label); + +/* * Uninstall a particular NHLFE in the forwarding table. If this is * the only NHLFE, the entire LSP forwarding entry has to be deleted. */ @@ -461,6 +467,7 @@ static inline uint8_t lsp_distance(enum lsp_types_t type) case ZEBRA_LSP_SHARP: case ZEBRA_LSP_OSPF_SR: case ZEBRA_LSP_ISIS_SR: + case ZEBRA_LSP_SRTE: return 150; } @@ -492,6 +499,8 @@ static inline enum lsp_types_t lsp_type_from_re_type(int re_type) return ZEBRA_LSP_ISIS_SR; case ZEBRA_ROUTE_SHARP: return ZEBRA_LSP_SHARP; + case ZEBRA_ROUTE_SRTE: + return ZEBRA_LSP_SRTE; default: return ZEBRA_LSP_NONE; } @@ -517,6 +526,8 @@ static inline int re_type_from_lsp_type(enum lsp_types_t lsp_type) return ZEBRA_ROUTE_KERNEL; case ZEBRA_LSP_SHARP: return ZEBRA_ROUTE_SHARP; + case ZEBRA_LSP_SRTE: + return ZEBRA_ROUTE_SRTE; } /* @@ -544,6 +555,8 @@ static inline const char *nhlfe_type2str(enum lsp_types_t lsp_type) return "SR (IS-IS)"; case ZEBRA_LSP_SHARP: return "SHARP"; + case ZEBRA_LSP_SRTE: + return "SR-TE"; case ZEBRA_LSP_NONE: return "Unknown"; } diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c index c95a021639..3b2279c66c 100644 --- a/zebra/zebra_mpls_netlink.c +++ b/zebra/zebra_mpls_netlink.c @@ -28,13 +28,9 @@ #include "zebra/zebra_mpls.h" #include "zebra/kernel_netlink.h" -/* - * LSP forwarding update using dataplane context information. - */ -enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx) +static ssize_t netlink_lsp_msg_encoder(struct zebra_dplane_ctx *ctx, void *buf, + size_t buflen) { - uint8_t nl_pkt[NL_PKT_BUF_SIZE]; - ssize_t ret = -1; int cmd; /* Call to netlink layer based on type of update */ @@ -48,26 +44,21 @@ enum zebra_dplane_result kernel_lsp_update(struct zebra_dplane_ctx *ctx) if (IS_ZEBRA_DEBUG_KERNEL || IS_ZEBRA_DEBUG_MPLS) zlog_debug("LSP in-label %u: update fails, no best NHLFE", dplane_ctx_get_in_label(ctx)); - goto done; + return -1; } cmd = RTM_NEWROUTE; } else /* Invalid op? */ - goto done; - - ret = netlink_mpls_multipath_msg_encode(cmd, ctx, nl_pkt, - sizeof(nl_pkt)); - if (ret <= 0) - return ZEBRA_DPLANE_REQUEST_FAILURE; - - ret = netlink_talk_info(netlink_talk_filter, (struct nlmsghdr *)nl_pkt, - dplane_ctx_get_ns(ctx), 0); + return -1; -done: + return netlink_mpls_multipath_msg_encode(cmd, ctx, buf, buflen); +} - return (ret == 0 ? - ZEBRA_DPLANE_REQUEST_SUCCESS : ZEBRA_DPLANE_REQUEST_FAILURE); +enum netlink_msg_status netlink_put_lsp_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) +{ + return netlink_batch_add_msg(bth, ctx, netlink_lsp_msg_encoder, false); } /* @@ -75,9 +66,10 @@ done: * but note that the default has been to report 'success' for pw updates * on unsupported platforms. */ -enum zebra_dplane_result kernel_pw_update(struct zebra_dplane_ctx *ctx) +enum netlink_msg_status netlink_put_pw_update_msg(struct nl_batch *bth, + struct zebra_dplane_ctx *ctx) { - return ZEBRA_DPLANE_REQUEST_SUCCESS; + return FRR_NETLINK_SUCCESS; } int mpls_kernel_init(void) diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c index c8a3cbbbce..b767929dc0 100644 --- a/zebra/zebra_mpls_openbsd.c +++ b/zebra/zebra_mpls_openbsd.c @@ -276,8 +276,7 @@ static int kernel_lsp_cmd(struct zebra_dplane_ctx *ctx) && CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)))) { if (nhlfe->nexthop->nh_label->num_labels > 1) { flog_warn(EC_ZEBRA_MAX_LABELS_PUSH, - "%s: can't push %u labels at once " - "(maximum is 1)", + "%s: can't push %u labels at once (maximum is 1)", __func__, nhlfe->nexthop->nh_label->num_labels); continue; diff --git a/zebra/zebra_nb.c b/zebra/zebra_nb.c index 53fe8e8e3f..1fc1faff67 100644 --- a/zebra/zebra_nb.c +++ b/zebra/zebra_nb.c @@ -22,40 +22,6 @@ #include "libfrr.h" #include "zebra_nb.h" -const char *zebra_afi_safi_value2identity(afi_t afi, safi_t safi) -{ - if (afi == AFI_IP && safi == SAFI_UNICAST) - return "ipv4-unicast"; - if (afi == AFI_IP6 && safi == SAFI_UNICAST) - return "ipv6-unicast"; - if (afi == AFI_IP && safi == SAFI_MULTICAST) - return "ipv4-multicast"; - if (afi == AFI_IP6 && safi == SAFI_MULTICAST) - return "ipv6-multicast"; - - return " "; -} - -void zebra_afi_safi_identity2value(const char *key, afi_t *afi, safi_t *safi) -{ - if (strmatch(key, "frr-zebra:ipv4-unicast")) { - *afi = AFI_IP; - *safi = SAFI_UNICAST; - } else if (strmatch(key, "frr-zebra:ipv6-unicast")) { - *afi = AFI_IP6; - *safi = SAFI_UNICAST; - } else if (strmatch(key, "frr-zebra:ipv4-multicast")) { - *afi = AFI_IP; - *safi = SAFI_MULTICAST; - } else if (strmatch(key, "frr-zebra:ipv6-multicast")) { - *afi = AFI_IP6; - *safi = SAFI_MULTICAST; - } else { - *afi = AFI_UNSPEC; - *safi = SAFI_UNSPEC; - } -} - /* clang-format off */ const struct frr_yang_module_info frr_zebra_info = { .name = "frr-zebra", @@ -126,27 +92,6 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { - .xpath = "/frr-zebra:zebra/vrf-vni-mapping", - .cbs = { - .create = zebra_vrf_vni_mapping_create, - .destroy = zebra_vrf_vni_mapping_destroy, - } - }, - { - .xpath = "/frr-zebra:zebra/vrf-vni-mapping/vni-id", - .cbs = { - .modify = zebra_vrf_vni_mapping_vni_id_modify, - .destroy = zebra_vrf_vni_mapping_vni_id_destroy, - } - }, - { - .xpath = "/frr-zebra:zebra/vrf-vni-mapping/prefix-only", - .cbs = { - .create = zebra_vrf_vni_mapping_prefix_only_create, - .destroy = zebra_vrf_vni_mapping_prefix_only_destroy, - } - }, - { .xpath = "/frr-zebra:zebra/debugs/debug-events", .cbs = { .modify = zebra_debugs_debug_events_modify, @@ -608,6 +553,12 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srte-color", + .cbs = { + .get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem, + } + }, + { .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry", .cbs = { .get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next, @@ -670,6 +621,19 @@ const struct frr_yang_module_info frr_zebra_info = { } }, { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id", + .cbs = { + .modify = lib_vrf_zebra_l3vni_id_modify, + .destroy = lib_vrf_zebra_l3vni_id_destroy, + } + }, + { + .xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/prefix-only", + .cbs = { + .modify = lib_vrf_zebra_prefix_only_modify, + } + }, + { .xpath = "/frr-route-map:lib/route-map/entry/match-condition/frr-zebra:ipv4-prefix-length", .cbs = { .modify = lib_route_map_entry_match_condition_ipv4_prefix_length_modify, diff --git a/zebra/zebra_nb.h b/zebra/zebra_nb.h index a9e7fd5fb0..e68b819767 100644 --- a/zebra/zebra_nb.h +++ b/zebra/zebra_nb.h @@ -26,10 +26,6 @@ extern "C" { extern const struct frr_yang_module_info frr_zebra_info; -/* helper functions */ -const char *zebra_afi_safi_value2identity(afi_t afi, safi_t safi); -void zebra_afi_safi_identity2value(const char *key, afi_t *afi, safi_t *safi); - /* prototypes */ int get_route_information_rpc(struct nb_cb_rpc_args *args); int get_v6_mroute_info_rpc(struct nb_cb_rpc_args *args); @@ -61,12 +57,6 @@ int zebra_import_kernel_table_route_map_destroy( int zebra_allow_external_route_update_create(struct nb_cb_create_args *args); int zebra_allow_external_route_update_destroy(struct nb_cb_destroy_args *args); int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args); -int zebra_vrf_vni_mapping_create(struct nb_cb_create_args *args); -int zebra_vrf_vni_mapping_destroy(struct nb_cb_destroy_args *args); -int zebra_vrf_vni_mapping_vni_id_modify(struct nb_cb_modify_args *args); -int zebra_vrf_vni_mapping_vni_id_destroy(struct nb_cb_destroy_args *args); -int zebra_vrf_vni_mapping_prefix_only_create(struct nb_cb_create_args *args); -int zebra_vrf_vni_mapping_prefix_only_destroy(struct nb_cb_destroy_args *args); int zebra_debugs_debug_events_modify(struct nb_cb_modify_args *args); int zebra_debugs_debug_events_destroy(struct nb_cb_destroy_args *args); int zebra_debugs_debug_zapi_send_modify(struct nb_cb_modify_args *args); @@ -285,6 +275,9 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_bh_type_get_elem( struct yang_data * lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem( struct nb_cb_get_elem_args *args); +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( + struct nb_cb_get_elem_args *args); const void * lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next( struct nb_cb_get_next_args *args); @@ -320,6 +313,9 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_fib_get_elem( struct yang_data * lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_weight_get_elem( struct nb_cb_get_elem_args *args); +int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args); +int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args); +int lib_vrf_zebra_prefix_only_modify(struct nb_cb_modify_args *args); #ifdef __cplusplus } diff --git a/zebra/zebra_nb_config.c b/zebra/zebra_nb_config.c index 948ef51320..b4ed910b4d 100644 --- a/zebra/zebra_nb_config.c +++ b/zebra/zebra_nb_config.c @@ -31,6 +31,8 @@ #include "zebra/connected.h" #include "zebra/zebra_router.h" #include "zebra/debug.h" +#include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_vxlan.h" /* * XPath: /frr-zebra:zebra/mcast-rpf-lookup @@ -273,99 +275,6 @@ int zebra_dplane_queue_limit_modify(struct nb_cb_modify_args *args) } /* - * XPath: /frr-zebra:zebra/vrf-vni-mapping - */ -int zebra_vrf_vni_mapping_create(struct nb_cb_create_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - /* TODO: implement me. */ - break; - } - - return NB_OK; -} - -int zebra_vrf_vni_mapping_destroy(struct nb_cb_destroy_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - /* TODO: implement me. */ - break; - } - - return NB_OK; -} - -/* - * XPath: /frr-zebra:zebra/vrf-vni-mapping/vni-id - */ -int zebra_vrf_vni_mapping_vni_id_modify(struct nb_cb_modify_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - /* TODO: implement me. */ - break; - } - - return NB_OK; -} - -int zebra_vrf_vni_mapping_vni_id_destroy(struct nb_cb_destroy_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - /* TODO: implement me. */ - break; - } - - return NB_OK; -} - -/* - * XPath: /frr-zebra:zebra/vrf-vni-mapping/prefix-only - */ -int zebra_vrf_vni_mapping_prefix_only_create(struct nb_cb_create_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - /* TODO: implement me. */ - break; - } - - return NB_OK; -} - -int zebra_vrf_vni_mapping_prefix_only_destroy(struct nb_cb_destroy_args *args) -{ - switch (args->event) { - case NB_EV_VALIDATE: - case NB_EV_PREPARE: - case NB_EV_ABORT: - case NB_EV_APPLY: - /* TODO: implement me. */ - break; - } - - return NB_OK; -} - -/* * XPath: /frr-zebra:zebra/debugs/debug-events */ int zebra_debugs_debug_events_modify(struct nb_cb_modify_args *args) @@ -1242,7 +1151,7 @@ int lib_vrf_zebra_ribs_rib_create(struct nb_cb_create_args *args) table_id = zvrf->table_id; afi_safi_name = yang_dnode_get_string(args->dnode, "./afi-safi-name"); - zebra_afi_safi_identity2value(afi_safi_name, &afi, &safi); + yang_afi_safi_identity2value(afi_safi_name, &afi, &safi); zrt = zebra_router_find_zrt(zvrf, table_id, afi, safi); @@ -1277,6 +1186,161 @@ int lib_vrf_zebra_ribs_rib_destroy(struct nb_cb_destroy_args *args) return NB_OK; } +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/l3vni-id + */ +int lib_vrf_zebra_l3vni_id_modify(struct nb_cb_modify_args *args) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + vni_t vni = 0; + zebra_l3vni_t *zl3vni = NULL; + struct zebra_vrf *zvrf_evpn = NULL; + char err[ERR_STR_SZ]; + bool pfx_only = false; + const struct lyd_node *pn_dnode; + const char *vrfname; + + switch (args->event) { + case NB_EV_PREPARE: + case NB_EV_ABORT: + return NB_OK; + case NB_EV_VALIDATE: + zvrf_evpn = zebra_vrf_get_evpn(); + if (!zvrf_evpn) { + snprintf(args->errmsg, args->errmsg_len, + "evpn vrf is not present."); + return NB_ERR_VALIDATION; + } + vni = yang_dnode_get_uint32(args->dnode, NULL); + /* Get vrf info from parent node, reject configuration + * if zebra vrf already mapped to different vni id. + */ + pn_dnode = yang_dnode_get_parent(args->dnode, "vrf"); + if (pn_dnode) { + vrfname = yang_dnode_get_string(pn_dnode, "./name"); + zvrf = zebra_vrf_lookup_by_name(vrfname); + if (!zvrf) { + snprintf(args->errmsg, args->errmsg_len, + "zebra vrf info not found for vrf:%s.", + vrfname); + return NB_ERR_VALIDATION; + } + if (zvrf->l3vni && zvrf->l3vni != vni) { + snprintf( + args->errmsg, args->errmsg_len, + "vni %u cannot be configured as vni %u is already configured under the vrf", + vni, zvrf->l3vni); + return NB_ERR_VALIDATION; + } + } + + /* Check if this VNI is already present in the system */ + zl3vni = zl3vni_lookup(vni); + if (zl3vni) { + snprintf(args->errmsg, args->errmsg_len, + "VNI %u is already configured as L3-VNI", vni); + return NB_ERR_VALIDATION; + } + + break; + case NB_EV_APPLY: + + vrf = nb_running_get_entry(args->dnode, NULL, true); + zvrf = zebra_vrf_lookup_by_name(vrf->name); + vni = yang_dnode_get_uint32(args->dnode, NULL); + /* Note: This covers lib_vrf_zebra_prefix_only_modify() config + * along with l3vni config + */ + pfx_only = yang_dnode_get_bool(args->dnode, "../prefix-only"); + + if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, + pfx_only ? 1 : 0, 1) + != 0) { + if (IS_ZEBRA_DEBUG_VXLAN) + snprintf( + args->errmsg, args->errmsg_len, + "vrf vni %u mapping failed with error: %s", + vni, err); + return NB_ERR; + } + + /* Mark as having FRR configuration */ + vrf_set_user_cfged(vrf); + + break; + } + + return NB_OK; +} + +int lib_vrf_zebra_l3vni_id_destroy(struct nb_cb_destroy_args *args) +{ + struct vrf *vrf; + struct zebra_vrf *zvrf; + vni_t vni = 0; + char err[ERR_STR_SZ]; + uint8_t filter = 0; + + switch (args->event) { + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_VALIDATE: + return NB_OK; + case NB_EV_APPLY: + vrf = nb_running_get_entry(args->dnode, NULL, true); + zvrf = zebra_vrf_lookup_by_name(vrf->name); + vni = yang_dnode_get_uint32(args->dnode, NULL); + + if (!zl3vni_lookup(vni)) + return NB_OK; + + if (zvrf->l3vni != vni) { + snprintf(args->errmsg, args->errmsg_len, + "vrf %s has different vni %u mapped", + vrf->name, zvrf->l3vni); + return NB_ERR; + } + + if (is_l3vni_for_prefix_routes_only(zvrf->l3vni)) + filter = 1; + + if (zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, + filter, 0) + != 0) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "vrf vni %u unmapping failed with error: %s", + vni, err); + return NB_ERR; + } + + /* If no other FRR config for this VRF, mark accordingly. */ + if (!zebra_vrf_has_config(zvrf)) + vrf_reset_user_cfged(vrf); + + break; + } + + return NB_OK; +} + +/* + * XPath: /frr-vrf:lib/vrf/frr-zebra:zebra/prefix-only + */ +int lib_vrf_zebra_prefix_only_modify(struct nb_cb_modify_args *args) +{ + switch (args->event) { + case NB_EV_VALIDATE: + case NB_EV_PREPARE: + case NB_EV_ABORT: + case NB_EV_APPLY: + /* TODO: implement me. */ + break; + } + + return NB_OK; +} /* * XPath: diff --git a/zebra/zebra_nb_state.c b/zebra/zebra_nb_state.c index 4bca3b36d6..21c89f64ed 100644 --- a/zebra/zebra_nb_state.c +++ b/zebra/zebra_nb_state.c @@ -185,10 +185,9 @@ int lib_vrf_zebra_ribs_rib_get_keys(struct nb_cb_get_keys_args *args) args->keys->num = 2; - snprintfrr(args->keys->key[0], sizeof(args->keys->key[0]), "%s:%s", - "frr-zebra", - zebra_afi_safi_value2identity(zrt->afi, zrt->safi)); - snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), "%" PRIu32, + snprintfrr(args->keys->key[0], sizeof(args->keys->key[0]), "%s", + yang_afi_safi_value2identity(zrt->afi, zrt->safi)); + snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), "%u", zrt->tableid); return NB_OK; @@ -205,7 +204,7 @@ lib_vrf_zebra_ribs_rib_lookup_entry(struct nb_cb_lookup_entry_args *args) zvrf = zebra_vrf_lookup_by_id(vrf->vrf_id); - zebra_afi_safi_identity2value(args->keys->key[0], &afi, &safi); + yang_afi_safi_identity2value(args->keys->key[0], &afi, &safi); table_id = yang_str2uint32(args->keys->key[1]); /* table_id 0 assume vrf's table_id. */ if (!table_id) @@ -526,54 +525,57 @@ int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_get_keys( { struct nexthop *nexthop = (struct nexthop *)args->list_entry; - args->keys->num = 3; + args->keys->num = 4; strlcpy(args->keys->key[0], yang_nexthop_type2str(nexthop->type), sizeof(args->keys->key[0])); + snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), "%" PRIu32, + nexthop->vrf_id); + switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: - snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), + snprintfrr(args->keys->key[2], sizeof(args->keys->key[2]), "%pI4", &nexthop->gate.ipv4); if (nexthop->ifindex) - strlcpy(args->keys->key[2], + strlcpy(args->keys->key[3], ifindex2ifname(nexthop->ifindex, nexthop->vrf_id), - sizeof(args->keys->key[2])); + sizeof(args->keys->key[3])); else /* no ifindex */ - strlcpy(args->keys->key[2], " ", - sizeof(args->keys->key[2])); + strlcpy(args->keys->key[3], " ", + sizeof(args->keys->key[3])); break; case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: - snprintfrr(args->keys->key[1], sizeof(args->keys->key[1]), + snprintfrr(args->keys->key[2], sizeof(args->keys->key[2]), "%pI6", &nexthop->gate.ipv6); if (nexthop->ifindex) - strlcpy(args->keys->key[2], + strlcpy(args->keys->key[3], ifindex2ifname(nexthop->ifindex, nexthop->vrf_id), - sizeof(args->keys->key[2])); + sizeof(args->keys->key[3])); else /* no ifindex */ - strlcpy(args->keys->key[2], " ", - sizeof(args->keys->key[2])); + strlcpy(args->keys->key[3], " ", + sizeof(args->keys->key[3])); break; case NEXTHOP_TYPE_IFINDEX: - strlcpy(args->keys->key[1], "", sizeof(args->keys->key[1])); - strlcpy(args->keys->key[2], + strlcpy(args->keys->key[2], "", sizeof(args->keys->key[2])); + strlcpy(args->keys->key[3], ifindex2ifname(nexthop->ifindex, nexthop->vrf_id), - sizeof(args->keys->key[2])); + sizeof(args->keys->key[3])); break; case NEXTHOP_TYPE_BLACKHOLE: /* Gateway IP */ - strlcpy(args->keys->key[1], "", sizeof(args->keys->key[1])); - strlcpy(args->keys->key[2], " ", sizeof(args->keys->key[2])); + strlcpy(args->keys->key[2], "", sizeof(args->keys->key[2])); + strlcpy(args->keys->key[3], " ", sizeof(args->keys->key[3])); break; default: break; @@ -806,6 +808,22 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_onlink_get_elem( /* * XPath: + * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srte-color + */ +struct yang_data * +lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( + struct nb_cb_get_elem_args *args) +{ + struct nexthop *nexthop = (struct nexthop *)args->list_entry; + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_SRTE)) + return yang_data_new_uint32(args->xpath, nexthop->srte_color); + + return NULL; +} + +/* + * XPath: * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry */ const void * diff --git a/zebra/zebra_netns_id.c b/zebra/zebra_netns_id.c index 79121bb086..8de4daf439 100644 --- a/zebra/zebra_netns_id.c +++ b/zebra/zebra_netns_id.c @@ -159,34 +159,27 @@ static ns_id_t extract_nsid(struct nlmsghdr *nlh, char *buf) return ns_id; } -/* fd_param = -1 is ignored. - * netnspath set to null is ignored. - * one of the 2 params is mandatory. netnspath is looked in priority - */ -ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) +ns_id_t zebra_ns_id_get(const char *netnspath) { int ns_id = -1; struct sockaddr_nl snl; - int fd = -1, sock, ret; + int fd, sock, ret; unsigned int seq; ns_id_t return_nsid = NS_UNKNOWN; /* netns path check */ - if (!netnspath && fd_param == -1) + if (!netnspath) return NS_UNKNOWN; - if (netnspath) { - fd = open(netnspath, O_RDONLY); - if (fd == -1) - return NS_UNKNOWN; - } else if (fd_param != -1) - fd = fd_param; + fd = open(netnspath, O_RDONLY); + if (fd == -1) + return NS_UNKNOWN; + /* netlink socket */ sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { flog_err_sys(EC_LIB_SOCKET, "netlink( %u) socket() error: %s", sock, safe_strerror(errno)); - if (fd_param == -1) - close(fd); + close(fd); return NS_UNKNOWN; } memset(&snl, 0, sizeof(snl)); @@ -199,8 +192,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) "netlink( %u) socket() bind error: %s", sock, safe_strerror(errno)); close(sock); - if (fd_param == -1) - close(fd); + close(fd); return NS_UNKNOWN; } @@ -222,8 +214,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - if (fd_param == -1) - close(fd); + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -267,8 +258,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) "netlink( %u) recvfrom() error 2 when reading: %s", fd, safe_strerror(errno)); close(sock); - if (fd_param == -1) - close(fd); + close(fd); if (errno == ENOTSUP) { zlog_debug("NEWNSID locally generated"); return zebra_ns_id_get_fallback(netnspath); @@ -289,8 +279,7 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) ret = send_receive(sock, nlh, seq, buf); if (ret < 0) { close(sock); - if (fd_param == -1) - close(fd); + close(fd); return NS_UNKNOWN; } nlh = (struct nlmsghdr *)buf; @@ -321,18 +310,16 @@ ns_id_t zebra_ns_id_get(const char *netnspath, int fd_param) } while (len != 0 && ret == 0); } - if (fd_param == -1) - close(fd); + close(fd); close(sock); return return_nsid; } #else -ns_id_t zebra_ns_id_get(const char *netnspath, int fd __attribute__ ((unused))) +ns_id_t zebra_ns_id_get(const char *netnspath) { return zebra_ns_id_get_fallback(netnspath); } - #endif /* ! defined(HAVE_NETLINK) */ #ifdef HAVE_NETNS @@ -368,7 +355,7 @@ ns_id_t zebra_ns_id_get_default(void) return NS_DEFAULT_INTERNAL; } close(fd); - return zebra_ns_id_get((char *)NS_DEFAULT_NAME, -1); + return zebra_ns_id_get((char *)NS_DEFAULT_NAME); #else /* HAVE_NETNS */ return NS_DEFAULT_INTERNAL; #endif /* !HAVE_NETNS */ diff --git a/zebra/zebra_netns_id.h b/zebra/zebra_netns_id.h index dd9eab18e0..7a5f6851f4 100644 --- a/zebra/zebra_netns_id.h +++ b/zebra/zebra_netns_id.h @@ -24,7 +24,7 @@ extern "C" { #endif -extern ns_id_t zebra_ns_id_get(const char *netnspath, int fd); +extern ns_id_t zebra_ns_id_get(const char *netnspath); extern ns_id_t zebra_ns_id_get_default(void); #ifdef __cplusplus diff --git a/zebra/zebra_netns_notify.c b/zebra/zebra_netns_notify.c index 72e4fd0055..ec7681bf23 100644 --- a/zebra/zebra_netns_notify.c +++ b/zebra/zebra_netns_notify.c @@ -72,14 +72,13 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) char *netnspath = ns_netns_pathname(NULL, name); struct vrf *vrf; int ret; - ns_id_t ns_id, ns_id_external, ns_id_relative = NS_UNKNOWN; - struct ns *default_ns; + ns_id_t ns_id, ns_id_external; if (netnspath == NULL) return; frr_with_privs(&zserv_privs) { - ns_id = zebra_ns_id_get(netnspath, -1); + ns_id = zebra_ns_id_get(netnspath); } if (ns_id == NS_UNKNOWN) return; @@ -98,21 +97,9 @@ static void zebra_ns_notify_create_context_from_entry_name(const char *name) ns_map_nsid_with_external(ns_id, false); return; } - - default_ns = ns_get_default(); - - /* force kernel ns_id creation in that new vrf */ - frr_with_privs(&zserv_privs) { - ns_switch_to_netns(netnspath); - ns_id_relative = zebra_ns_id_get(NULL, default_ns->fd); - ns_switchback_to_initial(); - } - frr_with_privs(&zserv_privs) { ret = vrf_netns_handler_create(NULL, vrf, netnspath, - ns_id_external, - ns_id, - ns_id_relative); + ns_id_external, ns_id); } if (ret != CMD_SUCCESS) { flog_warn(EC_ZEBRA_NS_VRF_CREATION_FAILED, @@ -185,7 +172,7 @@ static bool zebra_ns_notify_is_default_netns(const char *name) { struct stat default_netns_stat; struct stat st; - char netnspath[64]; + char netnspath[PATH_MAX]; if (zebra_ns_notify_self_identify(&default_netns_stat)) return false; @@ -230,14 +217,12 @@ static int zebra_ns_ready_read(struct thread *t) /* check default name is not already set */ if (strmatch(VRF_DEFAULT_NAME, basename(netnspath))) { - zlog_warn("NS notify : NS %s is already default VRF." - "Cancel VRF Creation", basename(netnspath)); + zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", basename(netnspath)); return zebra_ns_continue_read(zns_info, 1); } if (zebra_ns_notify_is_default_netns(basename(netnspath))) { zlog_warn( - "NS notify : NS %s is default VRF." - " Updating VRF Name", basename(netnspath)); + "NS notify : NS %s is default VRF. Updating VRF Name", basename(netnspath)); vrf_set_default_name(basename(netnspath), false); return zebra_ns_continue_read(zns_info, 1); } @@ -333,14 +318,12 @@ void zebra_ns_notify_parse(void) } /* check default name is not already set */ if (strmatch(VRF_DEFAULT_NAME, basename(dent->d_name))) { - zlog_warn("NS notify : NS %s is already default VRF." - "Cancel VRF Creation", dent->d_name); + zlog_warn("NS notify : NS %s is already default VRF.Cancel VRF Creation", dent->d_name); continue; } if (zebra_ns_notify_is_default_netns(dent->d_name)) { zlog_warn( - "NS notify : NS %s is default VRF." - " Updating VRF Name", dent->d_name); + "NS notify : NS %s is default VRF. Updating VRF Name", dent->d_name); vrf_set_default_name(dent->d_name, false); continue; } diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 9bfd7aacb7..b8faaa43fd 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -28,6 +28,7 @@ #include "lib/mpls.h" #include "lib/jhash.h" #include "lib/debug.h" +#include "lib/lib_errors.h" #include "zebra/connected.h" #include "zebra/debug.h" @@ -36,6 +37,7 @@ #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" #include "zebra/zebra_memory.h" +#include "zebra/zebra_srte.h" #include "zebra/zserv.h" #include "zebra/rt.h" #include "zebra_errors.h" @@ -1620,7 +1622,8 @@ void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe) } static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, - struct nexthop *nexthop) + struct nexthop *nexthop, + struct zebra_sr_policy *policy) { struct nexthop *resolved_hop; uint8_t num_labels = 0; @@ -1684,7 +1687,21 @@ static void nexthop_set_resolved(afi_t afi, const struct nexthop *newhop, resolved_hop->flags |= NEXTHOP_FLAG_ONLINK; /* Copy labels of the resolved route and the parent resolving to it */ - if (newhop->nh_label) { + if (policy) { + int i = 0; + + /* + * Don't push the first SID if the corresponding action in the + * LFIB is POP. + */ + if (!newhop->nh_label || !newhop->nh_label->num_labels + || newhop->nh_label->label[0] == MPLS_LABEL_IMPLICIT_NULL) + i = 1; + + for (; i < policy->segment_list.label_num; i++) + labels[num_labels++] = policy->segment_list.labels[i]; + label_type = policy->segment_list.type; + } else if (newhop->nh_label) { for (i = 0; i < newhop->nh_label->num_labels; i++) { /* Be a bit picky about overrunning the local array */ if (num_labels >= MPLS_MAX_LABELS) { @@ -1771,10 +1788,13 @@ static int nexthop_active(afi_t afi, struct route_entry *re, struct route_node *rn; struct route_entry *match = NULL; int resolved; + zebra_nhlfe_t *nhlfe; struct nexthop *newhop; struct interface *ifp; rib_dest_t *dest; struct zebra_vrf *zvrf; + struct in_addr local_ipv4; + struct in_addr *ipv4; if ((nexthop->type == NEXTHOP_TYPE_IPV4) || nexthop->type == NEXTHOP_TYPE_IPV6) @@ -1835,13 +1855,63 @@ static int nexthop_active(afi_t afi, struct route_entry *re, return 0; } + /* Validation for ipv4 mapped ipv6 nexthop. */ + if (IS_MAPPED_IPV6(&nexthop->gate.ipv6)) { + afi = AFI_IP; + ipv4 = &local_ipv4; + ipv4_mapped_ipv6_to_ipv4(&nexthop->gate.ipv6, ipv4); + } else { + ipv4 = &nexthop->gate.ipv4; + } + + if (nexthop->srte_color) { + struct ipaddr endpoint = {0}; + struct zebra_sr_policy *policy; + + switch (afi) { + case AFI_IP: + endpoint.ipa_type = IPADDR_V4; + endpoint.ipaddr_v4 = *ipv4; + break; + case AFI_IP6: + endpoint.ipa_type = IPADDR_V6; + endpoint.ipaddr_v6 = nexthop->gate.ipv6; + break; + default: + flog_err(EC_LIB_DEVELOPMENT, + "%s: unknown address-family: %u", __func__, + afi); + exit(1); + } + + policy = zebra_sr_policy_find(nexthop->srte_color, &endpoint); + if (policy && policy->status == ZEBRA_SR_POLICY_UP) { + resolved = 0; + frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, + nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, + NHLFE_FLAG_DELETED)) + continue; + SET_FLAG(nexthop->flags, + NEXTHOP_FLAG_RECURSIVE); + nexthop_set_resolved(afi, nhlfe->nexthop, + nexthop, policy); + resolved = 1; + } + if (resolved) + return 1; + } + } + /* Make lookup prefix. */ memset(&p, 0, sizeof(struct prefix)); switch (afi) { case AFI_IP: p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; - p.u.prefix4 = nexthop->gate.ipv4; + p.u.prefix4 = *ipv4; break; case AFI_IP6: p.family = AF_INET6; @@ -1951,8 +2021,11 @@ static int nexthop_active(afi_t afi, struct route_entry *re, goto done_with_match; } - /* Examine installed nexthops */ - nhg = &match->nhe->nhg; + /* Examine installed nexthops; note that there + * may not be any installed primary nexthops if + * only backups are installed. + */ + nhg = rib_get_fib_nhg(match); for (ALL_NEXTHOPS_PTR(nhg, newhop)) { if (!nexthop_valid_resolve(nexthop, newhop)) continue; @@ -1964,7 +2037,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re, SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop); + nexthop_set_resolved(afi, newhop, nexthop, + NULL); resolved = 1; } @@ -1973,8 +2047,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re, * dedicated fib list. */ nhg = rib_get_fib_backup_nhg(match); - if (nhg == NULL || - nhg == zebra_nhg_get_backup_nhg(match->nhe)) + if (nhg == NULL || nhg->nexthop == NULL) goto done_with_match; for (ALL_NEXTHOPS_PTR(nhg, newhop)) { @@ -1988,7 +2061,8 @@ static int nexthop_active(afi_t afi, struct route_entry *re, SET_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE); - nexthop_set_resolved(afi, newhop, nexthop); + nexthop_set_resolved(afi, newhop, nexthop, + NULL); resolved = 1; } done_with_match: @@ -2589,6 +2663,7 @@ void zebra_nhg_dplane_result(struct zebra_dplane_ctx *ctx) case DPLANE_OP_RULE_ADD: case DPLANE_OP_RULE_DELETE: case DPLANE_OP_RULE_UPDATE: + case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_NONE: break; } diff --git a/zebra/zebra_ns.c b/zebra/zebra_ns.c index 6462daf687..4e51437337 100644 --- a/zebra/zebra_ns.c +++ b/zebra/zebra_ns.c @@ -153,25 +153,20 @@ static int zebra_ns_disable_internal(struct zebra_ns *zns, bool complete) /* During zebra shutdown, do partial cleanup while the async dataplane * is still running. */ -int zebra_ns_early_shutdown(struct ns *ns, - void *param_in __attribute__((unused)), - void **param_out __attribute__((unused))) +int zebra_ns_early_shutdown(struct ns *ns) { struct zebra_ns *zns = ns->info; if (zns == NULL) return 0; - zebra_ns_disable_internal(zns, false); - return NS_WALK_CONTINUE; + return zebra_ns_disable_internal(zns, false); } /* During zebra shutdown, do final cleanup * after all dataplane work is complete. */ -int zebra_ns_final_shutdown(struct ns *ns, - void *param_in __attribute__((unused)), - void **param_out __attribute__((unused))) +int zebra_ns_final_shutdown(struct ns *ns) { struct zebra_ns *zns = ns->info; @@ -180,7 +175,7 @@ int zebra_ns_final_shutdown(struct ns *ns, kernel_terminate(zns, true); - return NS_WALK_CONTINUE; + return 0; } int zebra_ns_init(const char *optional_default_name) @@ -188,16 +183,12 @@ int zebra_ns_init(const char *optional_default_name) struct ns *default_ns; ns_id_t ns_id; ns_id_t ns_id_external; - struct ns *ns; frr_with_privs(&zserv_privs) { ns_id = zebra_ns_id_get_default(); } ns_id_external = ns_map_nsid_with_external(ns_id, true); ns_init_management(ns_id_external, ns_id); - ns = ns_get_default(); - if (ns) - ns->relative_default_ns = ns_id; default_ns = ns_lookup(ns_get_default_id()); if (!default_ns) { diff --git a/zebra/zebra_ns.h b/zebra/zebra_ns.h index f7d1f40782..dc79a83db0 100644 --- a/zebra/zebra_ns.h +++ b/zebra/zebra_ns.h @@ -67,12 +67,9 @@ struct zebra_ns *zebra_ns_lookup(ns_id_t ns_id); int zebra_ns_init(const char *optional_default_name); int zebra_ns_enable(ns_id_t ns_id, void **info); int zebra_ns_disabled(struct ns *ns); -int zebra_ns_early_shutdown(struct ns *ns, - void *param_in __attribute__((unused)), - void **param_out __attribute__((unused))); -int zebra_ns_final_shutdown(struct ns *ns, - void *param_in __attribute__((unused)), - void **param_out __attribute__((unused))); +int zebra_ns_early_shutdown(struct ns *ns); +int zebra_ns_final_shutdown(struct ns *ns); + int zebra_ns_config_write(struct vty *vty, struct ns *ns); #ifdef __cplusplus diff --git a/zebra/zebra_pbr.c b/zebra/zebra_pbr.c index c5a7795273..1bc8d893bc 100644 --- a/zebra/zebra_pbr.c +++ b/zebra/zebra_pbr.c @@ -336,14 +336,20 @@ bool zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2) return true; } -void zebra_pbr_iptable_free(void *arg) +/* this function gives option to flush plugin memory contexts + * with all parameter. set it to true to flush all + * set it to false to flush only passed arg argument + */ +static void _zebra_pbr_iptable_free_all(void *arg, bool all) { struct zebra_pbr_iptable *iptable; struct listnode *node, *nnode; char *name; iptable = (struct zebra_pbr_iptable *)arg; - hook_call(zebra_pbr_iptable_update, 0, iptable); + + if (all) + hook_call(zebra_pbr_iptable_update, 0, iptable); if (iptable->interface_name_list) { for (ALL_LIST_ELEMENTS(iptable->interface_name_list, node, @@ -356,6 +362,11 @@ void zebra_pbr_iptable_free(void *arg) XFREE(MTYPE_TMP, iptable); } +void zebra_pbr_iptable_free(void *arg) +{ + _zebra_pbr_iptable_free_all(arg, false); +} + uint32_t zebra_pbr_iptable_hash_key(const void *arg) { const struct zebra_pbr_iptable *iptable = arg; @@ -529,7 +540,7 @@ static void zebra_pbr_cleanup_iptable(struct hash_bucket *b, void *data) if (iptable->sock == *sock) { if (hash_release(zrouter.iptable_hash, iptable)) - zebra_pbr_iptable_free(iptable); + _zebra_pbr_iptable_free_all(iptable, true); else hook_call(zebra_pbr_iptable_update, 0, iptable); } diff --git a/zebra/zebra_pbr.h b/zebra/zebra_pbr.h index 4bc0f40037..888d2fcfa0 100644 --- a/zebra/zebra_pbr.h +++ b/zebra/zebra_pbr.h @@ -54,6 +54,8 @@ struct zebra_pbr_rule { (r->rule.filter.filter_bm & PBR_FILTER_SRC_PORT) #define IS_RULE_FILTERING_ON_DST_PORT(r) \ (r->rule.filter.filter_bm & PBR_FILTER_DST_PORT) +#define IS_RULE_FILTERING_ON_DSFIELD(r) \ + (r->rule.filter.filter_bm & PBR_FILTER_DSFIELD) #define IS_RULE_FILTERING_ON_FWMARK(r) \ (r->rule.filter.filter_bm & PBR_FILTER_FWMARK) @@ -170,13 +172,6 @@ void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable); void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable); /* - * Add, update or delete a rule from the - * kernel, using info from a dataplane context. - */ -extern enum zebra_dplane_result -kernel_pbr_rule_update(struct zebra_dplane_ctx *ctx); - -/* * Get to know existing PBR rules in the kernel - typically called at startup. */ extern void kernel_read_pbr_rules(struct zebra_ns *zns); diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 88ea2b87b1..9a3b567b5a 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -434,8 +434,7 @@ static void if_bfd_session_update(struct interface *ifp, struct prefix *dp, if (ifp) { zlog_debug( - "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d on %s" - " %s event", + "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d on %s %s event", inet_ntop(dp->family, &dp->u.prefix, buf[0], INET6_ADDRSTRLEN), dp->prefixlen, ifp->name, @@ -444,8 +443,7 @@ static void if_bfd_session_update(struct interface *ifp, struct prefix *dp, struct vrf *vrf = vrf_lookup_by_id(vrf_id); zlog_debug( - "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d " - "with src %s/%d and vrf %s(%u) %s event", + "MESSAGE: ZEBRA_INTERFACE_BFD_DEST_UPDATE %s/%d with src %s/%d and vrf %s(%u) %s event", inet_ntop(dp->family, &dp->u.prefix, buf[0], INET6_ADDRSTRLEN), dp->prefixlen, @@ -502,8 +500,7 @@ static int zebra_ptm_handle_bfd_msg(void *arg, void *in_ctxt, if (IS_ZEBRA_DEBUG_EVENT) zlog_debug( - "%s: Recv Port [%s] bfd status [%s] vrf [%s]" - " peer [%s] local [%s]", + "%s: Recv Port [%s] bfd status [%s] vrf [%s] peer [%s] local [%s]", __func__, ifp ? ifp->name : "N/A", bfdst_str, vrf_str, dest_str, src_str); diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 2328ab650a..cdcca1e930 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -215,8 +215,7 @@ void zebra_pw_install_failure(struct zebra_pw *pw, int pwstatus) { if (IS_ZEBRA_DEBUG_PW) zlog_debug( - "%u: failed installing pseudowire %s, " - "scheduling retry in %u seconds", + "%u: failed installing pseudowire %s, scheduling retry in %u seconds", pw->vrf_id, pw->ifname, PW_INSTALL_RETRY_INTERVAL); /* schedule to retry later */ @@ -518,6 +517,7 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty) struct zebra_pw *pw; struct route_entry *re; struct nexthop *nexthop; + struct nexthop_group *nhg; zvrf = vrf_info_lookup(VRF_DEFAULT); if (!zvrf) @@ -545,22 +545,41 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty) vty_out(vty, " VC-ID: %u\n", pw->data.ldp.pwid); vty_out(vty, " Status: %s \n", (zebra_pw_enabled(pw) && pw->status == PW_FORWARDING) - ? "Up" - : "Down"); + ? "Up" + : "Down"); re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); - if (re) { - for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) { - snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", - nexthop); - vty_out(vty, " Next Hop: %s\n", buf_nh); - if (nexthop->nh_label) - vty_out(vty, " Next Hop label: %u\n", - nexthop->nh_label->label[0]); - else - vty_out(vty, " Next Hop label: %s\n", - "-"); - } + if (re == NULL) + continue; + + nhg = rib_get_fib_nhg(re); + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", + nexthop); + vty_out(vty, " Next Hop: %s\n", buf_nh); + if (nexthop->nh_label) + vty_out(vty, " Next Hop label: %u\n", + nexthop->nh_label->label[0]); + else + vty_out(vty, " Next Hop label: %s\n", + "-"); + } + + /* Include any installed backups */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg == NULL) + continue; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", + nexthop); + vty_out(vty, " Next Hop: %s\n", buf_nh); + if (nexthop->nh_label) + vty_out(vty, " Next Hop label: %u\n", + nexthop->nh_label->label[0]); + else + vty_out(vty, " Next Hop label: %s\n", + "-"); } } } @@ -569,6 +588,7 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws) { struct route_entry *re; struct nexthop *nexthop; + struct nexthop_group *nhg; char buf_nbr[INET6_ADDRSTRLEN]; char buf_nh[100]; json_object *json_pw = NULL; @@ -603,23 +623,48 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws) : "Down"); re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id, &pw->nexthop, NULL); - if (re) { - for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) { - json_nexthop = json_object_new_object(); - snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); - json_object_string_add(json_nexthop, "nexthop", buf_nh); - if (nexthop->nh_label) - json_object_int_add( - json_nexthop, "nhLabel", - nexthop->nh_label->label[0]); - else - json_object_string_add(json_nexthop, "nhLabel", - "-"); + if (re == NULL) + goto done; + + nhg = rib_get_fib_nhg(re); + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); + json_object_string_add(json_nexthop, "nexthop", buf_nh); + if (nexthop->nh_label) + json_object_int_add( + json_nexthop, "nhLabel", + nexthop->nh_label->label[0]); + else + json_object_string_add(json_nexthop, "nhLabel", + "-"); - json_object_array_add(json_nexthops, json_nexthop); - } - json_object_object_add(json_pw, "nexthops", json_nexthops); + json_object_array_add(json_nexthops, json_nexthop); + } + + /* Include installed backup nexthops also */ + nhg = rib_get_fib_backup_nhg(re); + if (nhg == NULL) + goto done; + + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + json_nexthop = json_object_new_object(); + snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop); + json_object_string_add(json_nexthop, "nexthop", buf_nh); + if (nexthop->nh_label) + json_object_int_add( + json_nexthop, "nhLabel", + nexthop->nh_label->label[0]); + else + json_object_string_add(json_nexthop, "nhLabel", + "-"); + + json_object_array_add(json_nexthops, json_nexthop); } + +done: + + json_object_object_add(json_pw, "nexthops", json_nexthops); json_object_array_add(json_pws, json_pw); } @@ -680,8 +725,7 @@ static int zebra_pw_config(struct vty *vty) pw->local_label, pw->remote_label); else vty_out(vty, - " ! Incomplete config, specify the static " - "MPLS labels\n"); + " ! Incomplete config, specify the static MPLS labels\n"); if (pw->af != AF_UNSPEC) { char buf[INET6_ADDRSTRLEN]; @@ -689,8 +733,7 @@ static int zebra_pw_config(struct vty *vty) vty_out(vty, " neighbor %s\n", buf); } else vty_out(vty, - " ! Incomplete config, specify a neighbor " - "address\n"); + " ! Incomplete config, specify a neighbor address\n"); if (!(pw->flags & F_PSEUDOWIRE_CWORD)) vty_out(vty, " control-word exclude\n"); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 67b3812ed3..d1d56f2cdb 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -106,7 +106,8 @@ static const struct { [ZEBRA_ROUTE_PBR] = {ZEBRA_ROUTE_PBR, 200, 5}, [ZEBRA_ROUTE_BFD] = {ZEBRA_ROUTE_BFD, 255, 5}, [ZEBRA_ROUTE_OPENFABRIC] = {ZEBRA_ROUTE_OPENFABRIC, 115, 3}, - [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5} + [ZEBRA_ROUTE_VRRP] = {ZEBRA_ROUTE_VRRP, 255, 5}, + [ZEBRA_ROUTE_SRTE] = {ZEBRA_ROUTE_SRTE, 255, 5}, /* Any new route type added to zebra, should be mirrored here */ /* no entry/default: 150 */ @@ -472,6 +473,7 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, SET_FLAG(old->status, ROUTE_ENTRY_QUEUED); /* Free old FIB nexthop group */ + UNSET_FLAG(old->status, ROUTE_ENTRY_USE_FIB_NHG); if (old->fib_ng.nexthop) { nexthops_free(old->fib_ng.nexthop); old->fib_ng.nexthop = NULL; @@ -495,9 +497,6 @@ void rib_install_kernel(struct route_node *rn, struct route_entry *re, if (zvrf) zvrf->installs++; break; - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } return; @@ -542,9 +541,6 @@ void rib_uninstall_kernel(struct route_node *rn, struct route_entry *re) if (zvrf) zvrf->removals++; break; - /* Should never happen */ - case ZEBRA_DPLANE_REQUEST_PENDING: - break; } return; @@ -574,6 +570,7 @@ static void rib_uninstall(struct route_node *rn, struct route_entry *re) nexthops_free(re->fib_ng.nexthop); re->fib_ng.nexthop = NULL; } + UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); @@ -1081,8 +1078,7 @@ static void rib_process(struct route_node *rn) if (re != old_selected) { if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s: %s(%u):%s: imported via import-table but denied " - "by the ip protocol table route-map", + "%s: %s(%u):%s: imported via import-table but denied by the ip protocol table route-map", __func__, VRF_LOGNAME(vrf), vrf_id, buf); @@ -1376,7 +1372,7 @@ static bool rib_update_nhg_from_ctx(struct nexthop_group *re_nhg, continue; /* Check for a FIB nexthop corresponding to the RIB nexthop */ - if (nexthop_same(ctx_nexthop, nexthop) == false) { + if (!nexthop_same(ctx_nexthop, nexthop)) { /* If the FIB doesn't know about the nexthop, * it's not installed */ @@ -1491,7 +1487,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re, VRF_LOGNAME(vrf), re->vrf_id, dest_str); goto check_backups; - } else if (re->fib_ng.nexthop) { + } else if (CHECK_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG)) { /* * Free stale fib list and move on to check the rib nhg. */ @@ -1502,6 +1498,8 @@ static bool rib_update_re_from_ctx(struct route_entry *re, nexthops_free(re->fib_ng.nexthop); re->fib_ng.nexthop = NULL; + UNSET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); + /* Note that the installed nexthops have changed */ changed_p = true; } else { @@ -1547,20 +1545,15 @@ no_nexthops: */ if (IS_ZEBRA_DEBUG_RIB) zlog_debug( - "%s(%u):%s update_from_ctx(): changed %s, adding new fib nhg", + "%s(%u):%s update_from_ctx(): changed %s, adding new fib nhg%s", VRF_LOGNAME(vrf), re->vrf_id, dest_str, - (changed_p ? "true" : "false")); + (changed_p ? "true" : "false"), + ctxnhg->nexthop != NULL ? "" : " (empty)"); + /* Set the flag about the dedicated fib list */ + SET_FLAG(re->status, ROUTE_ENTRY_USE_FIB_NHG); if (ctxnhg->nexthop) copy_nexthops(&(re->fib_ng.nexthop), ctxnhg->nexthop, NULL); - else { - /* Bit of a special case when the fib has _no_ installed - * nexthops. - */ - nexthop = nexthop_new(); - nexthop->type = NEXTHOP_TYPE_IPV4; - _nexthop_add(&(re->fib_ng.nexthop), nexthop); - } check_backups: @@ -1612,7 +1605,7 @@ check_backups: } /* - * If a FIB backup nexthop set exists: attach a copy + * If a FIB backup nexthop set exists, attach a copy * to the route if any backup is installed */ if (ctxnhg && ctxnhg->nexthop) { @@ -1937,7 +1930,7 @@ static int rib_count_installed_nh(struct route_entry *re) /* The meaningful flag depends on where the installed * nexthops reside. */ - if (nhg == &(re->fib_backup_ng)) { + if (nhg == &(re->fib_ng)) { if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) count++; } else { @@ -1946,9 +1939,12 @@ static int rib_count_installed_nh(struct route_entry *re) } } - for (ALL_NEXTHOPS_PTR(rib_get_fib_backup_nhg(re), nexthop)) { - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) - count++; + nhg = rib_get_fib_backup_nhg(re); + if (nhg) { + for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) + count++; + } } return count; @@ -2630,6 +2626,8 @@ static void _route_entry_dump_nh(const struct route_entry *re, char nhname[PREFIX_STRLEN]; char backup_str[50]; char wgt_str[50]; + char temp_str[10]; + int i; struct interface *ifp; struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id); @@ -2655,8 +2653,12 @@ static void _route_entry_dump_nh(const struct route_entry *re, backup_str[0] = '\0'; if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { - snprintf(backup_str, sizeof(backup_str), "backup %d,", - (int)nexthop->backup_idx); + snprintf(backup_str, sizeof(backup_str), "backup "); + for (i = 0; i < nexthop->backup_num; i++) { + snprintf(temp_str, sizeof(temp_str), "%d, ", + nexthop->backup_idx[i]); + strlcat(backup_str, temp_str, sizeof(backup_str)); + } } wgt_str[0] = '\0'; @@ -3149,8 +3151,7 @@ void rib_delete(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type, if (nh) rnode_debug( rn, vrf_id, - "via %s ifindex %d type %d " - "doesn't exist in rib", + "via %s ifindex %d type %d doesn't exist in rib", inet_ntop(afi2family(afi), &nh->gate, buf2, sizeof(buf2)), @@ -3763,6 +3764,7 @@ static int rib_process_dplane_results(struct thread *thread) case DPLANE_OP_NEIGH_DELETE: case DPLANE_OP_VTEP_ADD: case DPLANE_OP_VTEP_DELETE: + case DPLANE_OP_NEIGH_DISCOVER: case DPLANE_OP_NONE: /* Don't expect this: just return the struct? */ dplane_ctx_fini(&ctx); diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index d1a5cf2a9d..cdc00f6026 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -46,6 +46,7 @@ #include "zebra/debug.h" #include "zebra/zebra_rnh.h" #include "zebra/zebra_routemap.h" +#include "zebra/zebra_srte.h" #include "zebra/interface.h" #include "zebra/zebra_memory.h" #include "zebra/zebra_errors.h" @@ -57,8 +58,6 @@ static void free_state(vrf_id_t vrf_id, struct route_entry *re, static void copy_state(struct rnh *rnh, const struct route_entry *re, struct route_node *rn); static int compare_state(struct route_entry *r1, struct route_entry *r2); -static int send_client(struct rnh *rnh, struct zserv *client, - enum rnh_type type, vrf_id_t vrf_id); static void print_rnh(struct route_node *rn, struct vty *vty); static int zebra_client_cleanup_rnh(struct zserv *client); @@ -305,7 +304,7 @@ void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, * We always need to respond with known information, * currently multiple daemons expect this behavior */ - send_client(rnh, client, type, vrf_id); + zebra_send_rnh_update(rnh, client, type, vrf_id, 0); } void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, @@ -531,8 +530,9 @@ static void zebra_rnh_eval_import_check_entry(struct zebra_vrf *zvrf, afi_t afi, } /* state changed, notify clients */ for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { - send_client(rnh, client, - RNH_IMPORT_CHECK_TYPE, zvrf->vrf->vrf_id); + zebra_send_rnh_update(rnh, client, + RNH_IMPORT_CHECK_TYPE, + zvrf->vrf->vrf_id, 0); } } } @@ -594,7 +594,8 @@ static void zebra_rnh_notify_protocol_clients(struct zebra_vrf *zvrf, afi_t afi, zebra_route_string(client->proto)); } - send_client(rnh, client, RNH_NEXTHOP_TYPE, zvrf->vrf->vrf_id); + zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE, + zvrf->vrf->vrf_id, 0); } if (re) @@ -1040,22 +1041,10 @@ static bool compare_valid_nexthops(struct route_entry *r1, * backups will be in the 'fib' list. */ nhg1 = rib_get_fib_backup_nhg(r1); - if (nhg1 == zebra_nhg_get_backup_nhg(r1->nhe)) - nhg1 = NULL; - nhg2 = rib_get_fib_backup_nhg(r2); - if (nhg2 == zebra_nhg_get_backup_nhg(r2->nhe)) - nhg2 = NULL; - - if (nhg1) - nh1 = nhg1->nexthop; - else - nh1 = NULL; - if (nhg2) - nh2 = nhg2->nexthop; - else - nh2 = NULL; + nh1 = nhg1->nexthop; + nh2 = nhg2->nexthop; while (1) { /* Find each backup list's next valid nexthop */ @@ -1119,8 +1108,9 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2) return 0; } -static int send_client(struct rnh *rnh, struct zserv *client, - enum rnh_type type, vrf_id_t vrf_id) +int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id, + uint32_t srte_color) { struct stream *s = NULL; struct route_entry *re; @@ -1129,6 +1119,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, struct nexthop *nh; struct route_node *rn; int ret; + uint32_t message = 0; int cmd = (type == RNH_IMPORT_CHECK_TYPE) ? ZEBRA_IMPORT_CHECK_UPDATE : ZEBRA_NEXTHOP_UPDATE; @@ -1140,6 +1131,11 @@ static int send_client(struct rnh *rnh, struct zserv *client, zclient_create_header(s, cmd, vrf_id); + /* Message flags. */ + if (srte_color) + SET_FLAG(message, ZAPI_MESSAGE_SRTE); + stream_putl(s, message); + stream_putw(s, rn->p.family); switch (rn->p.family) { case AF_INET: @@ -1156,6 +1152,9 @@ static int send_client(struct rnh *rnh, struct zserv *client, __func__, rn->p.family); goto failure; } + if (srte_color) + stream_putl(s, srte_color); + if (re) { struct zapi_nexthop znh; struct nexthop_group *nhg; @@ -1172,7 +1171,7 @@ static int send_client(struct rnh *rnh, struct zserv *client, for (ALL_NEXTHOPS_PTR(nhg, nh)) if (rnh_nexthop_valid(re, nh)) { zapi_nexthop_from_nexthop(&znh, nh); - ret = zapi_nexthop_encode(s, &znh, 0/*flags*/); + ret = zapi_nexthop_encode(s, &znh, 0, message); if (ret < 0) goto failure; @@ -1180,15 +1179,16 @@ static int send_client(struct rnh *rnh, struct zserv *client, } nhg = rib_get_fib_backup_nhg(re); - if (nhg == zebra_nhg_get_backup_nhg(re->nhe)) - nhg = NULL; - if (nhg) { for (ALL_NEXTHOPS_PTR(nhg, nh)) if (rnh_nexthop_valid(re, nh)) { zapi_nexthop_from_nexthop(&znh, nh); - zapi_nexthop_encode(s, &znh, - 0 /* flags */); + ret = zapi_nexthop_encode( + s, &znh, 0 /* flags */, + 0 /* message */); + if (ret < 0) + goto failure; + num++; } } diff --git a/zebra/zebra_rnh.h b/zebra/zebra_rnh.h index e744504920..ba12b1738f 100644 --- a/zebra/zebra_rnh.h +++ b/zebra/zebra_rnh.h @@ -50,6 +50,9 @@ extern struct rnh *zebra_lookup_rnh(struct prefix *p, vrf_id_t vrfid, extern void zebra_free_rnh(struct rnh *rnh); extern void zebra_add_rnh_client(struct rnh *rnh, struct zserv *client, enum rnh_type type, vrf_id_t vrfid); +extern int zebra_send_rnh_update(struct rnh *rnh, struct zserv *client, + enum rnh_type type, vrf_id_t vrf_id, + uint32_t srte_color); extern void zebra_register_rnh_pseudowire(vrf_id_t, struct zebra_pw *, bool *); extern void zebra_deregister_rnh_pseudowire(vrf_id_t, struct zebra_pw *); extern void zebra_remove_rnh_client(struct rnh *rnh, struct zserv *client, diff --git a/zebra/zebra_routemap.c b/zebra/zebra_routemap.c index 9b2a58fd17..8155f9acfe 100644 --- a/zebra/zebra_routemap.c +++ b/zebra/zebra_routemap.c @@ -351,7 +351,7 @@ static int ip_nht_rm_del(struct zebra_vrf *zvrf, const char *rmap, int rtype, return CMD_SUCCESS; } -DEFPY( +DEFPY_YANG( match_ip_address_prefix_len, match_ip_address_prefix_len_cmd, "match ip address prefix-len (0-32)$length", MATCH_STR @@ -371,7 +371,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( no_match_ip_address_prefix_len, no_match_ip_address_prefix_len_cmd, "no match ip address prefix-len [(0-32)]", NO_STR @@ -388,7 +388,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( match_ipv6_address_prefix_len, match_ipv6_address_prefix_len_cmd, "match ipv6 address prefix-len (0-128)$length", MATCH_STR @@ -408,7 +408,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( no_match_ipv6_address_prefix_len, no_match_ipv6_address_prefix_len_cmd, "no match ipv6 address prefix-len [(0-128)]", NO_STR @@ -425,7 +425,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( match_ip_nexthop_prefix_len, match_ip_nexthop_prefix_len_cmd, "match ip next-hop prefix-len (0-32)$length", MATCH_STR @@ -446,7 +446,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( no_match_ip_nexthop_prefix_len, no_match_ip_nexthop_prefix_len_cmd, "no match ip next-hop prefix-len [(0-32)]", NO_STR @@ -464,7 +464,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( match_source_protocol, match_source_protocol_cmd, "match source-protocol " FRR_REDIST_STR_ZEBRA "$proto", MATCH_STR @@ -482,7 +482,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( no_match_source_protocol, no_match_source_protocol_cmd, "no match source-protocol [" FRR_REDIST_STR_ZEBRA "]", NO_STR @@ -497,7 +497,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( match_source_instance, match_source_instance_cmd, "match source-instance (0-255)$instance", MATCH_STR @@ -515,7 +515,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( no_match_source_instance, no_match_source_instance_cmd, "no match source-instance [(0-255)]", NO_STR MATCH_STR @@ -531,7 +531,7 @@ DEFPY( /* set functions */ -DEFPY( +DEFPY_YANG( set_src, set_src_cmd, "set src <A.B.C.D$addrv4|X:X::X:X$addrv6>", SET_STR @@ -558,7 +558,7 @@ DEFPY( return nb_cli_apply_changes(vty, NULL); } -DEFPY( +DEFPY_YANG( no_set_src, no_set_src_cmd, "no set src [<A.B.C.D|X:X::X:X>]", NO_STR @@ -605,7 +605,7 @@ DEFUN (no_zebra_route_map_timer, return (CMD_SUCCESS); } -DEFPY (ip_protocol, +DEFPY_YANG (ip_protocol, ip_protocol_cmd, "ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA " $proto route-map ROUTE-MAP$rmap", @@ -639,7 +639,7 @@ DEFPY (ip_protocol, return ret; } -DEFPY (no_ip_protocol, +DEFPY_YANG (no_ip_protocol, no_ip_protocol_cmd, "no ip protocol " FRR_IP_PROTOCOL_MAP_STR_ZEBRA " $proto [route-map ROUTE-MAP$rmap]", @@ -673,7 +673,7 @@ DEFPY (no_ip_protocol, return ret; } -DEFPY (show_ip_protocol, +DEFPY_YANG (show_ip_protocol, show_ip_protocol_cmd, "show ip protocol [vrf <NAME$vrf_name|all$vrf_all>]", SHOW_STR @@ -686,7 +686,7 @@ DEFPY (show_ip_protocol, return ret; } -DEFPY (ipv6_protocol, +DEFPY_YANG (ipv6_protocol, ipv6_protocol_cmd, "ipv6 protocol " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA " $proto route-map ROUTE-MAP$rmap", @@ -720,7 +720,7 @@ DEFPY (ipv6_protocol, return ret; } -DEFPY (no_ipv6_protocol, +DEFPY_YANG (no_ipv6_protocol, no_ipv6_protocol_cmd, "no ipv6 protocol " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA " $proto [route-map ROUTE-MAP$rmap]", @@ -754,7 +754,7 @@ DEFPY (no_ipv6_protocol, return ret; } -DEFPY (show_ipv6_protocol, +DEFPY_YANG (show_ipv6_protocol, show_ipv6_protocol_cmd, "show ipv6 protocol [vrf <NAME$vrf_name|all$vrf_all>]", SHOW_STR @@ -767,7 +767,7 @@ DEFPY (show_ipv6_protocol, return ret; } -DEFPY (ip_protocol_nht_rmap, +DEFPY_YANG (ip_protocol_nht_rmap, ip_protocol_nht_rmap_cmd, "ip nht " FRR_IP_PROTOCOL_MAP_STR_ZEBRA " $proto route-map ROUTE-MAP$rmap", @@ -802,7 +802,7 @@ DEFPY (ip_protocol_nht_rmap, return ret; } -DEFPY (no_ip_protocol_nht_rmap, +DEFPY_YANG (no_ip_protocol_nht_rmap, no_ip_protocol_nht_rmap_cmd, "no ip nht " FRR_IP_PROTOCOL_MAP_STR_ZEBRA " $proto route-map [ROUTE-MAP$rmap]", @@ -836,7 +836,7 @@ DEFPY (no_ip_protocol_nht_rmap, return ret; } -DEFPY (show_ip_protocol_nht, +DEFPY_YANG (show_ip_protocol_nht, show_ip_protocol_nht_cmd, "show ip nht route-map [vrf <NAME$vrf_name|all$vrf_all>]", SHOW_STR @@ -850,7 +850,7 @@ DEFPY (show_ip_protocol_nht, return ret; } -DEFPY (ipv6_protocol_nht_rmap, +DEFPY_YANG (ipv6_protocol_nht_rmap, ipv6_protocol_nht_rmap_cmd, "ipv6 nht " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA " $proto route-map ROUTE-MAP$rmap", @@ -884,7 +884,7 @@ DEFPY (ipv6_protocol_nht_rmap, return ret; } -DEFPY (no_ipv6_protocol_nht_rmap, +DEFPY_YANG (no_ipv6_protocol_nht_rmap, no_ipv6_protocol_nht_rmap_cmd, "no ipv6 nht " FRR_IP6_PROTOCOL_MAP_STR_ZEBRA " $proto [route-map ROUTE-MAP$rmap]", @@ -918,7 +918,7 @@ DEFPY (no_ipv6_protocol_nht_rmap, return ret; } -DEFPY (show_ipv6_protocol_nht, +DEFPY_YANG (show_ipv6_protocol_nht, show_ipv6_protocol_nht_cmd, "show ipv6 nht route-map [vrf <NAME$vrf_name|all$vrf_all>]", SHOW_STR diff --git a/zebra/zebra_router.c b/zebra/zebra_router.c index 61fef8779f..66f2924555 100644 --- a/zebra/zebra_router.c +++ b/zebra/zebra_router.c @@ -133,6 +133,7 @@ struct route_table *zebra_router_get_table(struct zebra_vrf *zvrf, info->zvrf = zvrf; info->afi = afi; info->safi = safi; + info->table_id = tableid; route_table_set_info(zrt->table, info); zrt->table->cleanup = zebra_rtable_node_cleanup; diff --git a/zebra/zebra_router.h b/zebra/zebra_router.h index 863c5fa71c..f73a8f2d59 100644 --- a/zebra/zebra_router.h +++ b/zebra/zebra_router.h @@ -125,6 +125,12 @@ struct zebra_router { /* L3-VNI hash table (for EVPN). Only in default instance */ struct hash *l3vni_table; + /* Tables and other global info maintained for EVPN multihoming */ + struct zebra_evpn_mh_info *mh_info; + + /* EVPN MH broadcast domains indexed by the VID */ + struct hash *evpn_vlan_table; + struct hash *rules_hash; struct hash *ipset_hash; diff --git a/zebra/zebra_srte.c b/zebra/zebra_srte.c new file mode 100644 index 0000000000..d6043534e3 --- /dev/null +++ b/zebra/zebra_srte.c @@ -0,0 +1,378 @@ +/* Zebra SR-TE code + * Copyright (C) 2020 NetDEF, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <zebra.h> + +#include "lib/zclient.h" +#include "lib/lib_errors.h" + +#include "zebra/zebra_srte.h" +#include "zebra/zebra_memory.h" +#include "zebra/zebra_mpls.h" +#include "zebra/zebra_rnh.h" +#include "zebra/zapi_msg.h" + +DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_SR_POLICY, "SR Policy") + +static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy); + +/* Generate rb-tree of SR Policy instances. */ +static inline int +zebra_sr_policy_instance_compare(const struct zebra_sr_policy *a, + const struct zebra_sr_policy *b) +{ + return sr_policy_compare(&a->endpoint, &b->endpoint, a->color, + b->color); +} +RB_GENERATE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, + zebra_sr_policy_instance_compare) + +struct zebra_sr_policy_instance_head zebra_sr_policy_instances = + RB_INITIALIZER(&zebra_sr_policy_instances); + +struct zebra_sr_policy *zebra_sr_policy_add(uint32_t color, + struct ipaddr *endpoint, char *name) +{ + struct zebra_sr_policy *policy; + + policy = XCALLOC(MTYPE_ZEBRA_SR_POLICY, sizeof(*policy)); + policy->color = color; + policy->endpoint = *endpoint; + strlcpy(policy->name, name, sizeof(policy->name)); + policy->status = ZEBRA_SR_POLICY_DOWN; + RB_INSERT(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, + policy); + + return policy; +} + +void zebra_sr_policy_del(struct zebra_sr_policy *policy) +{ + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_deactivate(policy); + RB_REMOVE(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, + policy); + XFREE(MTYPE_ZEBRA_SR_POLICY, policy); +} + +struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, + struct ipaddr *endpoint) +{ + struct zebra_sr_policy policy = {}; + + policy.color = color; + policy.endpoint = *endpoint; + return RB_FIND(zebra_sr_policy_instance_head, + &zebra_sr_policy_instances, &policy); +} + +struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name) +{ + struct zebra_sr_policy *policy; + + // TODO: create index for policy names + RB_FOREACH (policy, zebra_sr_policy_instance_head, + &zebra_sr_policy_instances) { + if (strcmp(policy->name, name) == 0) + return policy; + } + + return NULL; +} + +static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, + struct zserv *client) +{ + const zebra_nhlfe_t *nhlfe; + struct stream *s; + uint32_t message = 0; + unsigned long nump = 0; + uint8_t num; + struct zapi_nexthop znh; + int ret; + + /* Get output stream. */ + s = stream_new(ZEBRA_MAX_PACKET_SIZ); + + zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, zvrf_id(policy->zvrf)); + + /* Message flags. */ + SET_FLAG(message, ZAPI_MESSAGE_SRTE); + stream_putl(s, message); + + switch (policy->endpoint.ipa_type) { + case IPADDR_V4: + stream_putw(s, AF_INET); + stream_putc(s, IPV4_MAX_BITLEN); + stream_put_in_addr(s, &policy->endpoint.ipaddr_v4); + break; + case IPADDR_V6: + stream_putw(s, AF_INET6); + stream_putc(s, IPV6_MAX_BITLEN); + stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN); + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, policy->endpoint.ipa_type); + exit(1); + } + stream_putl(s, policy->color); + + num = 0; + frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) { + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + continue; + + if (num == 0) { + stream_putc(s, re_type_from_lsp_type(nhlfe->type)); + stream_putw(s, 0); /* instance - not available */ + stream_putc(s, nhlfe->distance); + stream_putl(s, 0); /* metric - not available */ + nump = stream_get_endp(s); + stream_putc(s, 0); + } + + zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop); + ret = zapi_nexthop_encode(s, &znh, 0, message); + if (ret < 0) + goto failure; + + num++; + } + stream_putc_at(s, nump, num); + stream_putw_at(s, 0, stream_get_endp(s)); + + client->nh_last_upd_time = monotime(NULL); + client->last_write_cmd = ZEBRA_NEXTHOP_UPDATE; + return zserv_send_message(client, s); + +failure: + + stream_free(s); + return -1; +} + +static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy) +{ + struct rnh *rnh; + struct prefix p = {}; + struct zebra_vrf *zvrf; + struct listnode *node; + struct zserv *client; + + zvrf = policy->zvrf; + switch (policy->endpoint.ipa_type) { + case IPADDR_V4: + p.family = AF_INET; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = policy->endpoint.ipaddr_v4; + break; + case IPADDR_V6: + p.family = AF_INET6; + p.prefixlen = IPV6_MAX_BITLEN; + p.u.prefix6 = policy->endpoint.ipaddr_v6; + break; + default: + flog_warn(EC_LIB_DEVELOPMENT, + "%s: unknown policy endpoint address family: %u", + __func__, policy->endpoint.ipa_type); + exit(1); + } + + rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), RNH_NEXTHOP_TYPE); + if (!rnh) + return; + + for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_notify_update_client(policy, client); + else + /* Fallback to the IGP shortest path. */ + zebra_send_rnh_update(rnh, client, RNH_NEXTHOP_TYPE, + zvrf_id(zvrf), policy->color); + } +} + +static void zebra_sr_policy_activate(struct zebra_sr_policy *policy, + zebra_lsp_t *lsp) +{ + policy->status = ZEBRA_SR_POLICY_UP; + policy->lsp = lsp; + (void)zebra_sr_policy_bsid_install(policy); + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_UP); + zebra_sr_policy_notify_update(policy); +} + +static void zebra_sr_policy_update(struct zebra_sr_policy *policy, + zebra_lsp_t *lsp, + struct zapi_srte_tunnel *old_tunnel) +{ + bool bsid_changed; + bool segment_list_changed; + + policy->lsp = lsp; + + bsid_changed = + policy->segment_list.local_label != old_tunnel->local_label; + segment_list_changed = + policy->segment_list.label_num != old_tunnel->label_num + || memcmp(policy->segment_list.labels, old_tunnel->labels, + sizeof(mpls_label_t) + * policy->segment_list.label_num); + + /* Re-install label stack if necessary. */ + if (bsid_changed || segment_list_changed) { + zebra_sr_policy_bsid_uninstall(policy, old_tunnel->local_label); + (void)zebra_sr_policy_bsid_install(policy); + } + + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_UP); + + /* Handle segment-list update. */ + if (segment_list_changed) + zebra_sr_policy_notify_update(policy); +} + +static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy) +{ + policy->status = ZEBRA_SR_POLICY_DOWN; + policy->lsp = NULL; + zebra_sr_policy_bsid_uninstall(policy, + policy->segment_list.local_label); + zsend_sr_policy_notify_status(policy->color, &policy->endpoint, + policy->name, ZEBRA_SR_POLICY_DOWN); + zebra_sr_policy_notify_update(policy); +} + +int zebra_sr_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srte_tunnel *new_tunnel) +{ + struct zapi_srte_tunnel old_tunnel = policy->segment_list; + zebra_lsp_t *lsp; + + if (new_tunnel) + policy->segment_list = *new_tunnel; + + /* Try to resolve the Binding-SID nexthops. */ + lsp = mpls_lsp_find(policy->zvrf, policy->segment_list.labels[0]); + if (!lsp || !lsp->best_nhlfe + || lsp->addr_family != ipaddr_family(&policy->endpoint)) { + if (policy->status == ZEBRA_SR_POLICY_UP) + zebra_sr_policy_deactivate(policy); + return -1; + } + + /* First label was resolved successfully. */ + if (policy->status == ZEBRA_SR_POLICY_DOWN) + zebra_sr_policy_activate(policy, lsp); + else + zebra_sr_policy_update(policy, lsp, &old_tunnel); + + return 0; +} + +int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy) +{ + struct zapi_srte_tunnel *zt = &policy->segment_list; + zebra_nhlfe_t *nhlfe; + + if (zt->local_label == MPLS_LABEL_NONE) + return 0; + + frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, nhlfe) { + uint8_t num_out_labels; + mpls_label_t *out_labels; + mpls_label_t null_label = MPLS_LABEL_IMPLICIT_NULL; + + if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) + || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) + continue; + + /* + * Don't push the first SID if the corresponding action in the + * LFIB is POP. + */ + if (!nhlfe->nexthop->nh_label + || !nhlfe->nexthop->nh_label->num_labels + || nhlfe->nexthop->nh_label->label[0] + == MPLS_LABEL_IMPLICIT_NULL) { + if (zt->label_num > 1) { + num_out_labels = zt->label_num - 1; + out_labels = &zt->labels[1]; + } else { + num_out_labels = 1; + out_labels = &null_label; + } + } else { + num_out_labels = zt->label_num; + out_labels = zt->labels; + } + + if (mpls_lsp_install( + policy->zvrf, zt->type, zt->local_label, + num_out_labels, out_labels, nhlfe->nexthop->type, + &nhlfe->nexthop->gate, nhlfe->nexthop->ifindex) + < 0) + return -1; + } + + return 0; +} + +void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, + mpls_label_t old_bsid) +{ + struct zapi_srte_tunnel *zt = &policy->segment_list; + + mpls_lsp_uninstall_all_vrf(policy->zvrf, zt->type, old_bsid); +} + +int zebra_sr_policy_label_update(mpls_label_t label, + enum zebra_sr_policy_update_label_mode mode) +{ + struct zebra_sr_policy *policy; + + RB_FOREACH (policy, zebra_sr_policy_instance_head, + &zebra_sr_policy_instances) { + mpls_label_t next_hop_label; + + next_hop_label = policy->segment_list.labels[0]; + if (next_hop_label != label) + continue; + + switch (mode) { + case ZEBRA_SR_POLICY_LABEL_CREATED: + case ZEBRA_SR_POLICY_LABEL_UPDATED: + case ZEBRA_SR_POLICY_LABEL_REMOVED: + zebra_sr_policy_validate(policy, NULL); + break; + } + } + + return 0; +} + +void zebra_srte_init(void) +{ +} diff --git a/zebra/zebra_srte.h b/zebra/zebra_srte.h new file mode 100644 index 0000000000..e5239b7b7b --- /dev/null +++ b/zebra/zebra_srte.h @@ -0,0 +1,74 @@ +/* Zebra's client header. + * Copyright (C) 2020 Netdef, Inc. + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _ZEBRA_SRTE_H +#define _ZEBRA_SRTE_H + +#include "zebra/zebra_mpls.h" + +#include "lib/zclient.h" +#include "lib/srte.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum zebra_sr_policy_update_label_mode { + ZEBRA_SR_POLICY_LABEL_CREATED = 1, + ZEBRA_SR_POLICY_LABEL_UPDATED = 2, + ZEBRA_SR_POLICY_LABEL_REMOVED = 3, +}; + +struct zebra_sr_policy { + RB_ENTRY(zebra_sr_policy) entry; + uint32_t color; + struct ipaddr endpoint; + char name[SRTE_POLICY_NAME_MAX_LENGTH]; + enum zebra_sr_policy_status status; + struct zapi_srte_tunnel segment_list; + zebra_lsp_t *lsp; + struct zebra_vrf *zvrf; +}; +RB_HEAD(zebra_sr_policy_instance_head, zebra_sr_policy); +RB_PROTOTYPE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, + zebra_sr_policy_instance_compare) + +extern struct zebra_sr_policy_instance_head zebra_sr_policy_instances; + +struct zebra_sr_policy * +zebra_sr_policy_add(uint32_t color, struct ipaddr *endpoint, char *name); +void zebra_sr_policy_del(struct zebra_sr_policy *policy); +struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, + struct ipaddr *endpoint); +struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name); +int zebra_sr_policy_validate(struct zebra_sr_policy *policy, + struct zapi_srte_tunnel *new_tunnel); +int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy); +void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, + mpls_label_t old_bsid); +void zebra_srte_init(void); +int zebra_sr_policy_label_update(mpls_label_t label, + enum zebra_sr_policy_update_label_mode mode); + +#ifdef __cplusplus +} +#endif + +#endif /* _ZEBRA_SRTE_H */ diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index ee1e251a69..d102b02a21 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -544,6 +544,7 @@ static int vrf_config_write(struct vty *vty) zebra_routemap_config_write_protocol(vty, zvrf); + router_id_write(vty, zvrf); if (zvrf_id(zvrf) != VRF_DEFAULT) vty_endframe(vty, " exit-vrf\n!\n"); diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h index d262faa070..910d192317 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -92,6 +92,11 @@ struct zebra_vrf { struct list *rid_all_sorted_list; struct list *rid_lo_sorted_list; struct prefix rid_user_assigned; + struct list _rid6_all_sorted_list; + struct list _rid6_lo_sorted_list; + struct list *rid6_all_sorted_list; + struct list *rid6_lo_sorted_list; + struct prefix rid6_user_assigned; /* * Back pointer to the owning namespace. @@ -125,9 +130,9 @@ struct zebra_vrf { #define MPLS_FLAG_SCHEDULE_LSPS (1 << 0) /* - * VNI hash table (for EVPN). Only in the EVPN instance. + * EVPN hash table. Only in the EVPN instance. */ - struct hash *vni_table; + struct hash *evpn_table; /* * Whether EVPN is enabled or not. Only in the EVPN instance. diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 1da2660509..b6d0b26125 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -44,6 +44,7 @@ #include "zebra/zebra_routemap.h" #include "lib/json.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn_mh.h" #ifndef VTYSH_EXTRACT_PL #include "zebra/zebra_vty_clippy.c" #endif @@ -55,6 +56,8 @@ #include "zebra/zebra_nhg.h" #include "zebra/interface.h" #include "northbound_cli.h" +#include "zebra/zebra_nb.h" +#include "zebra/kernel_netlink.h" extern int allow_delete; @@ -71,6 +74,12 @@ static void vty_show_ip_route_summary(struct vty *vty, static void vty_show_ip_route_summary_prefix(struct vty *vty, struct route_table *table, bool use_json); +/* Helper api to format a nexthop in the 'detailed' output path. */ +static void show_nexthop_detail_helper(struct vty *vty, + const struct route_entry *re, + const struct nexthop *nexthop, + bool is_backup); + DEFUN (ip_multicast_mode, ip_multicast_mode_cmd, @@ -166,11 +175,24 @@ DEFUN (show_ip_rpf_addr, } static char re_status_output_char(const struct route_entry *re, - const struct nexthop *nhop) + const struct nexthop *nhop, + bool is_fib) { if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) { - if (!CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) && - !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) + bool star_p = false; + + if (nhop && + !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_DUPLICATE) && + !CHECK_FLAG(nhop->flags, NEXTHOP_FLAG_RECURSIVE)) { + /* More-specific test for 'fib' output */ + if (is_fib) { + star_p = !!CHECK_FLAG(nhop->flags, + NEXTHOP_FLAG_FIB); + } else + star_p = true; + } + + if (star_p) return '*'; else return ' '; @@ -190,19 +212,51 @@ static char re_status_output_char(const struct route_entry *re, } /* - * TODO -- Show backup nexthop info + * Show backup nexthop info, in the 'detailed' output path */ static void show_nh_backup_helper(struct vty *vty, - const struct nhg_hash_entry *nhe, + const struct route_entry *re, const struct nexthop *nexthop) { + const struct nexthop *start, *backup, *temp; + int i, idx; + /* Double-check that there _is_ a backup */ - if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) + if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP) || + re->nhe->backup_info == NULL || re->nhe->backup_info->nhe == NULL || + re->nhe->backup_info->nhe->nhg.nexthop == NULL) return; - /* Locate the backup nexthop */ + /* Locate the backup nexthop(s) */ + start = re->nhe->backup_info->nhe->nhg.nexthop; + for (i = 0; i < nexthop->backup_num; i++) { + /* Format the backup(s) (indented) */ + backup = start; + for (idx = 0; idx < nexthop->backup_idx[i]; idx++) { + backup = backup->next; + if (backup == NULL) + break; + } - /* Format the backup (indented) */ + /* It's possible for backups to be recursive too, + * so walk the recursive resolution list if present. + */ + temp = backup; + while (backup) { + vty_out(vty, " "); + show_nexthop_detail_helper(vty, re, backup, + true /*backup*/); + vty_out(vty, "\n"); + + if (backup->resolved && temp == backup) + backup = backup->resolved; + else + backup = nexthop_next(backup); + + if (backup == temp->next) + break; + } + } } @@ -212,14 +266,20 @@ static void show_nh_backup_helper(struct vty *vty, */ static void show_nexthop_detail_helper(struct vty *vty, const struct route_entry *re, - const struct nexthop *nexthop) + const struct nexthop *nexthop, + bool is_backup) { char addrstr[32]; char buf[MPLS_LABEL_STRLEN]; + int i; - vty_out(vty, " %c%s", - re_status_output_char(re, nexthop), - nexthop->rparent ? " " : ""); + if (is_backup) + vty_out(vty, " b%s", + nexthop->rparent ? " " : ""); + else + vty_out(vty, " %c%s", + re_status_output_char(re, nexthop, false), + nexthop->rparent ? " " : ""); switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -333,6 +393,13 @@ static void show_nexthop_detail_helper(struct vty *vty, if (nexthop->weight) vty_out(vty, ", weight %u", nexthop->weight); + + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + vty_out(vty, ", backup %d", nexthop->backup_idx[0]); + + for (i = 1; i < nexthop->backup_num; i++) + vty_out(vty, ",%d", nexthop->backup_idx[i]); + } } /* New RIB. Detailed information for IPv4 route. */ @@ -403,12 +470,13 @@ static void vty_show_ip_route_detail(struct vty *vty, struct route_node *rn, for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { /* Use helper to format each nexthop */ - show_nexthop_detail_helper(vty, re, nexthop); + show_nexthop_detail_helper(vty, re, nexthop, + false /*not backup*/); vty_out(vty, "\n"); - /* Include backup info, if present */ + /* Include backup(s), if present */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) - show_nh_backup_helper(vty, re->nhe, nexthop); + show_nh_backup_helper(vty, re, nexthop); } vty_out(vty, "\n"); } @@ -422,6 +490,7 @@ static void show_route_nexthop_helper(struct vty *vty, const struct nexthop *nexthop) { char buf[MPLS_LABEL_STRLEN]; + int i; switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -494,6 +563,10 @@ static void show_route_nexthop_helper(struct vty *vty, if (inet_ntop(AF_INET, &nexthop->src.ipv4, buf, sizeof(buf))) vty_out(vty, ", src %s", buf); + /* SR-TE information */ + if (nexthop->srte_color) + vty_out(vty, ", SR-TE color %u", + nexthop->srte_color); } break; case NEXTHOP_TYPE_IPV6: @@ -519,8 +592,12 @@ static void show_route_nexthop_helper(struct vty *vty, if (nexthop->weight) vty_out(vty, ", weight %u", nexthop->weight); - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) - vty_out(vty, ", backup %d", nexthop->backup_idx); + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + vty_out(vty, ", backup %d", nexthop->backup_idx[0]); + + for (i = 1; i < nexthop->backup_num; i++) + vty_out(vty, ",%d", nexthop->backup_idx[i]); + } } /* @@ -534,6 +611,8 @@ static void show_nexthop_json_helper(json_object *json_nexthop, char buf[SRCDEST2STR_BUFFER]; struct vrf *vrf = NULL; json_object *json_labels = NULL; + json_object *json_backups = NULL; + int i; json_object_int_add(json_nexthop, "flags", nexthop->flags); @@ -645,9 +724,17 @@ static void show_nexthop_json_helper(json_object *json_nexthop, json_object_boolean_true_add(json_nexthop, "recursive"); - if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) - json_object_int_add(json_nexthop, "backupIndex", - nexthop->backup_idx); + if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { + json_backups = json_object_new_array(); + for (i = 0; i < nexthop->backup_num; i++) { + json_object_array_add( + json_backups, + json_object_new_int(nexthop->backup_idx[i])); + } + + json_object_object_add(json_nexthop, "backupIndex", + json_backups); + } switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: @@ -699,24 +786,28 @@ static void show_nexthop_json_helper(json_object *json_nexthop, json_object_int_add(json_nexthop, "weight", nexthop->weight); + if (nexthop->srte_color) + json_object_int_add(json_nexthop, "srteColor", + nexthop->srte_color); } static void vty_show_ip_route(struct vty *vty, struct route_node *rn, struct route_entry *re, json_object *json, bool is_fib) { - struct nexthop *nexthop; + const struct nexthop *nexthop; int len = 0; char buf[SRCDEST2STR_BUFFER]; json_object *json_nexthops = NULL; json_object *json_nexthop = NULL; json_object *json_route = NULL; time_t uptime; - struct vrf *vrf = NULL; - rib_dest_t *dest = rib_dest_from_rnode(rn); - struct nexthop_group *nhg; + const struct vrf *vrf = NULL; + const rib_dest_t *dest = rib_dest_from_rnode(rn); + const struct nexthop_group *nhg; char up_str[MONOTIME_STRLEN]; - bool first_p; + bool first_p = true; + bool nhg_from_backup = false; uptime = monotime(NULL); uptime -= re->uptime; @@ -791,9 +882,11 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { json_nexthop = json_object_new_object(); + show_nexthop_json_helper(json_nexthop, + nexthop, re); - show_nexthop_json_helper(json_nexthop, nexthop, re); - json_object_array_add(json_nexthops, json_nexthop); + json_object_array_add(json_nexthops, + json_nexthop); } json_object_object_add(json_route, "nexthops", json_nexthops); @@ -804,7 +897,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, else nhg = zebra_nhg_get_backup_nhg(re->nhe); - if (nhg) { + if (nhg && nhg->nexthop) { json_nexthops = json_object_new_array(); for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { @@ -824,42 +917,62 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn, return; } + /* Prefix information, and first nexthop. If we're showing 'fib', + * and there are no installed primary nexthops, see if there are any + * backup nexthops and start with those. + */ + if (is_fib && nhg->nexthop == NULL) { + nhg = rib_get_fib_backup_nhg(re); + nhg_from_backup = true; + } + + len = vty_out(vty, "%c", zebra_route_char(re->type)); + if (re->instance) + len += vty_out(vty, "[%d]", re->instance); + if (nhg_from_backup && nhg->nexthop) { + len += vty_out( + vty, "%cb%c %s", + CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ', + re_status_output_char(re, nhg->nexthop, is_fib), + srcdest_rnode2str(rn, buf, sizeof(buf))); + } else { + len += vty_out( + vty, "%c%c %s", + CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ', + re_status_output_char(re, nhg->nexthop, is_fib), + srcdest_rnode2str(rn, buf, sizeof(buf))); + } + + /* Distance and metric display. */ + if (((re->type == ZEBRA_ROUTE_CONNECT) && + (re->distance || re->metric)) || + (re->type != ZEBRA_ROUTE_CONNECT)) + len += vty_out(vty, " [%u/%u]", re->distance, + re->metric); + /* Nexthop information. */ - first_p = true; for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { if (first_p) { first_p = false; - - /* Prefix information. */ - len = vty_out(vty, "%c", zebra_route_char(re->type)); - if (re->instance) - len += vty_out(vty, "[%d]", re->instance); - len += vty_out( - vty, "%c%c %s", - CHECK_FLAG(re->flags, ZEBRA_FLAG_SELECTED) - ? '>' - : ' ', - re_status_output_char(re, nexthop), - srcdest_rnode2str(rn, buf, sizeof(buf))); - - /* Distance and metric display. */ - if (((re->type == ZEBRA_ROUTE_CONNECT) && - (re->distance || re->metric)) || - (re->type != ZEBRA_ROUTE_CONNECT)) - len += vty_out(vty, " [%u/%u]", re->distance, - re->metric); + } else if (nhg_from_backup) { + vty_out(vty, " b%c%*c", + re_status_output_char(re, nexthop, is_fib), + len - 3 + (2 * nexthop_level(nexthop)), ' '); } else { vty_out(vty, " %c%*c", - re_status_output_char(re, nexthop), + re_status_output_char(re, nexthop, is_fib), len - 3 + (2 * nexthop_level(nexthop)), ' '); } show_route_nexthop_helper(vty, re, nexthop); - vty_out(vty, ", %s\n", up_str); } - /* Check for backup info if present */ + /* If we only had backup nexthops, we're done */ + if (nhg_from_backup) + return; + + /* Check for backup nexthop info if present */ if (is_fib) nhg = rib_get_fib_backup_nhg(re); else @@ -1206,7 +1319,7 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) vty_out(vty, " [backup %d]", - nexthop->backup_idx); + nexthop->backup_idx[0]); vty_out(vty, "\n"); continue; @@ -1214,22 +1327,13 @@ static void show_nexthop_group_out(struct vty *vty, struct nhg_hash_entry *nhe) /* TODO -- print more useful backup info */ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { - struct nexthop *backup; int i; - i = 0; - for (ALL_NEXTHOPS(nhe->backup_info->nhe->nhg, backup)) { - if (i == nexthop->backup_idx) - break; - i++; - } + vty_out(vty, "[backup"); + for (i = 0; i < nexthop->backup_num; i++) + vty_out(vty, " %d", nexthop->backup_idx[i]); - /* TODO */ - if (backup) - vty_out(vty, " [backup %d]", - nexthop->backup_idx); - else - vty_out(vty, " [backup INVALID]"); + vty_out(vty, "]"); } vty_out(vty, "\n"); @@ -2304,12 +2408,9 @@ DEFUN (vrf_vni_mapping, "VNI-ID\n" "prefix-routes-only\n") { - int ret = 0; int filter = 0; ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); - vni_t vni = strtoul(argv[1]->arg, NULL, 10); - char err[ERR_STR_SZ]; assert(vrf); assert(zvrf); @@ -2317,14 +2418,15 @@ DEFUN (vrf_vni_mapping, if (argc == 3) filter = 1; - /* Mark as having FRR configuration */ - vrf_set_user_cfged(vrf); - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, ERR_STR_SZ, - filter, 1); - if (ret != 0) { - vty_out(vty, "%s\n", err); - return CMD_WARNING; - } + nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_CREATE, NULL); + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_MODIFY, + argv[1]->arg); + + if (filter) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only", + NB_OP_MODIFY, "true"); + + nb_cli_apply_changes(vty, NULL); return CMD_SUCCESS; } @@ -2337,12 +2439,10 @@ DEFUN (no_vrf_vni_mapping, "VNI-ID\n" "prefix-routes-only\n") { - int ret = 0; int filter = 0; - char err[ERR_STR_SZ]; - vni_t vni = strtoul(argv[2]->arg, NULL, 10); ZEBRA_DECLVAR_CONTEXT(vrf, zvrf); + vni_t vni = strtoul(argv[2]->arg, NULL, 10); assert(vrf); assert(zvrf); @@ -2350,16 +2450,22 @@ DEFUN (no_vrf_vni_mapping, if (argc == 4) filter = 1; - ret = zebra_vxlan_process_vrf_vni_cmd(zvrf, vni, err, - ERR_STR_SZ, filter, 0); - if (ret != 0) { - vty_out(vty, "%s\n", err); + if (zvrf->l3vni != vni) { + vty_out(vty, "VNI %d doesn't exist in VRF: %s \n", vni, + zvrf->vrf->name); return CMD_WARNING; } - /* If no other FRR config for this VRF, mark accordingly. */ - if (!zebra_vrf_has_config(zvrf)) - vrf_reset_user_cfged(vrf); + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/l3vni-id", NB_OP_DESTROY, + argv[2]->arg); + + if (filter) + nb_cli_enqueue_change(vty, "./frr-zebra:zebra/prefix-only", + NB_OP_DESTROY, "true"); + + nb_cli_enqueue_change(vty, "./frr-zebra:zebra", NB_OP_DESTROY, NULL); + + nb_cli_apply_changes(vty, NULL); return CMD_SUCCESS; } @@ -2419,6 +2525,81 @@ DEFUN (show_evpn_global, return CMD_SUCCESS; } +DEFPY(show_evpn_es, + show_evpn_es_cmd, + "show evpn es [NAME$esi_str] [json$json] [detail$detail]", + SHOW_STR + "EVPN\n" + "Ethernet Segment\n" + "ES ID\n" + JSON_STR + "Detailed information\n") +{ + esi_t esi; + bool uj = !!json; + + if (esi_str) { + if (!str_to_esi(esi_str, &esi)) { + vty_out(vty, "%% Malformed ESI\n"); + return CMD_WARNING; + } + zebra_evpn_es_show_esi(vty, uj, &esi); + } else { + if (detail) + zebra_evpn_es_show_detail(vty, uj); + else + zebra_evpn_es_show(vty, uj); + } + + return CMD_SUCCESS; +} + +DEFPY(show_evpn_es_evi, + show_evpn_es_evi_cmd, + "show evpn es-evi [vni (1-16777215)$vni] [json$json] [detail$detail]", + SHOW_STR + "EVPN\n" + "Ethernet Segment per EVI\n" + "VxLAN Network Identifier\n" + "VNI\n" + JSON_STR + "Detailed information\n") +{ + bool uj = !!json; + bool ud = !!detail; + + if (vni) + zebra_evpn_es_evi_show_vni(vty, uj, vni, ud); + else + zebra_evpn_es_evi_show(vty, uj, ud); + + return CMD_SUCCESS; +} + +DEFPY(show_evpn_access_vlan, + show_evpn_access_vlan_cmd, + "show evpn access-vlan [(1-4094)$vid] [json$json] [detail$detail]", + SHOW_STR + "EVPN\n" + "Access VLANs\n" + "VLAN ID\n" + JSON_STR + "Detailed information\n") +{ + bool uj = !!json; + + if (vid) { + zebra_evpn_acc_vl_show_vid(vty, uj, vid); + } else { + if (detail) + zebra_evpn_acc_vl_show_detail(vty, uj); + else + zebra_evpn_acc_vl_show(vty, uj); + } + + return CMD_SUCCESS; +} + DEFUN (show_evpn_vni, show_evpn_vni_cmd, "show evpn vni [json]", @@ -3217,6 +3398,11 @@ static int config_write_protocol(struct vty *vty) if (!zebra_nhg_kernel_nexthops_enabled()) vty_out(vty, "no zebra nexthop kernel enable\n"); +#ifdef HAVE_NETLINK + /* Include netlink info */ + netlink_config_write_helper(vty); +#endif /* HAVE_NETLINK */ + return 1; } @@ -3236,8 +3422,7 @@ DEFUN (show_zebra, RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) { struct zebra_vrf *zvrf = vrf->info; - vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64 - " %10" PRIu64 " %10" PRIu64 "\n", + vty_out(vty, "%-25s %10" PRIu64 " %10" PRIu64 " %10" PRIu64" %10" PRIu64 " %10" PRIu64 "\n", vrf->name, zvrf->installs, zvrf->removals, zvrf->neigh_updates, zvrf->lsp_installs, zvrf->lsp_removals); @@ -3469,9 +3654,6 @@ static int config_write_table(struct vty *vty) /* IPForwarding configuration write function. */ static int config_write_forwarding(struct vty *vty) { - /* FIXME: Find better place for that. */ - router_id_write(vty); - if (!ipforward()) vty_out(vty, "no ip forwarding\n"); if (!ipforward_ipv6()) @@ -3543,6 +3725,44 @@ DEFUN_HIDDEN (show_frr, return CMD_SUCCESS; } +#ifdef HAVE_NETLINK +DEFUN_HIDDEN(zebra_kernel_netlink_batch_tx_buf, + zebra_kernel_netlink_batch_tx_buf_cmd, + "zebra kernel netlink batch-tx-buf (1-1048576) (1-1048576)", + ZEBRA_STR + "Zebra kernel interface\n" + "Set Netlink parameters\n" + "Set batch buffer size and send threshold\n" + "Size of the buffer\n" + "Send threshold\n") +{ + uint32_t bufsize = 0, threshold = 0; + + bufsize = strtoul(argv[4]->arg, NULL, 10); + threshold = strtoul(argv[5]->arg, NULL, 10); + + netlink_set_batch_buffer_size(bufsize, threshold, true); + + return CMD_SUCCESS; +} + +DEFUN_HIDDEN(no_zebra_kernel_netlink_batch_tx_buf, + no_zebra_kernel_netlink_batch_tx_buf_cmd, + "no zebra kernel netlink batch-tx-buf [(0-1048576)] [(0-1048576)]", + NO_STR ZEBRA_STR + "Zebra kernel interface\n" + "Set Netlink parameters\n" + "Set batch buffer size and send threshold\n" + "Size of the buffer\n" + "Send threshold\n") +{ + netlink_set_batch_buffer_size(0, 0, false); + + return CMD_SUCCESS; +} + +#endif /* HAVE_NETLINK */ + /* IP node for static routes. */ static int zebra_ip_config(struct vty *vty); static struct cmd_node ip_node = { @@ -3641,6 +3861,9 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_vni_cmd); install_element(VIEW_NODE, &show_evpn_vni_detail_cmd); install_element(VIEW_NODE, &show_evpn_vni_vni_cmd); + install_element(VIEW_NODE, &show_evpn_es_cmd); + install_element(VIEW_NODE, &show_evpn_es_evi_cmd); + install_element(VIEW_NODE, &show_evpn_access_vlan_cmd); install_element(VIEW_NODE, &show_evpn_rmac_vni_mac_cmd); install_element(VIEW_NODE, &show_evpn_rmac_vni_cmd); install_element(VIEW_NODE, &show_evpn_rmac_vni_all_cmd); @@ -3677,5 +3900,10 @@ void zebra_vty_init(void) install_element(CONFIG_NODE, &zebra_dplane_queue_limit_cmd); install_element(CONFIG_NODE, &no_zebra_dplane_queue_limit_cmd); +#ifdef HAVE_NETLINK + install_element(CONFIG_NODE, &zebra_kernel_netlink_batch_tx_buf_cmd); + install_element(CONFIG_NODE, &no_zebra_kernel_netlink_batch_tx_buf_cmd); +#endif /* HAVE_NETLINK */ + install_element(VIEW_NODE, &zebra_show_routing_tables_summary_cmd); } diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 998c035656..13a58bc34a 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -49,75 +49,31 @@ #include "zebra/zebra_ns.h" #include "zebra/zebra_vrf.h" #include "zebra/zebra_vxlan.h" +#include "zebra/zebra_evpn.h" +#include "zebra/zebra_evpn_mac.h" +#include "zebra/zebra_evpn_neigh.h" #include "zebra/zebra_vxlan_private.h" +#include "zebra/zebra_evpn_mh.h" +#include "zebra/zebra_evpn_vxlan.h" #include "zebra/zebra_router.h" DEFINE_MTYPE_STATIC(ZEBRA, HOST_PREFIX, "host prefix"); -DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash"); DEFINE_MTYPE_STATIC(ZEBRA, ZL3VNI, "L3 VNI hash"); -DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP"); -DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC"); -DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor"); +DEFINE_MTYPE_STATIC(ZEBRA, L3VNI_MAC, "EVPN L3VNI MAC"); +DEFINE_MTYPE_STATIC(ZEBRA, L3NEIGH, "EVPN Neighbor"); DEFINE_MTYPE_STATIC(ZEBRA, ZVXLAN_SG, "zebra VxLAN multicast group"); DEFINE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni, bool delete, const char *reason), (rmac, zl3vni, delete, reason)) -/* definitions */ -/* PMSI strings. */ -#define VXLAN_FLOOD_STR_NO_INFO "-" -#define VXLAN_FLOOD_STR_DEFAULT VXLAN_FLOOD_STR_NO_INFO -static const struct message zvtep_flood_str[] = { - {VXLAN_FLOOD_DISABLED, VXLAN_FLOOD_STR_NO_INFO}, - {VXLAN_FLOOD_PIM_SM, "PIM-SM"}, - {VXLAN_FLOOD_HEAD_END_REPL, "HER"}, - {0} -}; - - /* static function declarations */ -static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, - uint16_t cmd); -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); -static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt); -static void zvni_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt); -static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, - void **args); +static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket, + void **args); static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, json_object *json); static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, json_object *json); -static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json); -static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt); -static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt); -static void zvni_print(zebra_vni_t *zvni, void **ctxt); -static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[]); - -static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, - struct ipaddr *ip, uint8_t flags, - uint32_t seq, int state, uint16_t cmd); -static unsigned int neigh_hash_keymake(const void *p); -static void *zvni_neigh_alloc(void *p); -static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, - struct ethaddr *mac); -static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n); -static void zvni_neigh_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags); -static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip); -static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, - uint8_t flags, uint32_t seq); -static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, - uint8_t flags, int state); -static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n); -static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n); -static int zvni_neigh_probe(zebra_vni_t *zvni, zebra_neigh_t *n); -static zebra_vni_t *zvni_from_svi(struct interface *ifp, - struct interface *br_if); -static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if); -static struct interface *zvni_map_to_macvlan(struct interface *br_if, - struct interface *svi_if); +static void zevpn_print_mac_hash_all_evpn(struct hash_bucket *bucket, void *ctxt); /* l3-vni next-hop neigh related APIs */ static zebra_neigh_t *zl3vni_nh_lookup(zebra_l3vni_t *zl3vni, @@ -142,75 +98,13 @@ static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac); /* l3-vni related APIs*/ -static zebra_l3vni_t *zl3vni_lookup(vni_t vni); static void *zl3vni_alloc(void *p); static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id); static int zl3vni_del(zebra_l3vni_t *zl3vni); static void zebra_vxlan_process_l3vni_oper_up(zebra_l3vni_t *zl3vni); static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni); -static unsigned int mac_hash_keymake(const void *p); -static bool mac_cmp(const void *p1, const void *p2); -static void *zvni_mac_alloc(void *p); -static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr); -static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac); -static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags); -static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *macaddr); -static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, - uint8_t flags, uint32_t seq); -static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr); -static zebra_vni_t *zvni_map_vlan(struct interface *ifp, - struct interface *br_if, vlanid_t vid); -static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac); -static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac); -static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt); - -static unsigned int vni_hash_keymake(const void *p); -static void *zvni_alloc(void *p); -static zebra_vni_t *zvni_lookup(vni_t vni); -static zebra_vni_t *zvni_add(vni_t vni); -static int zvni_del(zebra_vni_t *zvni); -static int zvni_send_add_to_client(zebra_vni_t *zvni); -static int zvni_send_del_to_client(vni_t vni); -static void zvni_build_hash_table(void); -static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep); -static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip); -static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip, - int flood_control); -static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep); -static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall); -static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep); -static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip); -static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni); -static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni); -static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, - struct ethaddr *macaddr, struct ipaddr *ip); -static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, - struct ipaddr *ip); -struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp); -static int advertise_gw_macip_enabled(zebra_vni_t *zvni); -static int advertise_svi_macip_enabled(zebra_vni_t *zvni); -static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, - zebra_mac_t *old_zmac, - zebra_mac_t *new_zmac, - zebra_neigh_t *nbr); -static int remote_neigh_count(zebra_mac_t *zmac); -static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac); -static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t); -static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t); -static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, - zebra_neigh_t *nbr, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local); -static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, - zebra_mac_t *mac, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local); +static void zevpn_build_hash_table(void); static unsigned int zebra_vxlan_sg_hash_key_make(const void *p); static bool zebra_vxlan_sg_hash_eq(const void *p1, const void *p2); static void zebra_vxlan_sg_do_deref(struct zebra_vrf *zvrf, @@ -223,9 +117,6 @@ static void zebra_vxlan_sg_ref(struct in_addr local_vtep_ip, struct in_addr mcast_grp); static void zebra_vxlan_sg_cleanup(struct hash_bucket *bucket, void *arg); -static void zvni_send_mac_to_client(zebra_vni_t *zvn); -static void zvni_send_neigh_to_client(zebra_vni_t *zvni); - /* Private functions */ static int host_rb_entry_compare(const struct host_rb_entry *hle1, const struct host_rb_entry *hle2) @@ -273,677 +164,14 @@ static uint32_t rb_host_count(struct host_rb_tree_entry *hrbe) } /* - * Return number of valid MACs in a VNI's MAC hash table - all - * remote MACs and non-internal (auto) local MACs count. - */ -static uint32_t num_valid_macs(zebra_vni_t *zvni) -{ - unsigned int i; - uint32_t num_macs = 0; - struct hash *hash; - struct hash_bucket *hb; - zebra_mac_t *mac; - - hash = zvni->mac_table; - if (!hash) - return num_macs; - for (i = 0; i < hash->size; i++) { - for (hb = hash->index[i]; hb; hb = hb->next) { - mac = (zebra_mac_t *)hb->data; - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) - || CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) - || !CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) - num_macs++; - } - } - - return num_macs; -} - -static uint32_t num_dup_detected_macs(zebra_vni_t *zvni) -{ - unsigned int i; - uint32_t num_macs = 0; - struct hash *hash; - struct hash_bucket *hb; - zebra_mac_t *mac; - - hash = zvni->mac_table; - if (!hash) - return num_macs; - for (i = 0; i < hash->size; i++) { - for (hb = hash->index[i]; hb; hb = hb->next) { - mac = (zebra_mac_t *)hb->data; - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - num_macs++; - } - } - - return num_macs; -} - -static uint32_t num_dup_detected_neighs(zebra_vni_t *zvni) -{ - unsigned int i; - uint32_t num_neighs = 0; - struct hash *hash; - struct hash_bucket *hb; - zebra_neigh_t *nbr; - - hash = zvni->neigh_table; - if (!hash) - return num_neighs; - for (i = 0; i < hash->size; i++) { - for (hb = hash->index[i]; hb; hb = hb->next) { - nbr = (zebra_neigh_t *)hb->data; - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - num_neighs++; - } - } - - return num_neighs; -} - -static int advertise_gw_macip_enabled(zebra_vni_t *zvni) -{ - struct zebra_vrf *zvrf; - - zvrf = zebra_vrf_get_evpn(); - if (zvrf && zvrf->advertise_gw_macip) - return 1; - - if (zvni && zvni->advertise_gw_macip) - return 1; - - return 0; -} - -static int advertise_svi_macip_enabled(zebra_vni_t *zvni) -{ - struct zebra_vrf *zvrf; - - zvrf = zebra_vrf_get_evpn(); - if (zvrf && zvrf->advertise_svi_macip) - return 1; - - if (zvni && zvni->advertise_svi_macip) - return 1; - - return 0; -} - -/* As part Duplicate Address Detection (DAD) for IP mobility - * MAC binding changes, ensure to inherit duplicate flag - * from MAC. + * Print neighbors for all EVPN. */ -static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, - zebra_mac_t *old_zmac, - zebra_mac_t *new_zmac, - zebra_neigh_t *nbr) -{ - bool is_old_mac_dup = false; - bool is_new_mac_dup = false; - - if (!zvrf->dup_addr_detect) - return 0; - /* Check old or new MAC is detected as duplicate - * mark this neigh as duplicate - */ - if (old_zmac) - is_old_mac_dup = CHECK_FLAG(old_zmac->flags, - ZEBRA_MAC_DUPLICATE); - if (new_zmac) - is_new_mac_dup = CHECK_FLAG(new_zmac->flags, - ZEBRA_MAC_DUPLICATE); - /* Old and/or new MAC can be in duplicate state, - * based on that IP/Neigh Inherits the flag. - * If New MAC is marked duplicate, inherit to the IP. - * If old MAC is duplicate but new MAC is not, clear - * duplicate flag for IP and reset detection params - * and let IP DAD retrigger. - */ - if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - /* Capture Duplicate detection time */ - nbr->dad_dup_detect_time = monotime(NULL); - /* Mark neigh inactive */ - ZEBRA_NEIGH_SET_INACTIVE(nbr); - - return 1; - } else if (is_old_mac_dup && !is_new_mac_dup) { - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->detect_start_time.tv_usec = 0; - } - return 0; -} - -static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, - zebra_mac_t *mac, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local) -{ - zebra_neigh_t *nbr; - struct listnode *node = NULL; - struct timeval elapsed = {0, 0}; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - bool reset_params = false; - - if (!(zvrf->dup_addr_detect && do_dad)) - return; - - /* MAC is detected as duplicate, - * Local MAC event -> hold on advertising to BGP. - * Remote MAC event -> hold on installing it. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u", - __func__, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - mac->flags, mac->dad_count, - zvrf->dad_freeze_time); - - /* For duplicate MAC do not update - * client but update neigh due to - * this MAC update. - */ - if (zvrf->dad_freeze) - *is_dup_detect = true; - - return; - } - - /* Check if detection time (M-secs) expired. - * Reset learn count and detection start time. - */ - monotime_since(&mac->detect_start_time, &elapsed); - reset_params = (elapsed.tv_sec > zvrf->dad_time); - if (is_local && !reset_params) { - /* RFC-7432: A PE/VTEP that detects a MAC mobility - * event via LOCAL learning starts an M-second timer. - * - * NOTE: This is the START of the probe with count is - * 0 during LOCAL learn event. - * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time) - */ - reset_params = !mac->dad_count; - } - - if (reset_params) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u", - __func__, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - mac->flags, mac->dad_count); - - mac->dad_count = 0; - /* Start dup. addr detection (DAD) start time, - * ONLY during LOCAL learn. - */ - if (is_local) - monotime(&mac->detect_start_time); - - } else if (!is_local) { - /* For REMOTE MAC, increment detection count - * ONLY while in probe window, once window passed, - * next local learn event should trigger DAD. - */ - mac->dad_count++; - } - - /* For LOCAL MAC learn event, once count is reset above via either - * initial/start detection time or passed the probe time, the count - * needs to be incremented. - */ - if (is_local) - mac->dad_count++; - - if (mac->dad_count >= zvrf->dad_max_moves) { - flog_warn(EC_ZEBRA_DUP_MAC_DETECTED, - "VNI %u: MAC %s detected as duplicate during %s VTEP %s", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - is_local ? "local update, last" : - "remote update, from", inet_ntoa(vtep_ip)); - - SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); - - /* Capture Duplicate detection time */ - mac->dad_dup_detect_time = monotime(NULL); - - /* Mark all IPs/Neighs as duplicate - * associcated with this MAC - */ - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { - - /* Ony Mark IPs which are Local */ - if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) - continue; - - SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - - nbr->dad_dup_detect_time = monotime(NULL); - - flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC", - mac->zvni->vni, - prefix_mac2str(&mac->macaddr, - buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - is_local ? "local" : "remote"); - } - - /* Start auto recovery timer for this MAC */ - THREAD_OFF(mac->dad_mac_auto_recovery_timer); - if (zvrf->dad_freeze && zvrf->dad_freeze_time) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start", - __func__, - prefix_mac2str(&mac->macaddr, buf, - sizeof(buf)), - mac->flags, zvrf->dad_freeze_time); - - thread_add_timer(zrouter.master, - zebra_vxlan_dad_mac_auto_recovery_exp, - mac, zvrf->dad_freeze_time, - &mac->dad_mac_auto_recovery_timer); - } - - /* In case of local update, do not inform to client (BGPd), - * upd_neigh for neigh sequence change. - */ - if (zvrf->dad_freeze) - *is_dup_detect = true; - } -} - -static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, - zebra_neigh_t *nbr, - struct in_addr vtep_ip, - bool do_dad, - bool *is_dup_detect, - bool is_local) -{ - - struct timeval elapsed = {0, 0}; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - bool reset_params = false; - - if (!zvrf->dup_addr_detect) - return; - - /* IP is detected as duplicate or inherit dup - * state, hold on to install as remote entry - * only if freeze is enabled. - */ - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u", - __func__, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - nbr->flags, nbr->dad_count, - zvrf->dad_freeze_time); - - if (zvrf->dad_freeze) - *is_dup_detect = true; - - /* warn-only action, neigh will be installed. - * freeze action, it wil not be installed. - */ - return; - } - - if (!do_dad) - return; - - /* Check if detection time (M-secs) expired. - * Reset learn count and detection start time. - * During remote mac add, count should already be 1 - * via local learning. - */ - monotime_since(&nbr->detect_start_time, &elapsed); - reset_params = (elapsed.tv_sec > zvrf->dad_time); - - if (is_local && !reset_params) { - /* RFC-7432: A PE/VTEP that detects a MAC mobility - * event via LOCAL learning starts an M-second timer. - * - * NOTE: This is the START of the probe with count is - * 0 during LOCAL learn event. - */ - reset_params = !nbr->dad_count; - } - - if (reset_params) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u", - __func__, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - nbr->flags, nbr->dad_count); - /* Reset learn count but do not start detection - * during REMOTE learn event. - */ - nbr->dad_count = 0; - /* Start dup. addr detection (DAD) start time, - * ONLY during LOCAL learn. - */ - if (is_local) - monotime(&nbr->detect_start_time); - - } else if (!is_local) { - /* For REMOTE IP/Neigh, increment detection count - * ONLY while in probe window, once window passed, - * next local learn event should trigger DAD. - */ - nbr->dad_count++; - } - - /* For LOCAL IP/Neigh learn event, once count is reset above via either - * initial/start detection time or passed the probe time, the count - * needs to be incremented. - */ - if (is_local) - nbr->dad_count++; - - if (nbr->dad_count >= zvrf->dad_max_moves) { - flog_warn(EC_ZEBRA_DUP_IP_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s", - nbr->zvni->vni, - prefix_mac2str(&nbr->emac, buf, sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), - is_local ? "local update, last" : - "remote update, from", - inet_ntoa(vtep_ip)); - - SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - - /* Capture Duplicate detection time */ - nbr->dad_dup_detect_time = monotime(NULL); - - /* Start auto recovery timer for this IP */ - THREAD_OFF(nbr->dad_ip_auto_recovery_timer); - if (zvrf->dad_freeze && zvrf->dad_freeze_time) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start", - __func__, - prefix_mac2str(&nbr->emac, buf, - sizeof(buf)), - ipaddr2str(&nbr->ip, buf1, - sizeof(buf1)), - nbr->flags, zvrf->dad_freeze_time); - - thread_add_timer(zrouter.master, - zebra_vxlan_dad_ip_auto_recovery_exp, - nbr, zvrf->dad_freeze_time, - &nbr->dad_ip_auto_recovery_timer); - } - if (zvrf->dad_freeze) - *is_dup_detect = true; - } -} - -/* - * Helper function to determine maximum width of neighbor IP address for - * display - just because we're dealing with IPv6 addresses that can - * widely vary. - */ -static void zvni_find_neigh_addr_width(struct hash_bucket *bucket, void *ctxt) -{ - zebra_neigh_t *n; - char buf[INET6_ADDRSTRLEN]; - struct neigh_walk_ctx *wctx = ctxt; - int width; - - n = (zebra_neigh_t *)bucket->data; - - ipaddr2str(&n->ip, buf, sizeof(buf)); - width = strlen(buf); - if (width > wctx->addr_width) - wctx->addr_width = width; - -} - -/* - * Print a specific neighbor entry. - */ -static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) -{ - struct vty *vty; - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - const char *type_str; - const char *state_str; - bool flags_present = false; - struct zebra_vrf *zvrf = NULL; - struct timeval detect_start_time = {0, 0}; - char timebuf[MONOTIME_STRLEN]; - - zvrf = zebra_vrf_get_evpn(); - if (!zvrf) - return; - - ipaddr2str(&n->ip, buf2, sizeof(buf2)); - prefix_mac2str(&n->emac, buf1, sizeof(buf1)); - type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ? - "local" : "remote"; - state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; - vty = (struct vty *)ctxt; - if (json == NULL) { - vty_out(vty, "IP: %s\n", - ipaddr2str(&n->ip, buf2, sizeof(buf2))); - vty_out(vty, " Type: %s\n", type_str); - vty_out(vty, " State: %s\n", state_str); - vty_out(vty, " MAC: %s\n", - prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - } else { - json_object_string_add(json, "ip", buf2); - json_object_string_add(json, "type", type_str); - json_object_string_add(json, "state", state_str); - json_object_string_add(json, "mac", buf1); - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - if (json == NULL) { - vty_out(vty, " Remote VTEP: %s\n", - inet_ntoa(n->r_vtep_ip)); - } else - json_object_string_add(json, "remoteVtep", - inet_ntoa(n->r_vtep_ip)); - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) { - if (!json) { - vty_out(vty, " Flags: Default-gateway"); - flags_present = true; - } else - json_object_boolean_true_add(json, "defaultGateway"); - } - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG)) { - if (!json) { - vty_out(vty, - flags_present ? " ,Router" : " Flags: Router"); - flags_present = true; - } - } - if (json == NULL) { - if (flags_present) - vty_out(vty, "\n"); - vty_out(vty, " Local Seq: %u Remote Seq: %u\n", - n->loc_seq, n->rem_seq); - - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { - vty_out(vty, " Duplicate, detected at %s", - time_to_string(n->dad_dup_detect_time, - timebuf)); - } else if (n->dad_count) { - monotime_since(&n->detect_start_time, - &detect_start_time); - if (detect_start_time.tv_sec <= zvrf->dad_time) { - time_to_string(n->detect_start_time.tv_sec, - timebuf); - vty_out(vty, - " Duplicate detection started at %s, detection count %u\n", - timebuf, n->dad_count); - } - } - } else { - json_object_int_add(json, "localSequence", n->loc_seq); - json_object_int_add(json, "remoteSequence", n->rem_seq); - json_object_int_add(json, "detectionCount", - n->dad_count); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - json_object_boolean_true_add(json, "isDuplicate"); - else - json_object_boolean_false_add(json, "isDuplicate"); - - - } -} - -/* - * Print neighbor hash entry - called for display of all neighbors. - */ -static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json_vni = NULL, *json_row = NULL; - zebra_neigh_t *n; - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct neigh_walk_ctx *wctx = ctxt; - const char *state_str; - - vty = wctx->vty; - json_vni = wctx->json; - n = (zebra_neigh_t *)bucket->data; - - if (json_vni) - json_row = json_object_new_object(); - - prefix_mac2str(&n->emac, buf1, sizeof(buf1)); - ipaddr2str(&n->ip, buf2, sizeof(buf2)); - state_str = IS_ZEBRA_NEIGH_ACTIVE(n) ? "active" : "inactive"; - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) - return; - - if (json_vni == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s %u/%u\n", - -wctx->addr_width, buf2, "local", - state_str, buf1, n->loc_seq, n->rem_seq); - } else { - json_object_string_add(json_row, "type", "local"); - json_object_string_add(json_row, "state", state_str); - json_object_string_add(json_row, "mac", buf1); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) - json_object_boolean_true_add( - json_row, "defaultGateway"); - json_object_int_add(json_row, "localSequence", - n->loc_seq); - json_object_int_add(json_row, "remoteSequence", - n->rem_seq); - json_object_int_add(json_row, "detectionCount", - n->dad_count); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - json_object_boolean_true_add(json_row, - "isDuplicate"); - else - json_object_boolean_false_add(json_row, - "isDuplicate"); - } - wctx->count++; - } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) && - !IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) - return; - - if (json_vni == NULL) { - if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) && - (wctx->count == 0)) - vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n", - -wctx->addr_width, "Neighbor", "Type", - "State", "MAC", "Remote VTEP", - "Seq #'s"); - vty_out(vty, "%*s %-6s %-8s %-17s %-21s %u/%u\n", - -wctx->addr_width, buf2, "remote", state_str, - buf1, inet_ntoa(n->r_vtep_ip), n->loc_seq, n->rem_seq); - } else { - json_object_string_add(json_row, "type", "remote"); - json_object_string_add(json_row, "state", state_str); - json_object_string_add(json_row, "mac", buf1); - json_object_string_add(json_row, "remoteVtep", - inet_ntoa(n->r_vtep_ip)); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW)) - json_object_boolean_true_add(json_row, - "defaultGateway"); - json_object_int_add(json_row, "localSequence", - n->loc_seq); - json_object_int_add(json_row, "remoteSequence", - n->rem_seq); - json_object_int_add(json_row, "detectionCount", - n->dad_count); - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - json_object_boolean_true_add(json_row, - "isDuplicate"); - else - json_object_boolean_false_add(json_row, - "isDuplicate"); - } - wctx->count++; - } - - if (json_vni) - json_object_object_add(json_vni, buf2, json_row); -} - -/* - * Print neighbor hash entry in detail - called for display of all neighbors. - */ -static void zvni_print_neigh_hash_detail(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json_vni = NULL, *json_row = NULL; - zebra_neigh_t *n; - char buf[INET6_ADDRSTRLEN]; - struct neigh_walk_ctx *wctx = ctxt; - - vty = wctx->vty; - json_vni = wctx->json; - n = (zebra_neigh_t *)bucket->data; - if (!n) - return; - - ipaddr2str(&n->ip, buf, sizeof(buf)); - if (json_vni) - json_row = json_object_new_object(); - - zvni_print_neigh(n, vty, json_row); - - if (json_vni) - json_object_object_add(json_vni, buf, json_row); -} - -/* - * Print neighbors for all VNI. - */ -static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, +static void zevpn_print_neigh_hash_all_evpn(struct hash_bucket *bucket, void **args) { struct vty *vty; - json_object *json = NULL, *json_vni = NULL; - zebra_vni_t *zvni; + json_object *json = NULL, *json_evpn = NULL; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; char vni_str[VNI_STR_LEN]; @@ -953,26 +181,26 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, json = (json_object *)args[1]; print_dup = (uint32_t)(uintptr_t)args[2]; - zvni = (zebra_vni_t *)bucket->data; + zevpn = (zebra_evpn_t *)bucket->data; - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (print_dup) - num_neigh = num_dup_detected_neighs(zvni); + num_neigh = num_dup_detected_neighs(zevpn); if (json == NULL) { vty_out(vty, "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", - zvni->vni, num_neigh); + zevpn->vni, num_neigh); } else { - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "numArpNd", num_neigh); - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "numArpNd", num_neigh); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } if (!num_neigh) { if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); return; } @@ -981,61 +209,36 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, * the maximum width. */ memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; - wctx.json = json_vni; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + wctx.json = json_evpn; + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); + + if (json == NULL) + zebra_evpn_print_neigh_hdr(vty, &wctx); - if (json == NULL) { - vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n", - -wctx.addr_width, "IP", "Type", - "State", "MAC", "Remote VTEP", "Seq #'s"); - } if (print_dup) - hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, - &wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_print_dad_neigh_hash, &wctx); else - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, + &wctx); if (json) - json_object_object_add(json, vni_str, json_vni); -} - -static void zvni_print_dad_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_neigh_t *nbr; - - nbr = (zebra_neigh_t *)bucket->data; - if (!nbr) - return; - - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - zvni_print_neigh_hash(bucket, ctxt); -} - -static void zvni_print_dad_neigh_hash_detail(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_neigh_t *nbr; - - nbr = (zebra_neigh_t *)bucket->data; - if (!nbr) - return; - - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - zvni_print_neigh_hash_detail(bucket, ctxt); + json_object_object_add(json, vni_str, json_evpn); } /* - * Print neighbors for all VNIs in detail. + * Print neighbors for all EVPNs in detail. */ -static void zvni_print_neigh_hash_all_vni_detail(struct hash_bucket *bucket, +static void zevpn_print_neigh_hash_all_evpn_detail(struct hash_bucket *bucket, void **args) { struct vty *vty; - json_object *json = NULL, *json_vni = NULL; - zebra_vni_t *zvni; + json_object *json = NULL, *json_evpn = NULL; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; char vni_str[VNI_STR_LEN]; @@ -1045,47 +248,47 @@ static void zvni_print_neigh_hash_all_vni_detail(struct hash_bucket *bucket, json = (json_object *)args[1]; print_dup = (uint32_t)(uintptr_t)args[2]; - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) { + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) { if (json) vty_out(vty, "{}\n"); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); - if (print_dup && num_dup_detected_neighs(zvni) == 0) + if (print_dup && num_dup_detected_neighs(zevpn) == 0) return; if (json == NULL) { vty_out(vty, "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", - zvni->vni, num_neigh); + zevpn->vni, num_neigh); } else { - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "numArpNd", num_neigh); - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "numArpNd", num_neigh); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } if (!num_neigh) { if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); return; } memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; - wctx.json = json_vni; + wctx.json = json_evpn; if (print_dup) - hash_iterate(zvni->neigh_table, - zvni_print_dad_neigh_hash_detail, &wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_print_dad_neigh_hash_detail, &wctx); else - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash_detail, - &wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_print_neigh_hash_detail, &wctx); if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); } /* print a specific next hop for an l3vni */ @@ -1165,352 +368,14 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, } /* - * Print a specific MAC entry. - */ -static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) -{ - struct vty *vty; - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct zebra_vrf *zvrf; - struct timeval detect_start_time = {0, 0}; - char timebuf[MONOTIME_STRLEN]; - - zvrf = zebra_vrf_get_evpn(); - if (!zvrf) - return; - - vty = (struct vty *)ctxt; - prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); - - if (json) { - json_object *json_mac = json_object_new_object(); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct zebra_ns *zns; - struct interface *ifp; - ifindex_t ifindex; - - ifindex = mac->fwd_info.local.ifindex; - zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); - ifp = if_lookup_by_index_per_ns(zns, ifindex); - if (!ifp) - return; - json_object_string_add(json_mac, "type", "local"); - json_object_string_add(json_mac, "intf", ifp->name); - json_object_int_add(json_mac, "ifindex", ifindex); - if (mac->fwd_info.local.vid) - json_object_int_add(json_mac, "vlan", - mac->fwd_info.local.vid); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - json_object_string_add(json_mac, "type", "remote"); - json_object_string_add( - json_mac, "remoteVtep", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) - json_object_string_add(json_mac, "type", "auto"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) - json_object_boolean_true_add(json_mac, "stickyMac"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) - json_object_boolean_true_add(json_mac, - "defaultGateway"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) - json_object_boolean_true_add(json_mac, - "remoteGatewayMac"); - - json_object_int_add(json_mac, "localSequence", mac->loc_seq); - json_object_int_add(json_mac, "remoteSequence", mac->rem_seq); - - json_object_int_add(json_mac, "detectionCount", mac->dad_count); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - json_object_boolean_true_add(json_mac, "isDuplicate"); - else - json_object_boolean_false_add(json_mac, "isDuplicate"); - - /* print all the associated neigh */ - if (!listcount(mac->neigh_list)) - json_object_string_add(json_mac, "neighbors", "none"); - else { - json_object *json_active_nbrs = json_object_new_array(); - json_object *json_inactive_nbrs = - json_object_new_array(); - json_object *json_nbrs = json_object_new_object(); - - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { - if (IS_ZEBRA_NEIGH_ACTIVE(n)) - json_object_array_add( - json_active_nbrs, - json_object_new_string( - ipaddr2str( - &n->ip, buf2, - sizeof(buf2)))); - else - json_object_array_add( - json_inactive_nbrs, - json_object_new_string( - ipaddr2str( - &n->ip, buf2, - sizeof(buf2)))); - } - - json_object_object_add(json_nbrs, "active", - json_active_nbrs); - json_object_object_add(json_nbrs, "inactive", - json_inactive_nbrs); - json_object_object_add(json_mac, "neighbors", - json_nbrs); - } - - json_object_object_add(json, buf1, json_mac); - } else { - vty_out(vty, "MAC: %s\n", buf1); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct zebra_ns *zns; - struct interface *ifp; - ifindex_t ifindex; - - ifindex = mac->fwd_info.local.ifindex; - zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); - ifp = if_lookup_by_index_per_ns(zns, ifindex); - if (!ifp) - return; - vty_out(vty, " Intf: %s(%u)", ifp->name, ifindex); - if (mac->fwd_info.local.vid) - vty_out(vty, " VLAN: %u", - mac->fwd_info.local.vid); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - vty_out(vty, " Remote VTEP: %s", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { - vty_out(vty, " Auto Mac "); - } - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) - vty_out(vty, " Sticky Mac "); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) - vty_out(vty, " Default-gateway Mac "); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW)) - vty_out(vty, " Remote-gateway Mac "); - - vty_out(vty, "\n"); - vty_out(vty, " Local Seq: %u Remote Seq: %u", mac->loc_seq, - mac->rem_seq); - vty_out(vty, "\n"); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { - vty_out(vty, " Duplicate, detected at %s", - time_to_string(mac->dad_dup_detect_time, - timebuf)); - } else if (mac->dad_count) { - monotime_since(&mac->detect_start_time, - &detect_start_time); - if (detect_start_time.tv_sec <= zvrf->dad_time) { - time_to_string(mac->detect_start_time.tv_sec, - timebuf); - vty_out(vty, - " Duplicate detection started at %s, detection count %u\n", - timebuf, mac->dad_count); - } - } - - /* print all the associated neigh */ - vty_out(vty, " Neighbors:\n"); - if (!listcount(mac->neigh_list)) - vty_out(vty, " No Neighbors\n"); - else { - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) { - vty_out(vty, " %s %s\n", - ipaddr2str(&n->ip, buf2, sizeof(buf2)), - (IS_ZEBRA_NEIGH_ACTIVE(n) - ? "Active" - : "Inactive")); - } - } - - vty_out(vty, "\n"); - } -} - -/* - * Print MAC hash entry - called for display of all MACs. + * Print MACs for all EVPNs. */ -static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt) +static void zevpn_print_mac_hash_all_evpn(struct hash_bucket *bucket, void *ctxt) { struct vty *vty; - json_object *json_mac_hdr = NULL, *json_mac = NULL; - zebra_mac_t *mac; - char buf1[ETHER_ADDR_STRLEN]; - struct mac_walk_ctx *wctx = ctxt; - - vty = wctx->vty; - json_mac_hdr = wctx->json; - mac = (zebra_mac_t *)bucket->data; - - prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); - - if (json_mac_hdr) - json_mac = json_object_new_object(); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - struct zebra_ns *zns; - ifindex_t ifindex; - struct interface *ifp; - vlanid_t vid; - - if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) - return; - - zns = zebra_ns_lookup(mac->fwd_info.local.ns_id); - ifindex = mac->fwd_info.local.ifindex; - ifp = if_lookup_by_index_per_ns(zns, ifindex); - if (!ifp) // unexpected - return; - vid = mac->fwd_info.local.vid; - if (json_mac_hdr == NULL) - vty_out(vty, "%-17s %-6s %-21s", buf1, "local", - ifp->name); - else { - json_object_string_add(json_mac, "type", "local"); - json_object_string_add(json_mac, "intf", ifp->name); - } - if (vid) { - if (json_mac_hdr == NULL) - vty_out(vty, " %-5u", vid); - else - json_object_int_add(json_mac, "vlan", vid); - } else /* No vid? fill out the space */ - if (json_mac_hdr == NULL) - vty_out(vty, " %-5s", ""); - if (json_mac_hdr == NULL) { - vty_out(vty, " %u/%u", mac->loc_seq, mac->rem_seq); - vty_out(vty, "\n"); - } else { - json_object_int_add(json_mac, "localSequence", - mac->loc_seq); - json_object_int_add(json_mac, "remoteSequence", - mac->rem_seq); - json_object_int_add(json_mac, "detectionCount", - mac->dad_count); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - json_object_boolean_true_add(json_mac, - "isDuplicate"); - else - json_object_boolean_false_add(json_mac, - "isDuplicate"); - json_object_object_add(json_mac_hdr, buf1, json_mac); - } - - wctx->count++; - - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - - if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) && - !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, - &wctx->r_vtep_ip)) - return; - - if (json_mac_hdr == NULL) { - if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) && - (wctx->count == 0)) { - vty_out(vty, "\nVNI %u\n\n", wctx->zvni->vni); - vty_out(vty, "%-17s %-6s %-21s %-5s %s\n", - "MAC", "Type", "Intf/Remote VTEP", - "VLAN", "Seq #'s"); - } - vty_out(vty, "%-17s %-6s %-21s %-5s %u/%u\n", buf1, - "remote", inet_ntoa(mac->fwd_info.r_vtep_ip), - "", mac->loc_seq, mac->rem_seq); - } else { - json_object_string_add(json_mac, "type", "remote"); - json_object_string_add(json_mac, "remoteVtep", - inet_ntoa(mac->fwd_info.r_vtep_ip)); - json_object_object_add(json_mac_hdr, buf1, json_mac); - json_object_int_add(json_mac, "localSequence", - mac->loc_seq); - json_object_int_add(json_mac, "remoteSequence", - mac->rem_seq); - json_object_int_add(json_mac, "detectionCount", - mac->dad_count); - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - json_object_boolean_true_add(json_mac, - "isDuplicate"); - else - json_object_boolean_false_add(json_mac, - "isDuplicate"); - - } - - wctx->count++; - } -} - -/* Print Duplicate MAC */ -static void zvni_print_dad_mac_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_mac_t *mac; - - mac = (zebra_mac_t *)bucket->data; - if (!mac) - return; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - zvni_print_mac_hash(bucket, ctxt); -} - -/* - * Print MAC hash entry in detail - called for display of all MACs. - */ -static void zvni_print_mac_hash_detail(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json_mac_hdr = NULL; - zebra_mac_t *mac; - struct mac_walk_ctx *wctx = ctxt; - char buf1[ETHER_ADDR_STRLEN]; - - vty = wctx->vty; - json_mac_hdr = wctx->json; - mac = (zebra_mac_t *)bucket->data; - if (!mac) - return; - - wctx->count++; - prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); - - zvni_print_mac(mac, vty, json_mac_hdr); -} - -/* Print Duplicate MAC in detail */ -static void zvni_print_dad_mac_hash_detail(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_mac_t *mac; - - mac = (zebra_mac_t *)bucket->data; - if (!mac) - return; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - zvni_print_mac_hash_detail(bucket, ctxt); -} - -/* - * Print MACs for all VNI. - */ -static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt) -{ - struct vty *vty; - json_object *json = NULL, *json_vni = NULL; + json_object *json = NULL, *json_evpn = NULL; json_object *json_mac = NULL; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx *wctx = ctxt; char vni_str[VNI_STR_LEN]; @@ -1518,70 +383,74 @@ static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt) vty = wctx->vty; json = wctx->json; - zvni = (zebra_vni_t *)bucket->data; - wctx->zvni = zvni; + zevpn = (zebra_evpn_t *)bucket->data; + wctx->zevpn = zevpn; /*We are iterating over a new VNI, set the count to 0*/ wctx->count = 0; - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; if (wctx->print_dup) - num_macs = num_dup_detected_macs(zvni); + num_macs = num_dup_detected_macs(zevpn); if (json) { - json_vni = json_object_new_object(); + json_evpn = json_object_new_object(); json_mac = json_object_new_object(); - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { if (json == NULL) { vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", - zvni->vni, num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s %s\n", "MAC", - "Type", "Intf/Remote VTEP", "VLAN", "Seq #'s"); + zevpn->vni, num_macs); + vty_out(vty, + "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC", + "Type", "Flags", "Intf/Remote ES/VTEP", + "VLAN", "Seq #'s"); } else - json_object_int_add(json_vni, "numMacs", num_macs); + json_object_int_add(json_evpn, "numMacs", num_macs); } if (!num_macs) { if (json) { - json_object_int_add(json_vni, "numMacs", num_macs); - json_object_object_add(json, vni_str, json_vni); + json_object_int_add(json_evpn, "numMacs", num_macs); + json_object_object_add(json, vni_str, json_evpn); } return; } - /* assign per-vni to wctx->json object to fill macs - * under the vni. Re-assign primary json object to fill - * next vni information. + /* assign per-evpn to wctx->json object to fill macs + * under the evpn. Re-assign primary json object to fill + * next evpn information. */ wctx->json = json_mac; if (wctx->print_dup) - hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_dad_mac_hash, + wctx); else - hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, wctx); wctx->json = json; if (json) { if (wctx->count) - json_object_object_add(json_vni, "macs", json_mac); - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json_evpn, "macs", json_mac); + json_object_object_add(json, vni_str, json_evpn); } } /* - * Print MACs in detail for all VNI. + * Print MACs in detail for all EVPNs. */ -static void zvni_print_mac_hash_all_vni_detail(struct hash_bucket *bucket, +static void zevpn_print_mac_hash_all_evpn_detail(struct hash_bucket *bucket, void *ctxt) { struct vty *vty; - json_object *json = NULL, *json_vni = NULL; + json_object *json = NULL, *json_evpn = NULL; json_object *json_mac = NULL; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx *wctx = ctxt; char vni_str[VNI_STR_LEN]; @@ -1589,52 +458,53 @@ static void zvni_print_mac_hash_all_vni_detail(struct hash_bucket *bucket, vty = wctx->vty; json = wctx->json; - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) { + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) { if (json) vty_out(vty, "{}\n"); return; } - wctx->zvni = zvni; + wctx->zevpn = zevpn; - /*We are iterating over a new VNI, set the count to 0*/ + /*We are iterating over a new EVPN, set the count to 0*/ wctx->count = 0; - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; - if (wctx->print_dup && (num_dup_detected_macs(zvni) == 0)) + if (wctx->print_dup && (num_dup_detected_macs(zevpn) == 0)) return; if (json) { - json_vni = json_object_new_object(); + json_evpn = json_object_new_object(); json_mac = json_object_new_object(); - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); + snprintf(vni_str, VNI_STR_LEN, "%u", zevpn->vni); } if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { if (json == NULL) { vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", - zvni->vni, num_macs); + zevpn->vni, num_macs); } else - json_object_int_add(json_vni, "numMacs", num_macs); + json_object_int_add(json_evpn, "numMacs", num_macs); } - /* assign per-vni to wctx->json object to fill macs - * under the vni. Re-assign primary json object to fill - * next vni information. + /* assign per-evpn to wctx->json object to fill macs + * under the evpn. Re-assign primary json object to fill + * next evpn information. */ wctx->json = json_mac; if (wctx->print_dup) - hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash_detail, - wctx); + hash_iterate(zevpn->mac_table, + zebra_evpn_print_dad_mac_hash_detail, wctx); else - hash_iterate(zvni->mac_table, zvni_print_mac_hash_detail, wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash_detail, + wctx); wctx->json = json; if (json) { if (wctx->count) - json_object_object_add(json_vni, "macs", json_mac); - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json_evpn, "macs", json_mac); + json_object_object_add(json, vni_str, json_evpn); } } @@ -1642,7 +512,7 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx) { struct nh_walk_ctx *wctx = NULL; struct vty *vty = NULL; - struct json_object *json_vni = NULL; + struct json_object *json_evpn = NULL; struct json_object *json_nh = NULL; zebra_neigh_t *n = NULL; char buf1[ETHER_ADDR_STRLEN]; @@ -1650,12 +520,12 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx) wctx = (struct nh_walk_ctx *)ctx; vty = wctx->vty; - json_vni = wctx->json; - if (json_vni) + json_evpn = wctx->json; + if (json_evpn) json_nh = json_object_new_object(); n = (zebra_neigh_t *)bucket->data; - if (!json_vni) { + if (!json_evpn) { vty_out(vty, "%-15s %-17s\n", ipaddr2str(&(n->ip), buf2, sizeof(buf2)), prefix_mac2str(&n->emac, buf1, sizeof(buf1))); @@ -1665,7 +535,7 @@ static void zl3vni_print_nh_hash(struct hash_bucket *bucket, void *ctx) json_object_string_add( json_nh, "routerMac", prefix_mac2str(&n->emac, buf1, sizeof(buf1))); - json_object_object_add(json_vni, + json_object_object_add(json_evpn, ipaddr2str(&(n->ip), buf2, sizeof(buf2)), json_nh); } @@ -1676,7 +546,7 @@ static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket, { struct vty *vty = NULL; json_object *json = NULL; - json_object *json_vni = NULL; + json_object *json_evpn = NULL; zebra_l3vni_t *zl3vni = NULL; uint32_t num_nh = 0; struct nh_walk_ctx wctx; @@ -1692,22 +562,22 @@ static void zl3vni_print_nh_hash_all_vni(struct hash_bucket *bucket, return; if (json) { - json_vni = json_object_new_object(); - snprintf(vni_str, sizeof(vni_str), "%u", zl3vni->vni); + json_evpn = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); } if (json == NULL) { vty_out(vty, "\nVNI %u #Next-Hops %u\n\n", zl3vni->vni, num_nh); vty_out(vty, "%-15s %-17s\n", "IP", "RMAC"); } else - json_object_int_add(json_vni, "numNextHops", num_nh); + json_object_int_add(json_evpn, "numNextHops", num_nh); memset(&wctx, 0, sizeof(struct nh_walk_ctx)); wctx.vty = vty; - wctx.json = json_vni; + wctx.json = json_evpn; hash_iterate(zl3vni->nh_table, zl3vni_print_nh_hash, &wctx); if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); } static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, @@ -1715,7 +585,7 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, { struct vty *vty = NULL; json_object *json = NULL; - json_object *json_vni = NULL; + json_object *json_evpn = NULL; zebra_l3vni_t *zl3vni = NULL; uint32_t num_rmacs; struct rmac_walk_ctx wctx; @@ -1731,15 +601,15 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, return; if (json) { - json_vni = json_object_new_object(); - snprintf(vni_str, sizeof(vni_str), "%u", zl3vni->vni); + json_evpn = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); } if (json == NULL) { vty_out(vty, "\nVNI %u #RMACs %u\n\n", zl3vni->vni, num_rmacs); vty_out(vty, "%-17s %-21s\n", "RMAC", "Remote VTEP"); } else - json_object_int_add(json_vni, "numRmacs", num_rmacs); + json_object_int_add(json_evpn, "numRmacs", num_rmacs); /* assign per-vni to wctx->json object to fill macs * under the vni. Re-assign primary json object to fill @@ -1747,10 +617,10 @@ static void zl3vni_print_rmac_hash_all_vni(struct hash_bucket *bucket, */ memset(&wctx, 0, sizeof(struct rmac_walk_ctx)); wctx.vty = vty; - wctx.json = json_vni; + wctx.json = json_evpn; hash_iterate(zl3vni->rmac_table, zl3vni_print_rmac_hash, &wctx); if (json) - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); } static void zl3vni_print_rmac_hash(struct hash_bucket *bucket, void *ctx) @@ -1791,8 +661,8 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) char buf[ETHER_ADDR_STRLEN]; struct vty *vty = NULL; json_object *json = NULL; - zebra_vni_t *zvni = NULL; - json_object *json_vni_list = NULL; + zebra_evpn_t *zevpn = NULL; + json_object *json_evpn_list = NULL; struct listnode *node = NULL, *nnode = NULL; vty = ctx[0]; @@ -1817,11 +687,11 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) vty_out(vty, " Router MAC: %s\n", zl3vni_rmac2str(zl3vni, buf, sizeof(buf))); vty_out(vty, " L2 VNIs: "); - for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) - vty_out(vty, "%u ", zvni->vni); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zevpn)) + vty_out(vty, "%u ", zevpn->vni); vty_out(vty, "\n"); } else { - json_vni_list = json_object_new_array(); + json_evpn_list = json_object_new_array(); json_object_int_add(json, "vni", zl3vni->vni); json_object_string_add(json, "type", "L3"); json_object_string_add(json, "localVtepIp", @@ -1843,106 +713,11 @@ static void zl3vni_print(zebra_l3vni_t *zl3vni, void **ctx) CHECK_FLAG(zl3vni->filter, PREFIX_ROUTES_ONLY) ? "prefix-routes-only" : "none"); - for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zvni)) { - json_object_array_add(json_vni_list, - json_object_new_int(zvni->vni)); + for (ALL_LIST_ELEMENTS(zl3vni->l2vnis, node, nnode, zevpn)) { + json_object_array_add(json_evpn_list, + json_object_new_int(zevpn->vni)); } - json_object_object_add(json, "l2Vnis", json_vni_list); - } -} - -/* - * Print a specific VNI entry. - */ -static void zvni_print(zebra_vni_t *zvni, void **ctxt) -{ - struct vty *vty; - zebra_vtep_t *zvtep; - uint32_t num_macs; - uint32_t num_neigh; - json_object *json = NULL; - json_object *json_vtep_list = NULL; - json_object *json_ip_str = NULL; - - vty = ctxt[0]; - json = ctxt[1]; - - if (json == NULL) { - vty_out(vty, "VNI: %u\n", zvni->vni); - vty_out(vty, " Type: %s\n", "L2"); - vty_out(vty, " Tenant VRF: %s\n", vrf_id_to_name(zvni->vrf_id)); - } else { - json_object_int_add(json, "vni", zvni->vni); - json_object_string_add(json, "type", "L2"); - json_object_string_add(json, "vrf", - vrf_id_to_name(zvni->vrf_id)); - } - - if (!zvni->vxlan_if) { // unexpected - if (json == NULL) - vty_out(vty, " VxLAN interface: unknown\n"); - return; - } - num_macs = num_valid_macs(zvni); - num_neigh = hashcount(zvni->neigh_table); - if (json == NULL) { - vty_out(vty, " VxLAN interface: %s\n", zvni->vxlan_if->name); - vty_out(vty, " VxLAN ifIndex: %u\n", zvni->vxlan_if->ifindex); - vty_out(vty, " Local VTEP IP: %s\n", - inet_ntoa(zvni->local_vtep_ip)); - vty_out(vty, " Mcast group: %s\n", - inet_ntoa(zvni->mcast_grp)); - } else { - json_object_string_add(json, "vxlanInterface", - zvni->vxlan_if->name); - json_object_int_add(json, "ifindex", zvni->vxlan_if->ifindex); - json_object_string_add(json, "vtepIp", - inet_ntoa(zvni->local_vtep_ip)); - json_object_string_add(json, "mcastGroup", - inet_ntoa(zvni->mcast_grp)); - json_object_string_add(json, "advertiseGatewayMacip", - zvni->advertise_gw_macip ? "Yes" : "No"); - json_object_int_add(json, "numMacs", num_macs); - json_object_int_add(json, "numArpNd", num_neigh); - } - if (!zvni->vteps) { - if (json == NULL) - vty_out(vty, " No remote VTEPs known for this VNI\n"); - } else { - if (json == NULL) - vty_out(vty, " Remote VTEPs for this VNI:\n"); - else - json_vtep_list = json_object_new_array(); - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - const char *flood_str = lookup_msg(zvtep_flood_str, - zvtep->flood_control, - VXLAN_FLOOD_STR_DEFAULT); - - if (json == NULL) { - vty_out(vty, " %s flood: %s\n", - inet_ntoa(zvtep->vtep_ip), - flood_str); - } else { - json_ip_str = json_object_new_string( - inet_ntoa(zvtep->vtep_ip)); - json_object_array_add(json_vtep_list, - json_ip_str); - } - } - if (json) - json_object_object_add(json, "numRemoteVteps", - json_vtep_list); - } - if (json == NULL) { - vty_out(vty, - " Number of MACs (local and remote) known for this VNI: %u\n", - num_macs); - vty_out(vty, - " Number of ARPs (IPv4 and IPv6, local and remote) " - "known for this VNI: %u\n", - num_neigh); - vty_out(vty, " Advertise-gw-macip: %s\n", - zvni->advertise_gw_macip ? "Yes" : "No"); + json_object_object_add(json, "l2Vnis", json_evpn_list); } } @@ -1951,7 +726,7 @@ static void zl3vni_print_hash(struct hash_bucket *bucket, void *ctx[]) { struct vty *vty = NULL; json_object *json = NULL; - json_object *json_vni = NULL; + json_object *json_evpn = NULL; zebra_l3vni_t *zl3vni = NULL; vty = (struct vty *)ctx[0]; @@ -1968,31 +743,23 @@ static void zl3vni_print_hash(struct hash_bucket *bucket, void *ctx[]) } else { char vni_str[VNI_STR_LEN]; - snprintf(vni_str, sizeof(vni_str), "%u", zl3vni->vni); - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "vni", zl3vni->vni); - json_object_string_add(json_vni, "vxlanIf", + snprintf(vni_str, VNI_STR_LEN, "%u", zl3vni->vni); + json_evpn = json_object_new_object(); + json_object_int_add(json_evpn, "vni", zl3vni->vni); + json_object_string_add(json_evpn, "vxlanIf", zl3vni_vxlan_if_name(zl3vni)); - json_object_int_add(json_vni, "numMacs", + json_object_int_add(json_evpn, "numMacs", hashcount(zl3vni->rmac_table)); - json_object_int_add(json_vni, "numArpNd", + json_object_int_add(json_evpn, "numArpNd", hashcount(zl3vni->nh_table)); - json_object_string_add(json_vni, "numRemoteVteps", "n/a"); - json_object_string_add(json_vni, "type", "L3"); - json_object_string_add(json_vni, "tenantVrf", + json_object_string_add(json_evpn, "numRemoteVteps", "n/a"); + json_object_string_add(json_evpn, "type", "L3"); + json_object_string_add(json_evpn, "tenantVrf", zl3vni_vrf_name(zl3vni)); - json_object_object_add(json, vni_str, json_vni); + json_object_object_add(json, vni_str, json_evpn); } } -/* Private Structure to pass callback data for hash iterator */ -struct zvni_evpn_show { - struct vty *vty; - json_object *json; - struct zebra_vrf *zvrf; - bool use_json; -}; - /* print a L3 VNI hash entry in detail*/ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) { @@ -2000,7 +767,7 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) zebra_l3vni_t *zl3vni = NULL; json_object *json_array = NULL; bool use_json = false; - struct zvni_evpn_show *zes = data; + struct zebra_evpn_show *zes = data; vty = zes->vty; json_array = zes->json; @@ -2015,1749 +782,6 @@ static void zl3vni_print_hash_detail(struct hash_bucket *bucket, void *data) vty_out(vty, "\n"); } - -/* - * Print a VNI hash entry - called for display of all VNIs. - */ -static void zvni_print_hash(struct hash_bucket *bucket, void *ctxt[]) -{ - struct vty *vty; - zebra_vni_t *zvni; - zebra_vtep_t *zvtep; - uint32_t num_vteps = 0; - uint32_t num_macs = 0; - uint32_t num_neigh = 0; - json_object *json = NULL; - json_object *json_vni = NULL; - json_object *json_ip_str = NULL; - json_object *json_vtep_list = NULL; - - vty = ctxt[0]; - json = ctxt[1]; - - zvni = (zebra_vni_t *)bucket->data; - - zvtep = zvni->vteps; - while (zvtep) { - num_vteps++; - zvtep = zvtep->next; - } - - num_macs = num_valid_macs(zvni); - num_neigh = hashcount(zvni->neigh_table); - if (json == NULL) - vty_out(vty, "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n", - zvni->vni, "L2", - zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", - num_macs, num_neigh, num_vteps, - vrf_id_to_name(zvni->vrf_id)); - else { - char vni_str[VNI_STR_LEN]; - snprintf(vni_str, sizeof(vni_str), "%u", zvni->vni); - json_vni = json_object_new_object(); - json_object_int_add(json_vni, "vni", zvni->vni); - json_object_string_add(json_vni, "type", "L2"); - json_object_string_add(json_vni, "vxlanIf", - zvni->vxlan_if ? zvni->vxlan_if->name - : "unknown"); - json_object_int_add(json_vni, "numMacs", num_macs); - json_object_int_add(json_vni, "numArpNd", num_neigh); - json_object_int_add(json_vni, "numRemoteVteps", num_vteps); - json_object_string_add(json_vni, "tenantVrf", - vrf_id_to_name(zvni->vrf_id)); - if (num_vteps) { - json_vtep_list = json_object_new_array(); - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - json_ip_str = json_object_new_string( - inet_ntoa(zvtep->vtep_ip)); - json_object_array_add(json_vtep_list, - json_ip_str); - } - json_object_object_add(json_vni, "remoteVteps", - json_vtep_list); - } - json_object_object_add(json, vni_str, json_vni); - } -} - -/* - * Print a VNI hash entry in detail - called for display of all VNIs. - */ -static void zvni_print_hash_detail(struct hash_bucket *bucket, void *data) -{ - struct vty *vty; - zebra_vni_t *zvni; - json_object *json_array = NULL; - bool use_json = false; - struct zvni_evpn_show *zes = data; - - vty = zes->vty; - json_array = zes->json; - use_json = zes->use_json; - - zvni = (zebra_vni_t *)bucket->data; - - zebra_vxlan_print_vni(vty, zes->zvrf, zvni->vni, use_json, json_array); - - if (!use_json) - vty_out(vty, "\n"); -} - -/* - * Inform BGP about local MACIP. - */ -static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, - struct ipaddr *ip, uint8_t flags, - uint32_t seq, int state, uint16_t cmd) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - int ipa_len; - struct zserv *client = NULL; - struct stream *s = NULL; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, cmd, zebra_vrf_get_evpn_id()); - stream_putl(s, vni); - stream_put(s, macaddr->octet, ETH_ALEN); - if (ip) { - ipa_len = 0; - if (IS_IPADDR_V4(ip)) - ipa_len = IPV4_MAX_BYTELEN; - else if (IS_IPADDR_V6(ip)) - ipa_len = IPV6_MAX_BYTELEN; - - stream_putl(s, ipa_len); /* IP address length */ - if (ipa_len) - stream_put(s, &ip->ip.addr, ipa_len); /* IP address */ - } else - stream_putl(s, 0); /* Just MAC. */ - - if (cmd == ZEBRA_MACIP_ADD) { - stream_putc(s, flags); /* sticky mac/gateway mac */ - stream_putl(s, seq); /* sequence number */ - } else { - stream_putl(s, state); /* state - active/inactive */ - } - - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Send MACIP %s flags 0x%x MAC %s IP %s seq %u L2-VNI %u to %s", - (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), seq, vni, - zebra_route_string(client->proto)); - - if (cmd == ZEBRA_MACIP_ADD) - client->macipadd_cnt++; - else - client->macipdel_cnt++; - - return zserv_send_message(client, s); -} - -/* - * Make hash key for neighbors. - */ -static unsigned int neigh_hash_keymake(const void *p) -{ - const zebra_neigh_t *n = p; - const struct ipaddr *ip = &n->ip; - - if (IS_IPADDR_V4(ip)) - return jhash_1word(ip->ipaddr_v4.s_addr, 0); - - return jhash2(ip->ipaddr_v6.s6_addr32, - array_size(ip->ipaddr_v6.s6_addr32), 0); -} - -/* - * Compare two neighbor hash structures. - */ -static bool neigh_cmp(const void *p1, const void *p2) -{ - const zebra_neigh_t *n1 = p1; - const zebra_neigh_t *n2 = p2; - - if (n1 == NULL && n2 == NULL) - return true; - - if (n1 == NULL || n2 == NULL) - return false; - - return (memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)) == 0); -} - -static int neigh_list_cmp(void *p1, void *p2) -{ - const zebra_neigh_t *n1 = p1; - const zebra_neigh_t *n2 = p2; - - return memcmp(&n1->ip, &n2->ip, sizeof(struct ipaddr)); -} - -/* - * Callback to allocate neighbor hash entry. - */ -static void *zvni_neigh_alloc(void *p) -{ - const zebra_neigh_t *tmp_n = p; - zebra_neigh_t *n; - - n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); - *n = *tmp_n; - - return ((void *)n); -} - -/* - * Add neighbor entry. - */ -static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, - struct ethaddr *mac) -{ - zebra_neigh_t tmp_n; - zebra_neigh_t *n = NULL; - zebra_mac_t *zmac = NULL; - - memset(&tmp_n, 0, sizeof(zebra_neigh_t)); - memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr)); - n = hash_get(zvni->neigh_table, &tmp_n, zvni_neigh_alloc); - assert(n); - - memcpy(&n->emac, mac, ETH_ALEN); - n->state = ZEBRA_NEIGH_INACTIVE; - n->zvni = zvni; - n->dad_ip_auto_recovery_timer = NULL; - - /* Associate the neigh to mac */ - zmac = zvni_mac_lookup(zvni, mac); - if (zmac) - listnode_add_sort(zmac->neigh_list, n); - - return n; -} - -/* - * Delete neighbor entry. - */ -static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - zebra_neigh_t *tmp_n; - zebra_mac_t *zmac = NULL; - - zmac = zvni_mac_lookup(zvni, &n->emac); - if (zmac) - listnode_delete(zmac->neigh_list, n); - - /* Cancel auto recovery */ - THREAD_OFF(n->dad_ip_auto_recovery_timer); - - /* Free the VNI hash entry and allocated memory. */ - tmp_n = hash_release(zvni->neigh_table, n); - XFREE(MTYPE_NEIGH, tmp_n); - - return 0; -} - -/* - * Free neighbor hash entry (callback) - */ -static void zvni_neigh_del_hash_entry(struct hash_bucket *bucket, void *arg) -{ - struct neigh_walk_ctx *wctx = arg; - zebra_neigh_t *n = bucket->data; - - if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) - || ((wctx->flags & DEL_REMOTE_NEIGH) - && (n->flags & ZEBRA_NEIGH_REMOTE)) - || ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) - && (n->flags & ZEBRA_NEIGH_REMOTE) - && IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) { - if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL)) - zvni_neigh_send_del_to_client(wctx->zvni->vni, &n->ip, - &n->emac, 0, n->state); - - if (wctx->uninstall) - zvni_neigh_uninstall(wctx->zvni, n); - - zvni_neigh_del(wctx->zvni, n); - } - - return; -} - -/* - * Delete all neighbor entries for this VNI. - */ -static void zvni_neigh_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags) -{ - struct neigh_walk_ctx wctx; - - if (!zvni->neigh_table) - return; - - memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; - wctx.uninstall = uninstall; - wctx.upd_client = upd_client; - wctx.flags = flags; - - hash_iterate(zvni->neigh_table, zvni_neigh_del_hash_entry, &wctx); -} - -/* - * Look up neighbor hash entry. - */ -static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip) -{ - zebra_neigh_t tmp; - zebra_neigh_t *n; - - memset(&tmp, 0, sizeof(tmp)); - memcpy(&tmp.ip, ip, sizeof(struct ipaddr)); - n = hash_lookup(zvni->neigh_table, &tmp); - - return n; -} - -/* - * Process all neighbors associated with a MAC upon the MAC being learnt - * locally or undergoing any other change (such as sequence number). - */ -static void zvni_process_neigh_on_local_mac_change(zebra_vni_t *zvni, - zebra_mac_t *zmac, - bool seq_change) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - struct zebra_vrf *zvrf = NULL; - char buf[ETHER_ADDR_STRLEN]; - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Processing neighbors on local MAC %s %s, VNI %u", - prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), - seq_change ? "CHANGE" : "ADD", zvni->vni); - - /* Walk all neighbors and mark any inactive local neighbors as - * active and/or update sequence number upon a move, and inform BGP. - * The action for remote neighbors is TBD. - * NOTE: We can't simply uninstall remote neighbors as the kernel may - * accidentally end up deleting a just-learnt local neighbor. - */ - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change) { - ZEBRA_NEIGH_SET_ACTIVE(n); - n->loc_seq = zmac->loc_seq; - if (!(zvrf->dup_addr_detect && - zvrf->dad_freeze && !!CHECK_FLAG(n->flags, - ZEBRA_NEIGH_DUPLICATE))) - zvni_neigh_send_add_to_client( - zvni->vni, &n->ip, &n->emac, - n->flags, n->loc_seq); - } - } - } -} - -/* - * Process all neighbors associated with a local MAC upon the MAC being - * deleted. - */ -static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni, - zebra_mac_t *zmac) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - char buf[ETHER_ADDR_STRLEN]; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Processing neighbors on local MAC %s DEL, VNI %u", - prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), - zvni->vni); - - /* Walk all local neighbors and mark as inactive and inform - * BGP, if needed. - * TBD: There is currently no handling for remote neighbors. We - * don't expect them to exist, if they do, do we install the MAC - * as a remote MAC and the neighbor as remote? - */ - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (IS_ZEBRA_NEIGH_ACTIVE(n)) { - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, 0, ZEBRA_NEIGH_ACTIVE); - } - } - } -} - -/* - * Process all neighbors associated with a MAC upon the MAC being remotely - * learnt. - */ -static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni, - zebra_mac_t *zmac) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - char buf[ETHER_ADDR_STRLEN]; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Processing neighbors on remote MAC %s ADD, VNI %u", - prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), - zvni->vni); - - /* Walk all local neighbors and mark as inactive and inform - * BGP, if needed. - */ - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - if (IS_ZEBRA_NEIGH_ACTIVE(n)) { - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, 0, ZEBRA_NEIGH_ACTIVE); - } - } - } -} - -/* - * Process all neighbors associated with a remote MAC upon the MAC being - * deleted. - */ -static void zvni_process_neigh_on_remote_mac_del(zebra_vni_t *zvni, - zebra_mac_t *zmac) -{ - /* NOTE: Currently a NO-OP. */ -} - -static void zvni_probe_neigh_on_mac_add(zebra_vni_t *zvni, zebra_mac_t *zmac) -{ - zebra_neigh_t *nbr = NULL; - struct listnode *node = NULL; - - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, nbr)) { - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL) && - IS_ZEBRA_NEIGH_INACTIVE(nbr)) - zvni_neigh_probe(zvni, nbr); - } -} - -/* - * Inform BGP about local neighbor addition. - */ -static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, - uint8_t neigh_flags, - uint32_t seq) -{ - uint8_t flags = 0; - - if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_DEF_GW)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - /* Set router flag (R-bit) based on local neigh entry add */ - if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_ROUTER_FLAG)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); - if (CHECK_FLAG(neigh_flags, ZEBRA_NEIGH_SVI_IP)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP); - - return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags, - seq, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_ADD); -} - -/* - * Inform BGP about local neighbor deletion. - */ -static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, uint8_t flags, - int state) -{ - return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags, - 0, state, ZEBRA_MACIP_DEL); -} - -/* - * Install remote neighbor into the kernel. - */ -static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct interface *vlan_if; - int flags; - int ret = 0; - - if (!(n->flags & ZEBRA_NEIGH_REMOTE)) - return 0; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return -1; - - flags = DPLANE_NTF_EXT_LEARNED; - if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) - flags |= DPLANE_NTF_ROUTER; - ZEBRA_NEIGH_SET_ACTIVE(n); - - dplane_neigh_add(vlan_if, &n->ip, &n->emac, flags); - - return ret; -} - -/* - * Uninstall remote neighbor from the kernel. - */ -static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct interface *vlan_if; - - if (!(n->flags & ZEBRA_NEIGH_REMOTE)) - return 0; - - if (!zvni->vxlan_if) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", - zvni->vni, zvni); - return -1; - } - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return -1; - - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - - dplane_neigh_delete(vlan_if, &n->ip); - - return 0; -} - -/* - * Probe neighbor from the kernel. - */ -static int zvni_neigh_probe(zebra_vni_t *zvni, zebra_neigh_t *n) -{ - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl; - struct interface *vlan_if; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - vxl = &zif->l2info.vxl; - - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return -1; - - dplane_neigh_update(vlan_if, &n->ip, &n->emac); - - return 0; -} - -/* - * Install neighbor hash entry - called upon access VLAN change. - */ -static void zvni_install_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_neigh_t *n; - struct neigh_walk_ctx *wctx = ctxt; - - n = (zebra_neigh_t *)bucket->data; - - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - zvni_neigh_install(wctx->zvni, n); -} - -/* Get the VRR interface for SVI if any */ -struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp) -{ - struct zebra_vrf *zvrf = NULL; - struct interface *tmp_if = NULL; - struct zebra_if *zif = NULL; - - zvrf = vrf_info_lookup(ifp->vrf_id); - assert(zvrf); - - FOR_ALL_INTERFACES (zvrf->vrf, tmp_if) { - zif = tmp_if->info; - if (!zif) - continue; - - if (!IS_ZEBRA_IF_MACVLAN(tmp_if)) - continue; - - if (zif->link == ifp) - return tmp_if; - } - - return NULL; -} - -static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) -{ - struct listnode *cnode = NULL, *cnnode = NULL; - struct connected *c = NULL; - struct ethaddr macaddr; - - memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { - struct ipaddr ip; - - memset(&ip, 0, sizeof(struct ipaddr)); - if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) - continue; - - if (c->address->family == AF_INET) { - ip.ipa_type = IPADDR_V4; - memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), - sizeof(struct in_addr)); - } else if (c->address->family == AF_INET6) { - ip.ipa_type = IPADDR_V6; - memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), - sizeof(struct in6_addr)); - } else { - continue; - } - - zvni_gw_macip_del(ifp, zvni, &ip); - } - - return 0; -} - -static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni) -{ - struct listnode *cnode = NULL, *cnnode = NULL; - struct connected *c = NULL; - struct ethaddr macaddr; - - memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { - struct ipaddr ip; - - memset(&ip, 0, sizeof(struct ipaddr)); - if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL)) - continue; - - if (c->address->family == AF_INET) { - ip.ipa_type = IPADDR_V4; - memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4), - sizeof(struct in_addr)); - } else if (c->address->family == AF_INET6) { - ip.ipa_type = IPADDR_V6; - memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6), - sizeof(struct in6_addr)); - } else { - continue; - } - - zvni_gw_macip_add(ifp, zvni, &macaddr, &ip); - } - return 0; -} - - -static int zvni_advertise_subnet(zebra_vni_t *zvni, struct interface *ifp, - int advertise) -{ - struct listnode *cnode = NULL, *cnnode = NULL; - struct connected *c = NULL; - struct ethaddr macaddr; - - memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN); - - for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) { - struct prefix p; - - memcpy(&p, c->address, sizeof(struct prefix)); - - /* skip link local address */ - if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) - continue; - - apply_mask(&p); - if (advertise) - ip_prefix_send_to_client(ifp->vrf_id, &p, - ZEBRA_IP_PREFIX_ROUTE_ADD); - else - ip_prefix_send_to_client(ifp->vrf_id, &p, - ZEBRA_IP_PREFIX_ROUTE_DEL); - } - return 0; -} - -/* - * zvni_gw_macip_add_to_client - */ -static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, - struct ethaddr *macaddr, struct ipaddr *ip) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan *vxl = NULL; - struct zebra_vrf *zvrf; - ns_id_t local_ns_id = NS_DEFAULT; - - zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - if (zvrf && zvrf->zns) - local_ns_id = zvrf->zns->ns_id; - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - - vxl = &zif->l2info.vxl; - - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) { - mac = zvni_mac_add(zvni, macaddr); - if (!mac) { - flog_err(EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add MAC %s intf %s(%u) VID %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vxl->access_vlan); - return -1; - } - } - - /* Set "local" forwarding info. */ - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.ns_id = local_ns_id; - mac->fwd_info.local.vid = vxl->access_vlan; - - n = zvni_neigh_lookup(zvni, ip); - if (!n) { - n = zvni_neigh_add(zvni, ip, macaddr); - if (!n) { - flog_err( - EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", - ipaddr2str(ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, zvni->vni); - return -1; - } - } - - /* Set "local" forwarding info. */ - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - ZEBRA_NEIGH_SET_ACTIVE(n); - memcpy(&n->emac, macaddr, ETH_ALEN); - n->ifindex = ifp->ifindex; - - /* Only advertise in BGP if the knob is enabled */ - if (advertise_gw_macip_enabled(zvni)) { - - SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); - SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW); - /* Set Router flag (R-bit) */ - if (ip->ipa_type == IPADDR_V6) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "SVI %s(%u) L2-VNI %u, sending GW MAC %s IP %s add to BGP with flags 0x%x", - ifp->name, ifp->ifindex, zvni->vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); - - zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, - n->flags, n->loc_seq); - } else if (advertise_svi_macip_enabled(zvni)) { - - SET_FLAG(n->flags, ZEBRA_NEIGH_SVI_IP); - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "SVI %s(%u) L2-VNI %u, sending SVI MAC %s IP %s add to BGP with flags 0x%x", - ifp->name, ifp->ifindex, zvni->vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), n->flags); - - zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, - n->flags, n->loc_seq); - } - - return 0; -} - -/* - * zvni_gw_macip_del_from_client - */ -static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, - struct ipaddr *ip) -{ - char buf1[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - zebra_neigh_t *n = NULL; - zebra_mac_t *mac = NULL; - - /* If the neigh entry is not present nothing to do*/ - n = zvni_neigh_lookup(zvni, ip); - if (!n) - return 0; - - /* mac entry should be present */ - mac = zvni_mac_lookup(zvni, &n->emac); - if (!mac) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("MAC %s doesn't exist for neigh %s on VNI %u", - prefix_mac2str(&n->emac, - buf1, sizeof(buf1)), - ipaddr2str(ip, buf2, sizeof(buf2)), - zvni->vni); - return -1; - } - - /* If the entry is not local nothing to do*/ - if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) - return -1; - - /* only need to delete the entry from bgp if we sent it before */ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", - ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni, - prefix_mac2str(&(n->emac), buf1, sizeof(buf1)), - ipaddr2str(ip, buf2, sizeof(buf2))); - - /* Remove neighbor from BGP. */ - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac, - ZEBRA_MACIP_TYPE_GW, ZEBRA_NEIGH_ACTIVE); - - /* Delete this neighbor entry. */ - zvni_neigh_del(zvni, n); - - /* see if the mac needs to be deleted as well*/ - if (mac) - zvni_deref_ip2mac(zvni, mac); - - return 0; -} - -static void zvni_gw_macip_del_for_vni_hash(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_vni_t *zvni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; - struct interface *vlan_if = NULL; - struct interface *vrr_if = NULL; - struct interface *ifp; - - /* Add primary SVI MAC*/ - zvni = (zebra_vni_t *)bucket->data; - - /* Global (Zvrf) advertise-default-gw is disabled, - * but zvni advertise-default-gw is enabled - */ - if (zvni->advertise_gw_macip) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("VNI: %u GW-MACIP enabled, retain gw-macip", - zvni->vni); - return; - } - - ifp = zvni->vxlan_if; - if (!ifp) - return; - zif = ifp->info; - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return; - - zl2_info = zif->l2info.vxl; - - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return; - - /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); - - /* Del VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zvni_del_macip_for_intf(vrr_if, zvni); - - return; -} - -static void zvni_gw_macip_add_for_vni_hash(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_vni_t *zvni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; - struct interface *vlan_if = NULL; - struct interface *vrr_if = NULL; - struct interface *ifp = NULL; - - zvni = (zebra_vni_t *)bucket->data; - - ifp = zvni->vxlan_if; - if (!ifp) - return; - zif = ifp->info; - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return; - zl2_info = zif->l2info.vxl; - - vlan_if = - zvni_map_to_svi(zl2_info.access_vlan, zif->brslave_info.br_if); - if (!vlan_if) - return; - - /* Add primary SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); - - if (advertise_gw_macip_enabled(zvni)) { - /* Add VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zvni_add_macip_for_intf(vrr_if, zvni); - } - - return; -} - -static void zvni_svi_macip_del_for_vni_hash(struct hash_bucket *bucket, - void *ctxt) -{ - zebra_vni_t *zvni = NULL; - struct zebra_if *zif = NULL; - struct zebra_l2info_vxlan zl2_info; - struct interface *vlan_if = NULL; - struct interface *ifp; - - /* Add primary SVI MAC*/ - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) - return; - - /* Global(vrf) advertise-svi-ip disabled, but zvni advertise-svi-ip - * enabled - */ - if (zvni->advertise_svi_macip) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("VNI: %u SVI-MACIP enabled, retain svi-macip", - zvni->vni); - return; - } - - ifp = zvni->vxlan_if; - if (!ifp) - return; - zif = ifp->info; - - /* If down or not mapped to a bridge, we're done. */ - if (!if_is_operative(ifp) || !zif->brslave_info.br_if) - return; - - zl2_info = zif->l2info.vxl; - - vlan_if = zvni_map_to_svi(zl2_info.access_vlan, - zif->brslave_info.br_if); - if (!vlan_if) - return; - - /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); - - return; -} - -static int zvni_local_neigh_update(zebra_vni_t *zvni, - struct interface *ifp, - struct ipaddr *ip, - struct ethaddr *macaddr, - bool is_router) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - struct zebra_vrf *zvrf; - zebra_neigh_t *n = NULL; - zebra_mac_t *zmac = NULL, *old_zmac = NULL; - uint32_t old_mac_seq = 0, mac_new_seq = 0; - bool upd_mac_seq = false; - bool neigh_mac_change = false; - bool neigh_on_hold = false; - bool neigh_was_remote = false; - bool do_dad = false; - struct in_addr vtep_ip = {.s_addr = 0}; - - /* Check if the MAC exists. */ - zmac = zvni_mac_lookup(zvni, macaddr); - if (!zmac) { - /* create a dummy MAC if the MAC is not already present */ - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "AUTO MAC %s created for neigh %s on VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni); - - zmac = zvni_mac_add(zvni, macaddr); - if (!zmac) { - zlog_debug("Failed to add MAC %s VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni); - return -1; - } - - memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info)); - memset(&zmac->flags, 0, sizeof(uint32_t)); - SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO); - } else { - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { - /* - * We don't change the MAC to local upon a neighbor - * learn event, we wait for the explicit local MAC - * learn. However, we have to compute its sequence - * number in preparation for when it actually turns - * local. - */ - upd_mac_seq = true; - } - } - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - if (!zvrf) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug(" Unable to find vrf for: %d", - zvni->vxlan_if->vrf_id); - return -1; - } - - /* Check if the neighbor exists. */ - n = zvni_neigh_lookup(zvni, ip); - if (!n) { - /* New neighbor - create */ - n = zvni_neigh_add(zvni, ip, macaddr); - if (!n) { - flog_err( - EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u", - ipaddr2str(ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, zvni->vni); - return -1; - } - /* Set "local" forwarding info. */ - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->ifindex = ifp->ifindex; - } else { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - bool mac_different; - bool cur_is_router; - - /* Note any changes and see if of interest to BGP. */ - mac_different = (memcmp(n->emac.octet, - macaddr->octet, ETH_ALEN) != 0) ? 1 : 0; - cur_is_router = !!CHECK_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - if (!mac_different && is_router == cur_is_router) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - " Ignoring entry mac is the same and is_router == cur_is_router"); - n->ifindex = ifp->ifindex; - return 0; - } - - if (!mac_different) { - bool is_neigh_freezed = false; - - /* Only the router flag has changed. */ - if (is_router) - SET_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, - ZEBRA_NEIGH_ROUTER_FLAG); - - /* Neigh is in freeze state and freeze action - * is enabled, do not send update to client. - */ - is_neigh_freezed = (zvrf->dup_addr_detect && - zvrf->dad_freeze && - CHECK_FLAG(n->flags, - ZEBRA_NEIGH_DUPLICATE)); - - if (IS_ZEBRA_NEIGH_ACTIVE(n) && - !is_neigh_freezed) - return zvni_neigh_send_add_to_client( - zvni->vni, ip, macaddr, - n->flags, n->loc_seq); - else { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - " Neighbor active and frozen"); - } - return 0; - } - - /* The MAC has changed, need to issue a delete - * first as this means a different MACIP route. - * Also, need to do some unlinking/relinking. - * We also need to update the MAC's sequence number - * in different situations. - */ - if (IS_ZEBRA_NEIGH_ACTIVE(n)) - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, 0, n->state); - old_zmac = zvni_mac_lookup(zvni, &n->emac); - if (old_zmac) { - old_mac_seq = CHECK_FLAG(old_zmac->flags, - ZEBRA_MAC_REMOTE) ? - old_zmac->rem_seq : old_zmac->loc_seq; - neigh_mac_change = upd_mac_seq = true; - listnode_delete(old_zmac->neigh_list, n); - zvni_deref_ip2mac(zvni, old_zmac); - } - - /* Update the forwarding info. */ - n->ifindex = ifp->ifindex; - memcpy(&n->emac, macaddr, ETH_ALEN); - - /* Link to new MAC */ - listnode_add_sort(zmac->neigh_list, n); - } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - /* - * Neighbor has moved from remote to local. Its - * MAC could have also changed as part of the move. - */ - if (memcmp(n->emac.octet, macaddr->octet, - ETH_ALEN) != 0) { - old_zmac = zvni_mac_lookup(zvni, &n->emac); - if (old_zmac) { - old_mac_seq = CHECK_FLAG( - old_zmac->flags, - ZEBRA_MAC_REMOTE) ? - old_zmac->rem_seq : - old_zmac->loc_seq; - neigh_mac_change = upd_mac_seq = true; - listnode_delete(old_zmac->neigh_list, - n); - zvni_deref_ip2mac(zvni, old_zmac); - } - - /* Link to new MAC */ - memcpy(&n->emac, macaddr, ETH_ALEN); - listnode_add_sort(zmac->neigh_list, n); - } - /* Based on Mobility event Scenario-B from the - * draft, neigh's previous state was remote treat this - * event for DAD. - */ - neigh_was_remote = true; - vtep_ip = n->r_vtep_ip; - /* Mark appropriately */ - UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - n->r_vtep_ip.s_addr = INADDR_ANY; - SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->ifindex = ifp->ifindex; - } - } - - /* If MAC was previously remote, or the neighbor had a different - * MAC earlier, recompute the sequence number. - */ - if (upd_mac_seq) { - uint32_t seq1, seq2; - - seq1 = CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE) ? - zmac->rem_seq + 1 : zmac->loc_seq; - seq2 = neigh_mac_change ? old_mac_seq + 1 : 0; - mac_new_seq = zmac->loc_seq < MAX(seq1, seq2) ? - MAX(seq1, seq2) : zmac->loc_seq; - } - - /* Mark Router flag (R-bit) */ - if (is_router) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - /* Check old and/or new MAC detected as duplicate mark - * the neigh as duplicate - */ - if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) { - flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC", - zvni->vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(&n->ip, buf2, sizeof(buf2))); - } - - /* For IP Duplicate Address Detection (DAD) is trigger, - * when the event is extended mobility based on scenario-B - * from the draft, IP/Neigh's MAC binding changed and - * neigh's previous state was remote. - */ - if (neigh_mac_change && neigh_was_remote) - do_dad = true; - - zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad, - &neigh_on_hold, true); - - /* Before we program this in BGP, we need to check if MAC is locally - * learnt. If not, force neighbor to be inactive and reset its seq. - */ - if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) { - ZEBRA_NEIGH_SET_INACTIVE(n); - n->loc_seq = 0; - zmac->loc_seq = mac_new_seq; - return 0; - } - - /* If the MAC's sequence number has changed, inform the MAC and all - * neighbors associated with the MAC to BGP, else just inform this - * neighbor. - */ - if (upd_mac_seq && zmac->loc_seq != mac_new_seq) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Seq changed for MAC %s VNI %u - old %u new %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni, zmac->loc_seq, mac_new_seq); - zmac->loc_seq = mac_new_seq; - if (zvni_mac_send_add_to_client(zvni->vni, macaddr, - zmac->flags, zmac->loc_seq)) - return -1; - zvni_process_neigh_on_local_mac_change(zvni, zmac, 1); - return 0; - } - - n->loc_seq = zmac->loc_seq; - - if (!neigh_on_hold) { - ZEBRA_NEIGH_SET_ACTIVE(n); - - return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, - n->flags, n->loc_seq); - } else { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug(" Neighbor on hold not sending"); - } - return 0; -} - -static int zvni_remote_neigh_update(zebra_vni_t *zvni, - struct interface *ifp, - struct ipaddr *ip, - struct ethaddr *macaddr, - uint16_t state) -{ - char buf[ETHER_ADDR_STRLEN]; - char buf2[INET6_ADDRSTRLEN]; - zebra_neigh_t *n = NULL; - zebra_mac_t *zmac = NULL; - - /* If the neighbor is unknown, there is no further action. */ - n = zvni_neigh_lookup(zvni, ip); - if (!n) - return 0; - - /* If a remote entry, see if it needs to be refreshed */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { -#ifdef GNU_LINUX - if (state & NUD_STALE) - zvni_neigh_install(zvni, n); -#endif - } else { - /* We got a "remote" neighbor notification for an entry - * we think is local. This can happen in a multihoming - * scenario - but only if the MAC is already "remote". - * Just mark our entry as "remote". - */ - zmac = zvni_mac_lookup(zvni, macaddr); - if (!zmac || !CHECK_FLAG(zmac->flags, ZEBRA_MAC_REMOTE)) { - zlog_debug( - "Ignore remote neigh %s (MAC %s) on L2-VNI %u - MAC unknown or local", - ipaddr2str(&n->ip, buf2, sizeof(buf2)), - prefix_mac2str(macaddr, buf, sizeof(buf)), - zvni->vni); - return -1; - } - - UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - ZEBRA_NEIGH_SET_ACTIVE(n); - n->r_vtep_ip = zmac->fwd_info.r_vtep_ip; - } - - return 0; -} - -/* - * Make hash key for MAC. - */ -static unsigned int mac_hash_keymake(const void *p) -{ - const zebra_mac_t *pmac = p; - const void *pnt = (void *)pmac->macaddr.octet; - - return jhash(pnt, ETH_ALEN, 0xa5a5a55a); -} - -/* - * Compare two MAC addresses. - */ -static bool mac_cmp(const void *p1, const void *p2) -{ - const zebra_mac_t *pmac1 = p1; - const zebra_mac_t *pmac2 = p2; - - if (pmac1 == NULL && pmac2 == NULL) - return true; - - if (pmac1 == NULL || pmac2 == NULL) - return false; - - return (memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETH_ALEN) - == 0); -} - -/* - * Callback to allocate MAC hash entry. - */ -static void *zvni_mac_alloc(void *p) -{ - const zebra_mac_t *tmp_mac = p; - zebra_mac_t *mac; - - mac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); - *mac = *tmp_mac; - - return ((void *)mac); -} - -/* - * Add MAC entry. - */ -static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr) -{ - zebra_mac_t tmp_mac; - zebra_mac_t *mac = NULL; - - memset(&tmp_mac, 0, sizeof(zebra_mac_t)); - memcpy(&tmp_mac.macaddr, macaddr, ETH_ALEN); - mac = hash_get(zvni->mac_table, &tmp_mac, zvni_mac_alloc); - assert(mac); - - mac->zvni = zvni; - mac->dad_mac_auto_recovery_timer = NULL; - - mac->neigh_list = list_new(); - mac->neigh_list->cmp = neigh_list_cmp; - - return mac; -} - -/* - * Delete MAC entry. - */ -static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - zebra_mac_t *tmp_mac; - - /* Cancel auto recovery */ - THREAD_OFF(mac->dad_mac_auto_recovery_timer); - - list_delete(&mac->neigh_list); - - /* Free the VNI hash entry and allocated memory. */ - tmp_mac = hash_release(zvni->mac_table, mac); - XFREE(MTYPE_MAC, tmp_mac); - - return 0; -} - -static bool zvni_check_mac_del_from_db(struct mac_walk_ctx *wctx, - zebra_mac_t *mac) -{ - if ((wctx->flags & DEL_LOCAL_MAC) && - (mac->flags & ZEBRA_MAC_LOCAL)) - return true; - else if ((wctx->flags & DEL_REMOTE_MAC) && - (mac->flags & ZEBRA_MAC_REMOTE)) - return true; - else if ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) && - (mac->flags & ZEBRA_MAC_REMOTE) && - IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)) - return true; - else if ((wctx->flags & DEL_LOCAL_MAC) && - (mac->flags & ZEBRA_MAC_AUTO) && - !listcount(mac->neigh_list)) { - if (IS_ZEBRA_DEBUG_VXLAN) { - char buf[ETHER_ADDR_STRLEN]; - - zlog_debug( - "%s: Del MAC %s flags 0x%x", __func__, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - mac->flags); - } - wctx->uninstall = 0; - - return true; - } - - return false; -} - -/* - * Free MAC hash entry (callback) - */ -static void zvni_mac_del_hash_entry(struct hash_bucket *bucket, void *arg) -{ - struct mac_walk_ctx *wctx = arg; - zebra_mac_t *mac = bucket->data; - - if (zvni_check_mac_del_from_db(wctx, mac)) { - if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) { - zvni_mac_send_del_to_client(wctx->zvni->vni, - &mac->macaddr); - } - if (wctx->uninstall) - zvni_mac_uninstall(wctx->zvni, mac); - - zvni_mac_del(wctx->zvni, mac); - } - - return; -} - -/* - * Delete all MAC entries for this VNI. - */ -static void zvni_mac_del_all(zebra_vni_t *zvni, int uninstall, int upd_client, - uint32_t flags) -{ - struct mac_walk_ctx wctx; - - if (!zvni->mac_table) - return; - - memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; - wctx.uninstall = uninstall; - wctx.upd_client = upd_client; - wctx.flags = flags; - - hash_iterate(zvni->mac_table, zvni_mac_del_hash_entry, &wctx); -} - -/* - * Look up MAC hash entry. - */ -static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *mac) -{ - zebra_mac_t tmp; - zebra_mac_t *pmac; - - memset(&tmp, 0, sizeof(tmp)); - memcpy(&tmp.macaddr, mac, ETH_ALEN); - pmac = hash_lookup(zvni->mac_table, &tmp); - - return pmac; -} - -/* - * Inform BGP about local MAC addition. - */ -static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, - uint8_t mac_flags, uint32_t seq) -{ - uint8_t flags = 0; - - if (CHECK_FLAG(mac_flags, ZEBRA_MAC_STICKY)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); - if (CHECK_FLAG(mac_flags, ZEBRA_MAC_DEF_GW)) - SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - - return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags, - seq, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_ADD); -} - -/* - * Inform BGP about local MAC deletion. - */ -static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr) -{ - return zvni_macip_send_msg_to_client(vni, macaddr, NULL, 0 /* flags */, - 0 /* seq */, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_DEL); -} - -struct zvni_from_svi_param { - struct interface *br_if; - struct interface *svi_if; - struct zebra_if *zif; - uint8_t bridge_vlan_aware; - vlanid_t vid; -}; - -static int zvni_map_vlan_ns(struct ns *ns, - void *_in_param, - void **_p_zvni) -{ - struct zebra_ns *zns = ns->info; - struct route_node *rn; - struct interface *br_if; - zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni; - zebra_vni_t *zvni; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl = NULL; - struct zvni_from_svi_param *in_param = - (struct zvni_from_svi_param *)_in_param; - int found = 0; - - if (!in_param) - return NS_WALK_STOP; - br_if = in_param->br_if; - zif = in_param->zif; - assert(zif); - assert(br_if); - - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - /* TODO: Optimize with a hash. */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - vxl = &zif->l2info.vxl; - - if (zif->brslave_info.br_if != br_if) - continue; - - if (!in_param->bridge_vlan_aware - || vxl->access_vlan == in_param->vid) { - found = 1; - break; - } - } - if (!found) - return NS_WALK_CONTINUE; - - zvni = zvni_lookup(vxl->vni); - if (p_zvni) - *p_zvni = zvni; - return NS_WALK_STOP; -} - -/* - * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC - * notifications, to see if they are of interest. - */ -static zebra_vni_t *zvni_map_vlan(struct interface *ifp, - struct interface *br_if, vlanid_t vid) -{ - struct zebra_if *zif; - struct zebra_l2info_bridge *br; - zebra_vni_t **p_zvni; - zebra_vni_t *zvni = NULL; - struct zvni_from_svi_param in_param; - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - br = &zif->l2info.br; - in_param.bridge_vlan_aware = br->vlan_aware; - in_param.vid = vid; - in_param.br_if = br_if; - in_param.zif = zif; - p_zvni = &zvni; - - ns_walk_func(zvni_map_vlan_ns, - (void *)&in_param, - (void **)p_zvni); - return zvni; -} - -static int zvni_from_svi_ns(struct ns *ns, - void *_in_param, - void **_p_zvni) -{ - struct zebra_ns *zns = ns->info; - struct route_node *rn; - struct interface *br_if; - zebra_vni_t **p_zvni = (zebra_vni_t **)_p_zvni; - zebra_vni_t *zvni; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - struct zebra_l2info_vxlan *vxl = NULL; - struct zvni_from_svi_param *in_param = - (struct zvni_from_svi_param *)_in_param; - int found = 0; - - if (!in_param) - return NS_WALK_STOP; - br_if = in_param->br_if; - zif = in_param->zif; - assert(zif); - - /* TODO: Optimize with a hash. */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - if (!tmp_if) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VXLAN) - continue; - if (!if_is_operative(tmp_if)) - continue; - vxl = &zif->l2info.vxl; - - if (zif->brslave_info.br_if != br_if) - continue; - - if (!in_param->bridge_vlan_aware - || vxl->access_vlan == !in_param->vid) { - found = 1; - break; - } - } - - if (!found) - return NS_WALK_CONTINUE; - - zvni = zvni_lookup(vxl->vni); - if (p_zvni) - *p_zvni = zvni; - return NS_WALK_STOP; -} - -/* - * Map SVI and associated bridge to a VNI. This is invoked upon getting - * neighbor notifications, to see if they are of interest. - */ -static zebra_vni_t *zvni_from_svi(struct interface *ifp, - struct interface *br_if) -{ - struct zebra_l2info_bridge *br; - zebra_vni_t *zvni = NULL; - zebra_vni_t **p_zvni; - struct zebra_if *zif; - struct zvni_from_svi_param in_param; - - if (!br_if) - return NULL; - - /* Make sure the linked interface is a bridge. */ - if (!IS_ZEBRA_IF_BRIDGE(br_if)) - return NULL; - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - br = &zif->l2info.br; - in_param.bridge_vlan_aware = br->vlan_aware; - in_param.vid = 0; - - if (in_param.bridge_vlan_aware) { - struct zebra_l2info_vlan *vl; - - if (!IS_ZEBRA_IF_VLAN(ifp)) - return NULL; - - zif = ifp->info; - assert(zif); - vl = &zif->l2info.vl; - in_param.vid = vl->vid; - } - - in_param.br_if = br_if; - in_param.zif = zif; - p_zvni = &zvni; - /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */ - ns_walk_func(zvni_from_svi_ns, - (void *)&in_param, - (void **)p_zvni); - return zvni; -} - -static int zvni_map_to_svi_ns(struct ns *ns, - void *_in_param, - void **_p_ifp) -{ - struct zebra_ns *zns = ns->info; - struct route_node *rn; - struct zvni_from_svi_param *in_param = - (struct zvni_from_svi_param *)_in_param; - struct zebra_l2info_vlan *vl; - struct interface *tmp_if = NULL; - struct interface **p_ifp = (struct interface **)_p_ifp; - struct zebra_if *zif; - - if (!in_param) - return NS_WALK_STOP; - - /* TODO: Optimize with a hash. */ - for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { - tmp_if = (struct interface *)rn->info; - /* Check oper status of the SVI. */ - if (!tmp_if || !if_is_operative(tmp_if)) - continue; - zif = tmp_if->info; - if (!zif || zif->zif_type != ZEBRA_IF_VLAN - || zif->link != in_param->br_if) - continue; - vl = (struct zebra_l2info_vlan *)&zif->l2info.vl; - - if (vl->vid == in_param->vid) { - if (p_ifp) - *p_ifp = tmp_if; - return NS_WALK_STOP; - } - } - return NS_WALK_CONTINUE; -} - /* Map to SVI on bridge corresponding to specified VLAN. This can be one * of two cases: * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface @@ -3765,13 +789,17 @@ static int zvni_map_to_svi_ns(struct ns *ns, * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge interface * itself */ -static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) +struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) { + struct zebra_ns *zns; + struct route_node *rn; struct interface *tmp_if = NULL; struct zebra_if *zif; struct zebra_l2info_bridge *br; - struct zvni_from_svi_param in_param; - struct interface **p_ifp; + struct zebra_l2info_vlan *vl; + uint8_t bridge_vlan_aware; + int found = 0; + /* Defensive check, caller expected to invoke only with valid bridge. */ if (!br_if) return NULL; @@ -3780,483 +808,59 @@ static struct interface *zvni_map_to_svi(vlanid_t vid, struct interface *br_if) zif = br_if->info; assert(zif); br = &zif->l2info.br; - in_param.bridge_vlan_aware = br->vlan_aware; + bridge_vlan_aware = br->vlan_aware; + /* Check oper status of the SVI. */ - if (!in_param.bridge_vlan_aware) + if (!bridge_vlan_aware) return if_is_operative(br_if) ? br_if : NULL; - in_param.vid = vid; - in_param.br_if = br_if; - in_param.zif = NULL; - p_ifp = &tmp_if; - /* Identify corresponding VLAN interface. */ - ns_walk_func(zvni_map_to_svi_ns, - (void *)&in_param, - (void **)p_ifp); - return tmp_if; -} - -static int zvni_map_to_macvlan_ns(struct ns *ns, - void *_in_param, - void **_p_ifp) -{ - struct zebra_ns *zns = ns->info; - struct zvni_from_svi_param *in_param = - (struct zvni_from_svi_param *)_in_param; - struct interface **p_ifp = (struct interface **)_p_ifp; - struct route_node *rn; - struct interface *tmp_if = NULL; - struct zebra_if *zif; - - if (!in_param) - return NS_WALK_STOP; - /* Identify corresponding VLAN interface. */ + /* TODO: Optimize with a hash. */ + zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { tmp_if = (struct interface *)rn->info; /* Check oper status of the SVI. */ if (!tmp_if || !if_is_operative(tmp_if)) continue; zif = tmp_if->info; - - if (!zif || zif->zif_type != ZEBRA_IF_MACVLAN) + if (!zif || zif->zif_type != ZEBRA_IF_VLAN + || zif->link != br_if) continue; + vl = &zif->l2info.vl; - if (zif->link == in_param->svi_if) { - if (p_ifp) - *p_ifp = tmp_if; - return NS_WALK_STOP; + if (vl->vid == vid) { + found = 1; + break; } } - return NS_WALK_CONTINUE; + return found ? tmp_if : NULL; } -/* Map to MAC-VLAN interface corresponding to specified SVI interface. - */ -static struct interface *zvni_map_to_macvlan(struct interface *br_if, - struct interface *svi_if) +static int zebra_evpn_vxlan_del(zebra_evpn_t *zevpn) { - struct interface *tmp_if = NULL; - struct zebra_if *zif; - struct interface **p_ifp; - struct zvni_from_svi_param in_param; - - /* Defensive check, caller expected to invoke only with valid bridge. */ - if (!br_if) - return NULL; - - if (!svi_if) { - zlog_debug("svi_if is not passed."); - return NULL; - } - - /* Determine if bridge is VLAN-aware or not */ - zif = br_if->info; - assert(zif); - - in_param.vid = 0; - in_param.br_if = br_if; - in_param.zif = NULL; - in_param.svi_if = svi_if; - p_ifp = &tmp_if; - - /* Identify corresponding VLAN interface. */ - ns_walk_func(zvni_map_to_macvlan_ns, - (void *)&in_param, - (void **)p_ifp); - return tmp_if; -} - -/* - * Install remote MAC into the forwarding plane. - */ -static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - const struct zebra_if *zif, *br_zif; - const struct zebra_l2info_vxlan *vxl; - bool sticky; - enum zebra_dplane_result res; - const struct interface *br_ifp; - vlanid_t vid; - - if (!(mac->flags & ZEBRA_MAC_REMOTE)) - return 0; - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - - br_ifp = zif->brslave_info.br_if; - if (br_ifp == NULL) - return -1; - - vxl = &zif->l2info.vxl; - - sticky = !!CHECK_FLAG(mac->flags, - (ZEBRA_MAC_STICKY | ZEBRA_MAC_REMOTE_DEF_GW)); - - br_zif = (const struct zebra_if *)(br_ifp->info); - - if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - vid = vxl->access_vlan; - else - vid = 0; - - res = dplane_mac_add(zvni->vxlan_if, br_ifp, vid, - &mac->macaddr, mac->fwd_info.r_vtep_ip, sticky); - if (res != ZEBRA_DPLANE_REQUEST_FAILURE) - return 0; - else - return -1; -} - -/* - * Uninstall remote MAC from the forwarding plane. - */ -static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - const struct zebra_if *zif, *br_zif; - const struct zebra_l2info_vxlan *vxl; - struct in_addr vtep_ip; - const struct interface *ifp, *br_ifp; - vlanid_t vid; - enum zebra_dplane_result res; - - if (!(mac->flags & ZEBRA_MAC_REMOTE)) - return 0; - - if (!zvni->vxlan_if) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", - zvni->vni, zvni); - return -1; - } - - zif = zvni->vxlan_if->info; - if (!zif) - return -1; - - br_ifp = zif->brslave_info.br_if; - if (br_ifp == NULL) - return -1; - - vxl = &zif->l2info.vxl; - - br_zif = (const struct zebra_if *)br_ifp->info; - - if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif)) - vid = vxl->access_vlan; - else - vid = 0; - - ifp = zvni->vxlan_if; - vtep_ip = mac->fwd_info.r_vtep_ip; - - res = dplane_mac_del(ifp, br_ifp, vid, &mac->macaddr, vtep_ip); - if (res != ZEBRA_DPLANE_REQUEST_FAILURE) - return 0; - else - return -1; -} - -/* - * Install MAC hash entry - called upon access VLAN change. - */ -static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt) -{ - zebra_mac_t *mac; - struct mac_walk_ctx *wctx = ctxt; - - mac = (zebra_mac_t *)bucket->data; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) - zvni_mac_install(wctx->zvni, mac); -} - -/* - * Count of remote neighbors referencing this MAC. - */ -static int remote_neigh_count(zebra_mac_t *zmac) -{ - zebra_neigh_t *n = NULL; - struct listnode *node = NULL; - int count = 0; - - for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) { - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - count++; - } - - return count; -} - -/* - * Decrement neighbor refcount of MAC; uninstall and free it if - * appropriate. - */ -static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac) -{ - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) - return; - - /* If all remote neighbors referencing a remote MAC go away, - * we need to uninstall the MAC. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) && - remote_neigh_count(mac) == 0) { - zvni_mac_uninstall(zvni, mac); - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - } - - /* If no neighbors, delete the MAC. */ - if (list_isempty(mac->neigh_list)) - zvni_mac_del(zvni, mac); -} - -/* - * Read and populate local MACs and neighbors corresponding to this VNI. - */ -static void zvni_read_mac_neigh(zebra_vni_t *zvni, struct interface *ifp) -{ - struct zebra_ns *zns; - struct zebra_vrf *zvrf; - struct zebra_if *zif; - struct interface *vlan_if; - struct zebra_l2info_vxlan *vxl; - struct interface *vrr_if; - - zif = ifp->info; - vxl = &zif->l2info.vxl; - zvrf = zebra_vrf_lookup_by_id(zvni->vrf_id); - if (!zvrf || !zvrf->zns) - return; - zns = zvrf->zns; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u", - ifp->name, ifp->ifindex, zvni->vni, - zif->brslave_info.bridge_ifindex); - - macfdb_read_for_bridge(zns, ifp, zif->brslave_info.br_if); - vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); - if (vlan_if) { - - /* Add SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); - - /* Add VRR MAC-IP - if any*/ - vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); - if (vrr_if) - zvni_add_macip_for_intf(vrr_if, zvni); - - neigh_read_for_vlan(zns, vlan_if); - } -} - -/* - * Hash function for VNI. - */ -static unsigned int vni_hash_keymake(const void *p) -{ - const zebra_vni_t *zvni = p; - - return (jhash_1word(zvni->vni, 0)); -} - -/* - * Compare 2 VNI hash entries. - */ -static bool vni_hash_cmp(const void *p1, const void *p2) -{ - const zebra_vni_t *zvni1 = p1; - const zebra_vni_t *zvni2 = p2; - - return (zvni1->vni == zvni2->vni); -} - -static int vni_list_cmp(void *p1, void *p2) -{ - const zebra_vni_t *zvni1 = p1; - const zebra_vni_t *zvni2 = p2; - - if (zvni1->vni == zvni2->vni) - return 0; - return (zvni1->vni < zvni2->vni) ? -1 : 1; -} - -/* - * Callback to allocate VNI hash entry. - */ -static void *zvni_alloc(void *p) -{ - const zebra_vni_t *tmp_vni = p; - zebra_vni_t *zvni; - - zvni = XCALLOC(MTYPE_ZVNI, sizeof(zebra_vni_t)); - zvni->vni = tmp_vni->vni; - return ((void *)zvni); -} - -/* - * Look up VNI hash entry. - */ -static zebra_vni_t *zvni_lookup(vni_t vni) -{ - struct zebra_vrf *zvrf; - zebra_vni_t tmp_vni; - zebra_vni_t *zvni = NULL; - - zvrf = zebra_vrf_get_evpn(); - assert(zvrf); - memset(&tmp_vni, 0, sizeof(zebra_vni_t)); - tmp_vni.vni = vni; - zvni = hash_lookup(zvrf->vni_table, &tmp_vni); - - return zvni; -} - -/* - * Add VNI hash entry. - */ -static zebra_vni_t *zvni_add(vni_t vni) -{ - struct zebra_vrf *zvrf; - zebra_vni_t tmp_zvni; - zebra_vni_t *zvni = NULL; - - zvrf = zebra_vrf_get_evpn(); - assert(zvrf); - memset(&tmp_zvni, 0, sizeof(zebra_vni_t)); - tmp_zvni.vni = vni; - zvni = hash_get(zvrf->vni_table, &tmp_zvni, zvni_alloc); - assert(zvni); - - /* Create hash table for MAC */ - zvni->mac_table = - hash_create(mac_hash_keymake, mac_cmp, "Zebra VNI MAC Table"); - - /* Create hash table for neighbors */ - zvni->neigh_table = hash_create(neigh_hash_keymake, neigh_cmp, - "Zebra VNI Neighbor Table"); - - return zvni; -} - -/* - * Delete VNI hash entry. - */ -static int zvni_del(zebra_vni_t *zvni) -{ - struct zebra_vrf *zvrf; - zebra_vni_t *tmp_zvni; - - zvrf = zebra_vrf_get_evpn(); - assert(zvrf); - - zvni->vxlan_if = NULL; + zevpn_vxlan_if_set(zevpn, zevpn->vxlan_if, false /* set */); /* Remove references to the BUM mcast grp */ - zebra_vxlan_sg_deref(zvni->local_vtep_ip, zvni->mcast_grp); - - /* Free the neighbor hash table. */ - hash_free(zvni->neigh_table); - zvni->neigh_table = NULL; - - /* Free the MAC hash table. */ - hash_free(zvni->mac_table); - zvni->mac_table = NULL; - - /* Free the VNI hash entry and allocated memory. */ - tmp_zvni = hash_release(zvrf->vni_table, zvni); - XFREE(MTYPE_ZVNI, tmp_zvni); - - return 0; -} - -/* - * Inform BGP about local VNI addition. - */ -static int zvni_send_add_to_client(zebra_vni_t *zvni) -{ - struct zserv *client; - struct stream *s; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, ZEBRA_VNI_ADD, zebra_vrf_get_evpn_id()); - stream_putl(s, zvni->vni); - stream_put_in_addr(s, &zvni->local_vtep_ip); - stream_put(s, &zvni->vrf_id, sizeof(vrf_id_t)); /* tenant vrf */ - stream_put_in_addr(s, &zvni->mcast_grp); + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, zevpn->mcast_grp); - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send VNI_ADD %u %s tenant vrf %s to %s", zvni->vni, - inet_ntoa(zvni->local_vtep_ip), - vrf_id_to_name(zvni->vrf_id), - zebra_route_string(client->proto)); - - client->vniadd_cnt++; - return zserv_send_message(client, s); + return zebra_evpn_del(zevpn); } - /* - * Inform BGP about local VNI deletion. + * Build the VNI hash table by going over the VxLAN interfaces. This + * is called when EVPN (advertise-all-vni) is enabled. */ -static int zvni_send_del_to_client(vni_t vni) -{ - struct zserv *client; - struct stream *s; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - stream_reset(s); - - zclient_create_header(s, ZEBRA_VNI_DEL, zebra_vrf_get_evpn_id()); - stream_putl(s, vni); - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send VNI_DEL %u to %s", vni, - zebra_route_string(client->proto)); - - client->vnidel_cnt++; - return zserv_send_message(client, s); -} - -static int zvni_build_hash_table_ns(struct ns *ns, - void *param_in __attribute__((unused)), - void **param_out __attribute__((unused))) +static void zevpn_build_hash_table(void) { - struct zebra_ns *zns = ns->info; + struct zebra_ns *zns; struct route_node *rn; struct interface *ifp; - struct zebra_vrf *zvrf; - - zvrf = zebra_vrf_get_evpn(); - if (!zvrf) - return NS_WALK_STOP; - - /* Walk VxLAN interfaces and create VNI hash. */ + /* Walk VxLAN interfaces and create EVPN hash. */ + zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { vni_t vni; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; @@ -4270,15 +874,7 @@ static int zvni_build_hash_table_ns(struct ns *ns, vxl = &zif->l2info.vxl; vni = vxl->vni; - /* link of VXLAN interface should be in zebra_evpn_vrf */ - if (zvrf->zns->ns_id != vxl->link_nsid) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Intf %s(%u) VNI %u, link not in same " - "namespace than BGP EVPN core instance ", - ifp->name, ifp->ifindex, vni); - continue; - } + /* L3-VNI and L2-VNI are handled seperately */ zl3vni = zl3vni_lookup(vni); if (zl3vni) { @@ -4321,11 +917,11 @@ static int zvni_build_hash_table_ns(struct ns *ns, ifp->name, ifp->ifindex, vni, inet_ntoa(vxl->vtep_ip)); - /* VNI hash entry is expected to exist, if the BGP process is killed */ - zvni = zvni_lookup(vni); - if (zvni) { + /* EVPN hash entry is expected to exist, if the BGP process is killed */ + zevpn = zebra_evpn_lookup(vni); + if (zevpn) { zlog_debug( - "VNI hash already present for IF %s(%u) L2-VNI %u", + "EVPN hash already present for IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); /* @@ -4334,44 +930,49 @@ static int zvni_build_hash_table_ns(struct ns *ns, */ if (if_is_operative(ifp) && zif->brslave_info.br_if) - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); /* Send Local MAC-entries to client */ - zvni_send_mac_to_client(zvni); + zebra_evpn_send_mac_list_to_client(zevpn); /* Send Loval Neighbor entries to client */ - zvni_send_neigh_to_client(zvni); + zebra_evpn_send_neigh_to_client(zevpn); } else { - zvni = zvni_add(vni); - if (!zvni) { + zevpn = zebra_evpn_add(vni); + if (!zevpn) { zlog_debug( - "Failed to add VNI hash, IF %s(%u) L2-VNI %u", + "Failed to add EVPN hash, IF %s(%u) L2-VNI %u", ifp->name, ifp->ifindex, vni); - return NS_WALK_CONTINUE; + return; } - if (zvni->local_vtep_ip.s_addr != + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zvni->mcast_grp.s_addr != + zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { zebra_vxlan_sg_deref( - zvni->local_vtep_ip, - zvni->mcast_grp); + zevpn->local_vtep_ip, + zevpn->mcast_grp); zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->mcast_grp = vxl->mcast_grp; + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vxl->mcast_grp; + /* on local vtep-ip check if ES + * orig-ip needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); } - zvni->vxlan_if = ifp; - vlan_if = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); + vlan_if = zvni_map_to_svi( + vxl->access_vlan, + zif->brslave_info.br_if); if (vlan_if) { - zvni->vrf_id = vlan_if->vrf_id; + zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf( vlan_if->vrf_id); if (zl3vni) listnode_add_sort( - zl3vni->l2vnis, zvni); + zl3vni->l2vnis, zevpn); } /* @@ -4380,195 +981,30 @@ static int zvni_build_hash_table_ns(struct ns *ns, */ if (if_is_operative(ifp) && zif->brslave_info.br_if) - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); } } } - return NS_WALK_CONTINUE; -} - -/* - * Build the VNI hash table by going over the VxLAN interfaces. This - * is called when EVPN (advertise-all-vni) is enabled. - */ - -static void zvni_build_hash_table(void) -{ - ns_walk_func(zvni_build_hash_table_ns, - (void *)NULL, - (void **)NULL); -} - -/* - * See if remote VTEP matches with prefix. - */ -static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep) -{ - return (IPV4_ADDR_SAME(vtep_ip, &zvtep->vtep_ip)); -} - -/* - * Locate remote VTEP in VNI hash table. - */ -static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip) -{ - zebra_vtep_t *zvtep; - - if (!zvni) - return NULL; - - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - if (zvni_vtep_match(vtep_ip, zvtep)) - break; - } - - return zvtep; -} - -/* - * Add remote VTEP to VNI hash table. - */ -static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip, - int flood_control) - -{ - zebra_vtep_t *zvtep; - - zvtep = XCALLOC(MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t)); - - zvtep->vtep_ip = *vtep_ip; - zvtep->flood_control = flood_control; - - if (zvni->vteps) - zvni->vteps->prev = zvtep; - zvtep->next = zvni->vteps; - zvni->vteps = zvtep; - - return zvtep; -} - -/* - * Remove remote VTEP from VNI hash table. - */ -static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep) -{ - if (zvtep->next) - zvtep->next->prev = zvtep->prev; - if (zvtep->prev) - zvtep->prev->next = zvtep->next; - else - zvni->vteps = zvtep->next; - - zvtep->prev = zvtep->next = NULL; - XFREE(MTYPE_ZVNI_VTEP, zvtep); - - return 0; -} - -/* - * Delete all remote VTEPs for this VNI (upon VNI delete). Also - * uninstall from kernel if asked to. - */ -static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall) -{ - zebra_vtep_t *zvtep, *zvtep_next; - - if (!zvni) - return -1; - - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next) { - zvtep_next = zvtep->next; - if (uninstall) - zvni_vtep_uninstall(zvni, &zvtep->vtep_ip); - zvni_vtep_del(zvni, zvtep); - } - - return 0; -} - -/* - * Install remote VTEP into the kernel if the remote VTEP has asked - * for head-end-replication. - */ -static int zvni_vtep_install(zebra_vni_t *zvni, zebra_vtep_t *zvtep) -{ - if (is_vxlan_flooding_head_end() && - (zvtep->flood_control == VXLAN_FLOOD_HEAD_END_REPL)) { - if (ZEBRA_DPLANE_REQUEST_FAILURE == - dplane_vtep_add(zvni->vxlan_if, - &zvtep->vtep_ip, zvni->vni)) - return -1; - } - - return 0; -} - -/* - * Uninstall remote VTEP from the kernel. - */ -static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip) -{ - if (!zvni->vxlan_if) { - zlog_debug("VNI %u hash %p couldn't be uninstalled - no intf", - zvni->vni, zvni); - return -1; - } - - if (ZEBRA_DPLANE_REQUEST_FAILURE == - dplane_vtep_delete(zvni->vxlan_if, vtep_ip, zvni->vni)) - return -1; - - return 0; -} - -/* - * Install or uninstall flood entries in the kernel corresponding to - * remote VTEPs. This is invoked upon change to BUM handling. - */ -static void zvni_handle_flooding_remote_vteps(struct hash_bucket *bucket, - void *zvrf) -{ - zebra_vni_t *zvni; - zebra_vtep_t *zvtep; - - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) - return; - - for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) { - if (is_vxlan_flooding_head_end()) - zvni_vtep_install(zvni, zvtep); - else - zvni_vtep_uninstall(zvni, &zvtep->vtep_ip); - } } /* - * Cleanup VNI/VTEP and update kernel + * Cleanup EVPN/VTEP and update kernel */ -static void zvni_cleanup_all(struct hash_bucket *bucket, void *arg) +static void zebra_evpn_vxlan_cleanup_all(struct hash_bucket *bucket, void *arg) { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; struct zebra_vrf *zvrf = (struct zebra_vrf *)arg; - zvni = (zebra_vni_t *)bucket->data; + zevpn = (zebra_evpn_t *)bucket->data; /* remove from l3-vni list */ if (zvrf->l3vni) zl3vni = zl3vni_lookup(zvrf->l3vni); if (zl3vni) - listnode_delete(zl3vni->l2vnis, zvni); - - /* Free up all neighbors and MACs, if any. */ - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + listnode_delete(zl3vni->l2vnis, zevpn); - /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 1); - - /* Delete the hash entry. */ - zvni_del(zvni); + zebra_evpn_cleanup_all(bucket, arg); } /* cleanup L3VNI */ @@ -4641,7 +1077,7 @@ static void *zl3vni_rmac_alloc(void *p) const zebra_mac_t *tmp_rmac = p; zebra_mac_t *zrmac; - zrmac = XCALLOC(MTYPE_MAC, sizeof(zebra_mac_t)); + zrmac = XCALLOC(MTYPE_L3VNI_MAC, sizeof(zebra_mac_t)); *zrmac = *tmp_rmac; return ((void *)zrmac); @@ -4685,7 +1121,7 @@ static int zl3vni_rmac_del(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) } tmp_rmac = hash_release(zl3vni->rmac_table, zrmac); - XFREE(MTYPE_MAC, tmp_rmac); + XFREE(MTYPE_L3VNI_MAC, tmp_rmac); return 0; } @@ -4722,8 +1158,9 @@ static int zl3vni_rmac_install(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) else vid = 0; - res = dplane_mac_add(zl3vni->vxlan_if, br_ifp, vid, - &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0); + res = dplane_rem_mac_add(zl3vni->vxlan_if, br_ifp, vid, + &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip, 0, 0, + false /*was_static*/); if (res != ZEBRA_DPLANE_REQUEST_FAILURE) return 0; else @@ -4772,7 +1209,7 @@ static int zl3vni_rmac_uninstall(zebra_l3vni_t *zl3vni, zebra_mac_t *zrmac) else vid = 0; - res = dplane_mac_del(zl3vni->vxlan_if, br_ifp, vid, + res = dplane_rem_mac_del(zl3vni->vxlan_if, br_ifp, vid, &zrmac->macaddr, zrmac->fwd_info.r_vtep_ip); if (res != ZEBRA_DPLANE_REQUEST_FAILURE) return 0; @@ -4881,7 +1318,7 @@ static void *zl3vni_nh_alloc(void *p) const zebra_neigh_t *tmp_n = p; zebra_neigh_t *n; - n = XCALLOC(MTYPE_NEIGH, sizeof(zebra_neigh_t)); + n = XCALLOC(MTYPE_L3NEIGH, sizeof(zebra_neigh_t)); *n = *tmp_n; return ((void *)n); @@ -4927,7 +1364,7 @@ static int zl3vni_nh_del(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) } tmp_n = hash_release(zl3vni->nh_table, n); - XFREE(MTYPE_NEIGH, tmp_n); + XFREE(MTYPE_L3NEIGH, tmp_n); return 0; } @@ -4951,7 +1388,8 @@ static int zl3vni_nh_install(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) if (n->flags & ZEBRA_NEIGH_ROUTER_FLAG) flags |= DPLANE_NTF_ROUTER; - dplane_neigh_add(zl3vni->svi_if, &n->ip, &n->emac, flags); + dplane_rem_neigh_add(zl3vni->svi_if, &n->ip, &n->emac, flags, + false /*was_static*/); return ret; } @@ -4968,7 +1406,7 @@ static int zl3vni_nh_uninstall(zebra_l3vni_t *zl3vni, zebra_neigh_t *n) if (!zl3vni->svi_if || !if_is_operative(zl3vni->svi_if)) return 0; - dplane_neigh_delete(zl3vni->svi_if, &n->ip); + dplane_rem_neigh_delete(zl3vni->svi_if, &n->ip); return 0; } @@ -5112,7 +1550,7 @@ static void *zl3vni_alloc(void *p) /* * Look up L3 VNI hash entry. */ -static zebra_l3vni_t *zl3vni_lookup(vni_t vni) +zebra_l3vni_t *zl3vni_lookup(vni_t vni) { zebra_l3vni_t tmp_l3vni; zebra_l3vni_t *zl3vni = NULL; @@ -5142,15 +1580,13 @@ static zebra_l3vni_t *zl3vni_add(vni_t vni, vrf_id_t vrf_id) zl3vni->svi_if = NULL; zl3vni->vxlan_if = NULL; zl3vni->l2vnis = list_new(); - zl3vni->l2vnis->cmp = vni_list_cmp; + zl3vni->l2vnis->cmp = zebra_evpn_list_cmp; /* Create hash table for remote RMAC */ - zl3vni->rmac_table = hash_create(mac_hash_keymake, mac_cmp, - "Zebra L3-VNI RMAC-Table"); + zl3vni->rmac_table = zebra_mac_db_create("Zebra L3-VNI RMAC-Table"); /* Create hash table for neighbors */ - zl3vni->nh_table = hash_create(neigh_hash_keymake, neigh_cmp, - "Zebra L3-VNI next-hop table"); + zl3vni->nh_table = zebra_neigh_db_create("Zebra L3-VNI next-hop table"); return zl3vni; } @@ -5181,22 +1617,14 @@ static int zl3vni_del(zebra_l3vni_t *zl3vni) return 0; } -static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, - void *_zl3vni, - void **_pifp) +struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) { - struct zebra_ns *zns = ns->info; - zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)_zl3vni; + struct zebra_ns *zns = NULL; struct route_node *rn = NULL; struct interface *ifp = NULL; - struct zebra_vrf *zvrf; - - zvrf = zebra_vrf_get_evpn(); - - if (!zvrf) - return NS_WALK_STOP; /* loop through all vxlan-interface */ + zns = zebra_ns_lookup(NS_DEFAULT); for (rn = route_top(zns->if_table); rn; rn = route_next(rn)) { struct zebra_if *zif = NULL; @@ -5211,39 +1639,13 @@ static int zl3vni_map_to_vxlan_if_ns(struct ns *ns, continue; vxl = &zif->l2info.vxl; - if (vxl->vni != zl3vni->vni) - continue; - - /* link of VXLAN interface should be in zebra_evpn_vrf */ - if (zvrf->zns->ns_id != vxl->link_nsid) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Intf %s(%u) VNI %u, link not in same " - "namespace than BGP EVPN core instance ", - ifp->name, ifp->ifindex, vxl->vni); - continue; + if (vxl->vni == zl3vni->vni) { + zl3vni->local_vtep_ip = vxl->vtep_ip; + return ifp; } - - - zl3vni->local_vtep_ip = vxl->vtep_ip; - if (_pifp) - *_pifp = (void *)ifp; - return NS_WALK_STOP; } - return NS_WALK_CONTINUE; -} - -struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni) -{ - struct interface **p_ifp; - struct interface *ifp = NULL; - - p_ifp = &ifp; - - ns_walk_func(zl3vni_map_to_vxlan_if_ns, - (void *)zl3vni, (void **)p_ifp); - return ifp; + return NULL; } struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni) @@ -5280,7 +1682,8 @@ struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni) if (!zif) return NULL; - return zvni_map_to_macvlan(zif->brslave_info.br_if, zl3vni->svi_if); + return zebra_evpn_map_to_macvlan(zif->brslave_info.br_if, + zl3vni->svi_if); } @@ -5493,13 +1896,13 @@ static void zebra_vxlan_process_l3vni_oper_down(zebra_l3vni_t *zl3vni) zl3vni_send_del_to_client(zl3vni); } -static void zvni_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) +static void zevpn_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) { - zebra_vni_t *zvni = (zebra_vni_t *)bucket->data; + zebra_evpn_t *zevpn = (zebra_evpn_t *)bucket->data; zebra_l3vni_t *zl3vni = (zebra_l3vni_t *)ctxt; - if (zvni->vrf_id == zl3vni_vrf_id(zl3vni)) - listnode_add_sort(zl3vni->l2vnis, zvni); + if (zevpn->vrf_id == zl3vni_vrf_id(zl3vni)) + listnode_add_sort(zl3vni->l2vnis, zevpn); } /* @@ -5508,7 +1911,7 @@ static void zvni_add_to_l3vni_list(struct hash_bucket *bucket, void *ctxt) static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, int add) { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; /* There is a possibility that VNI notification was already received * from kernel and we programmed it as L2-VNI @@ -5520,28 +1923,27 @@ static int zebra_vxlan_handle_vni_transition(struct zebra_vrf *zvrf, vni_t vni, */ if (add) { /* Locate hash entry */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Del L2-VNI %u - transition to L3-VNI", vni); - /* Delete VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + /* Delete EVPN from BGP. */ + zebra_evpn_send_del_to_client(zevpn); - /* Free up all neighbors and MAC, if any. */ - zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 0); + zebra_evpn_vtep_del_all(zevpn, 0); /* Delete the hash entry. */ - if (zvni_del(zvni)) { + if (zebra_evpn_vxlan_del(zevpn)) { flog_err(EC_ZEBRA_VNI_DEL_FAILED, - "Failed to del VNI hash %p, VNI %u", zvni, - zvni->vni); + "Failed to del EVPN hash %p, VNI %u", zevpn, + zevpn->vni); return -1; } } else { @@ -5583,40 +1985,6 @@ static void zl3vni_del_nh_hash_entry(struct hash_bucket *bucket, void *ctx) zl3vni_nh_del(zl3vni, n); } -static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, - uint16_t cmd) -{ - struct zserv *client = NULL; - struct stream *s = NULL; - char buf[PREFIX_STRLEN]; - - client = zserv_find_client(ZEBRA_ROUTE_BGP, 0); - /* BGP may not be running. */ - if (!client) - return 0; - - s = stream_new(ZEBRA_MAX_PACKET_SIZ); - - zclient_create_header(s, cmd, vrf_id); - stream_put(s, p, sizeof(struct prefix)); - - /* Write packet size. */ - stream_putw_at(s, 0, stream_get_endp(s)); - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Send ip prefix %s %s on vrf %s", - prefix2str(p, buf, sizeof(buf)), - (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL", - vrf_id_to_name(vrf_id)); - - if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) - client->prefixadd_cnt++; - else - client->prefixdel_cnt++; - - return zserv_send_message(client, s); -} - /* re-add remote rmac if needed */ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni, struct ethaddr *rmac) @@ -5636,503 +2004,6 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni, return 0; } -/* Process a remote MACIP add from BGP. */ -static void process_remote_macip_add(vni_t vni, - struct ethaddr *macaddr, - uint16_t ipa_len, - struct ipaddr *ipaddr, - uint8_t flags, - uint32_t seq, - struct in_addr vtep_ip) -{ - zebra_vni_t *zvni; - zebra_vtep_t *zvtep; - zebra_mac_t *mac = NULL, *old_mac = NULL; - zebra_neigh_t *n = NULL; - int update_mac = 0, update_neigh = 0; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - struct interface *ifp = NULL; - struct zebra_if *zif = NULL; - struct zebra_vrf *zvrf; - uint32_t tmp_seq; - bool sticky; - bool remote_gw; - bool is_router; - bool do_dad = false; - bool is_dup_detect = false; - - /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni); - return; - } - - ifp = zvni->vxlan_if; - if (ifp) - zif = ifp->info; - if (!ifp || - !if_is_operative(ifp) || - !zif || - !zif->brslave_info.br_if) { - zlog_warn("Ignoring remote MACIP ADD VNI %u, invalid interface state or info", - vni); - return; - } - - /* The remote VTEP specified should normally exist, but it is - * possible that when peering comes up, peer may advertise MACIP - * routes before advertising type-3 routes. - */ - zvtep = zvni_vtep_find(zvni, &vtep_ip); - if (!zvtep) { - zvtep = zvni_vtep_add(zvni, &vtep_ip, VXLAN_FLOOD_DISABLED); - if (!zvtep) { - flog_err( - EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to add remote VTEP, VNI %u zvni %p upon remote MACIP ADD", - vni, zvni); - return; - } - - zvni_vtep_install(zvni, zvtep); - } - - sticky = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY); - remote_gw = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW); - is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); - - mac = zvni_mac_lookup(zvni, macaddr); - - /* Ignore if the mac is already present as a gateway mac */ - if (mac && - CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW) && - CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", - vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); - return; - } - - zvrf = zebra_vrf_get_evpn(); - if (!zvrf) - return; - - /* check if the remote MAC is unknown or has a change. - * If so, that needs to be updated first. Note that client could - * install MAC and MACIP separately or just install the latter. - */ - if (!mac - || !CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) - || sticky != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) - || remote_gw != !!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW) - || !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip) - || seq != mac->rem_seq) - update_mac = 1; - - if (update_mac) { - if (!mac) { - mac = zvni_mac_add(zvni, macaddr); - if (!mac) { - zlog_warn( - "Failed to add MAC %s VNI %u Remote VTEP %s", - prefix_mac2str(macaddr, buf, - sizeof(buf)), - vni, inet_ntoa(vtep_ip)); - return; - } - - /* Is this MAC created for a MACIP? */ - if (ipa_len) - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } else { - /* When host moves but changes its (MAC,IP) - * binding, BGP may install a MACIP entry that - * corresponds to "older" location of the host - * in transient situations (because {IP1,M1} - * is a different route from {IP1,M2}). Check - * the sequence number and ignore this update - * if appropriate. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) - tmp_seq = mac->loc_seq; - else - tmp_seq = mac->rem_seq; - - if (seq < tmp_seq) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing MAC has higher seq %u flags 0x%x", - vni, - prefix_mac2str(macaddr, - buf, sizeof(buf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, - buf1, sizeof(buf1)) : "", - tmp_seq, mac->flags); - return; - } - } - - /* Check MAC's curent state is local (this is the case - * where MAC has moved from L->R) and check previous - * detection started via local learning. - * RFC-7432: A PE/VTEP that detects a MAC mobility - * event via local learning starts an M-second timer. - * - * VTEP-IP or seq. change alone is not considered - * for dup. detection. - * - * MAC is already marked duplicate set dad, then - * is_dup_detect will be set to not install the entry. - */ - if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) && - mac->dad_count) || - CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) - do_dad = true; - - /* Remove local MAC from BGP. */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) - zvni_mac_send_del_to_client(zvni->vni, macaddr); - - /* Set "auto" and "remote" forwarding info. */ - UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - mac->fwd_info.r_vtep_ip = vtep_ip; - - if (sticky) - SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - - if (remote_gw) - SET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); - - zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, - mac->fwd_info.r_vtep_ip, - do_dad, &is_dup_detect, - false); - - if (!is_dup_detect) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); - /* Install the entry. */ - zvni_mac_install(zvni, mac); - } - } - - /* Update seq number. */ - mac->rem_seq = seq; - - /* If there is no IP, return after clearing AUTO flag of MAC. */ - if (!ipa_len) { - UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - return; - } - - /* Reset flag */ - do_dad = false; - - /* Check if the remote neighbor itself is unknown or has a - * change. If so, create or update and then install the entry. - */ - n = zvni_neigh_lookup(zvni, ipaddr); - if (!n - || !CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) - || is_router != !!CHECK_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG) - || (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0) - || !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip) - || seq != n->rem_seq) - update_neigh = 1; - - if (update_neigh) { - if (!n) { - n = zvni_neigh_add(zvni, ipaddr, macaddr); - if (!n) { - zlog_warn( - "Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s", - ipaddr2str(ipaddr, buf1, - sizeof(buf1)), - prefix_mac2str(macaddr, buf, - sizeof(buf)), - vni, inet_ntoa(vtep_ip)); - return; - } - - } else { - const char *n_type; - - /* When host moves but changes its (MAC,IP) - * binding, BGP may install a MACIP entry that - * corresponds to "older" location of the host - * in transient situations (because {IP1,M1} - * is a different route from {IP1,M2}). Check - * the sequence number and ignore this update - * if appropriate. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { - tmp_seq = n->loc_seq; - n_type = "local"; - } else { - tmp_seq = n->rem_seq; - n_type = "remote"; - } - if (seq < tmp_seq) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignore remote MACIP ADD VNI %u MAC %s%s%s as existing %s Neigh has higher seq %u", - vni, - prefix_mac2str(macaddr, - buf, sizeof(buf)), - " IP ", - ipaddr2str(ipaddr, buf1, sizeof(buf1)), - n_type, - tmp_seq); - return; - } - if (memcmp(&n->emac, macaddr, sizeof(*macaddr)) != 0) { - /* MAC change, send a delete for old - * neigh if learnt locally. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) && - IS_ZEBRA_NEIGH_ACTIVE(n)) - zvni_neigh_send_del_to_client( - zvni->vni, &n->ip, - &n->emac, 0, n->state); - - /* update neigh list for macs */ - old_mac = zvni_mac_lookup(zvni, &n->emac); - if (old_mac) { - listnode_delete(old_mac->neigh_list, n); - zvni_deref_ip2mac(zvni, old_mac); - } - listnode_add_sort(mac->neigh_list, n); - memcpy(&n->emac, macaddr, ETH_ALEN); - - /* Check Neigh's curent state is local - * (this is the case where neigh/host has moved - * from L->R) and check previous detction - * started via local learning. - * - * RFC-7432: A PE/VTEP that detects a MAC - * mobilit event via local learning starts - * an M-second timer. - * VTEP-IP or seq. change along is not - * considered for dup. detection. - * - * Mobilty event scenario-B IP-MAC binding - * changed. - */ - if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) - && n->dad_count) - do_dad = true; - - } - } - - /* Set "remote" forwarding info. */ - UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); - n->r_vtep_ip = vtep_ip; - SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); - - /* Set router flag (R-bit) to this Neighbor entry */ - if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG)) - SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - else - UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - - /* Check old or new MAC detected as duplicate, - * inherit duplicate flag to this neigh. - */ - if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_mac, - mac, n)) { - flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, - "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC", - zvni->vni, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - ipaddr2str(&n->ip, buf1, sizeof(buf1))); - } - - /* Check duplicate address detection for IP */ - zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, - n->r_vtep_ip, - do_dad, - &is_dup_detect, - false); - /* Install the entry. */ - if (!is_dup_detect) - zvni_neigh_install(zvni, n); - } - - zvni_probe_neigh_on_mac_add(zvni, mac); - - /* Update seq number. */ - n->rem_seq = seq; -} - -/* Process a remote MACIP delete from BGP. */ -static void process_remote_macip_del(vni_t vni, - struct ethaddr *macaddr, - uint16_t ipa_len, - struct ipaddr *ipaddr, - struct in_addr vtep_ip) -{ - zebra_vni_t *zvni; - zebra_mac_t *mac = NULL; - zebra_neigh_t *n = NULL; - struct interface *ifp = NULL; - struct zebra_if *zif = NULL; - struct zebra_ns *zns; - struct zebra_l2info_vxlan *vxl; - struct zebra_vrf *zvrf; - char buf[ETHER_ADDR_STRLEN]; - char buf1[INET6_ADDRSTRLEN]; - - /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Unknown VNI %u upon remote MACIP DEL", vni); - return; - } - - ifp = zvni->vxlan_if; - if (ifp) - zif = ifp->info; - if (!ifp || - !if_is_operative(ifp) || - !zif || - !zif->brslave_info.br_if) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("Ignoring remote MACIP DEL VNI %u, invalid interface state or info", - vni); - return; - } - zns = zebra_ns_lookup(NS_DEFAULT); - vxl = &zif->l2info.vxl; - - /* It is possible remote vtep del request is processed prior to - * remote macip route delete. remote_vtep_del does not clean up - * the macip route delete. Explicite withdraw of the macip route - * is expected to recieve. This handler removes the remote route. - */ - mac = zvni_mac_lookup(zvni, macaddr); - if (ipa_len) - n = zvni_neigh_lookup(zvni, ipaddr); - - if (n && !mac) { - zlog_warn("Failed to locate MAC %s for neigh %s VNI %u upon remote MACIP DEL", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipaddr2str(ipaddr, buf1, sizeof(buf1)), vni); - return; - } - - /* If the remote mac or neighbor doesn't exist there is nothing - * more to do. Otherwise, uninstall the entry and then remove it. - */ - if (!mac && !n) - return; - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - - /* Ignore the delete if this mac is a gateway mac-ip */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) - && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) { - zlog_warn( - "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", - vni, - prefix_mac2str(macaddr, buf, sizeof(buf)), - ipa_len ? " IP " : "", - ipa_len ? - ipaddr2str(ipaddr, buf1, sizeof(buf1)) : ""); - return; - } - - /* Uninstall remote neighbor or MAC. */ - if (n) { - if (zvrf->dad_freeze && - CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE) && - CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) && - (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) { - struct interface *vlan_if; - - vlan_if = zvni_map_to_svi(vxl->access_vlan, - zif->brslave_info.br_if); - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry", - __func__, - ipaddr2str(ipaddr, buf1, sizeof(buf1)), - n->flags, - vlan_if ? vlan_if->name : "Unknown"); - if (vlan_if) - neigh_read_specific_ip(ipaddr, vlan_if); - } - - /* When the MAC changes for an IP, it is possible the - * client may update the new MAC before trying to delete the - * "old" neighbor (as these are two different MACIP routes). - * Do the delete only if the MAC matches. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) - && (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) { - zvni_neigh_uninstall(zvni, n); - zvni_neigh_del(zvni, n); - zvni_deref_ip2mac(zvni, mac); - } - } else { - /* DAD: when MAC is freeze state as remote learn event, - * remote mac-ip delete event is received will result in freeze - * entry removal, first fetch kernel for the same entry present - * as LOCAL and reachable, avoid deleting this entry instead - * use kerenel local entry to update during unfreeze time. - */ - if (zvrf->dad_freeze && - CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) && - CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry", - __func__, - prefix_mac2str(macaddr, buf, - sizeof(buf)), - mac->flags); - macfdb_read_specific_mac(zns, zif->brslave_info.br_if, - macaddr, vxl->access_vlan); - } - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_del(zvni, mac); - /* - * the remote sequence number in the auto mac entry - * needs to be reset to 0 as the mac entry may have - * been removed on all VTEPs (including - * the originating one) - */ - mac->rem_seq = 0; - - /* If all remote neighbors referencing a remote MAC - * go away, we need to uninstall the MAC. - */ - if (remote_neigh_count(mac) == 0) { - zvni_mac_uninstall(zvni, mac); - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - } - if (list_isempty(mac->neigh_list)) - zvni_mac_del(zvni, mac); - else - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } - } -} - - /* Public functions */ int is_l3vni_for_prefix_routes_only(vni_t vni) @@ -6526,22 +2397,22 @@ void zebra_vxlan_print_vrf_vni(struct vty *vty, struct zebra_vrf *zvrf, void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (!num_neigh) return; @@ -6553,22 +2424,22 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, * the maximum width. */ memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); if (!use_json) { vty_out(vty, "Number of ARPs (local and remote) known for this VNI: %u\n", num_neigh); - vty_out(vty, "%*s %-6s %-8s %-17s %-21s %s\n", -wctx.addr_width, - "IP", "Type", "State", "MAC", "Remote VTEP", "Seq #'s"); + zebra_evpn_print_neigh_hdr(vty, &wctx); } else json_object_int_add(json, "numArpNd", num_neigh); - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); @@ -6595,9 +2466,9 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; - hash_iterate(zvrf->vni_table, + hash_iterate(zvrf->evpn_table, (void (*)(struct hash_bucket *, - void *))zvni_print_neigh_hash_all_vni, + void *))zevpn_print_neigh_hash_all_evpn, args); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6626,9 +2497,9 @@ void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty, args[1] = json; args[2] = (void *)(ptrdiff_t)print_dup; - hash_iterate(zvrf->vni_table, + hash_iterate(zvrf->evpn_table, (void (*)(struct hash_bucket *, - void *))zvni_print_neigh_hash_all_vni_detail, + void *))zevpn_print_neigh_hash_all_evpn_detail, args); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6644,21 +2515,21 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_neigh_t *n; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - n = zvni_neigh_lookup(zvni, ip); + n = zebra_evpn_neigh_lookup(zevpn, ip); if (!n) { if (!use_json) vty_out(vty, @@ -6669,7 +2540,7 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, if (use_json) json = json_object_new_object(); - zvni_print_neigh(n, vty, json); + zebra_evpn_print_neigh(n, vty, json); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6686,22 +2557,22 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct in_addr vtep_ip, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (!num_neigh) return; @@ -6709,14 +2580,15 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_neigh_hash, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6734,7 +2606,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_neigh; struct neigh_walk_ctx wctx; json_object *json = NULL; @@ -6742,17 +2614,17 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_neigh = hashcount(zvni->neigh_table); + num_neigh = hashcount(zevpn->neigh_table); if (!num_neigh) return; - num_neigh = num_dup_detected_neighs(zvni); + num_neigh = num_dup_detected_neighs(zevpn); if (!num_neigh) return; @@ -6764,23 +2636,25 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, * the maximum width. */ memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.addr_width = 15; wctx.json = json; - hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_find_neigh_addr_width, + &wctx); if (!use_json) { vty_out(vty, "Number of ARPs (local and remote) known for this VNI: %u\n", num_neigh); - vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + vty_out(vty, "%*s %-6s %-8s %-17s %-30s\n", -wctx.addr_width, "IP", "Type", - "State", "MAC", "Remote VTEP"); + "State", "MAC", "Remote ES/VTEP"); } else json_object_int_add(json, "numArpNd", num_neigh); - hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, &wctx); + hash_iterate(zevpn->neigh_table, zebra_evpn_print_dad_neigh_hash, + &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6795,7 +2669,7 @@ void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx wctx; json_object *json = NULL; @@ -6803,15 +2677,15 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; @@ -6821,7 +2695,7 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, } memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.json = json_mac; @@ -6829,12 +2703,15 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vty_out(vty, "Number of MACs (local and remote) known for this VNI: %u\n", num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s %s\n", "MAC", "Type", - "Intf/Remote VTEP", "VLAN", "Seq #'s"); + vty_out(vty, + "Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy\n"); + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %s\n", "MAC", + "Type", "Flags", "Intf/Remote ES/VTEP", + "VLAN", "Seq #'s"); } else json_object_int_add(json, "numMacs", num_macs); - hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, &wctx); if (use_json) { json_object_object_add(json, "macs", json_mac); @@ -6865,7 +2742,7 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, wctx.vty = vty; wctx.json = json; wctx.print_dup = print_dup; - hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6896,7 +2773,7 @@ void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty, wctx.vty = vty; wctx.json = json; wctx.print_dup = print_dup; - hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni_detail, + hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn_detail, &wctx); if (use_json) { @@ -6927,7 +2804,7 @@ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; wctx.json = json; - hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); + hash_iterate(zvrf->evpn_table, zevpn_print_mac_hash_all_evpn, &wctx); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( @@ -6943,22 +2820,22 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *macaddr, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_mac_t *mac; json_object *json = NULL; if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { if (use_json) vty_out(vty, "{}\n"); @@ -6972,7 +2849,7 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, if (use_json) json = json_object_new_object(); - zvni_print_mac(mac, vty, json); + zebra_evpn_print_mac(mac, vty, json); if (use_json) { vty_out(vty, "%s\n", json_object_to_json_string_ext( json, JSON_C_TO_STRING_PRETTY)); @@ -6985,7 +2862,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct mac_walk_ctx wctx; uint32_t num_macs; json_object *json = NULL; @@ -6994,17 +2871,17 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; - num_macs = num_dup_detected_macs(zvni); + num_macs = num_dup_detected_macs(zevpn); if (!num_macs) return; @@ -7014,7 +2891,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, } memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.json = json_mac; @@ -7022,12 +2899,12 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, vty_out(vty, "Number of MACs (local and remote) known for this VNI: %u\n", num_macs); - vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", - "Intf/Remote VTEP", "VLAN"); + vty_out(vty, "%-17s %-6s %-5s %-30s %-5s\n", "MAC", "Type", + "Flags", "Intf/Remote ES/VTEP", "VLAN"); } else json_object_int_add(json, "numMacs", num_macs); - hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, &wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_dad_mac_hash, &wctx); if (use_json) { json_object_object_add(json, "macs", json_mac); @@ -7041,7 +2918,7 @@ void zebra_vxlan_print_macs_vni_dad(struct vty *vty, int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, struct ethaddr *macaddr) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_mac_t *mac; struct listnode *node = NULL; zebra_neigh_t *nbr = NULL; @@ -7049,13 +2926,13 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, if (!is_evpn_enabled()) return 0; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_warn("VNI %u does not exist\n", vni); return -1; } - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) { zlog_warn("Requested MAC does not exist in VNI %u\n", vni); return -1; @@ -7080,7 +2957,8 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) ZEBRA_NEIGH_SET_INACTIVE(nbr); else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) - zvni_neigh_install(zvni, nbr); + zebra_evpn_rem_neigh_install( + zevpn, nbr, false /*was_static*/); } UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); @@ -7103,20 +2981,20 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, /* Local: Notify Peer VTEPs, Remote: Install the entry */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { /* Inform to BGP */ - if (zvni_mac_send_add_to_client(zvni->vni, - &mac->macaddr, - mac->flags, - mac->loc_seq)) + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr, + mac->flags, mac->loc_seq, + mac->es)) return 0; /* Process all neighbors associated with this MAC. */ - zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + 0 /*es_change*/); } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); /* Install the entry. */ - zvni_mac_install(zvni, mac); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); } return 0; @@ -7125,7 +3003,7 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_neigh_t *nbr; zebra_mac_t *mac; char buf[INET6_ADDRSTRLEN]; @@ -7134,13 +3012,13 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, if (!is_evpn_enabled()) return 0; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug("VNI %u does not exist\n", vni); return -1; } - nbr = zvni_neigh_lookup(zvni, ip); + nbr = zebra_evpn_neigh_lookup(zevpn, ip); if (!nbr) { zlog_warn("Requested host IP does not exist in VNI %u\n", vni); return -1; @@ -7154,7 +3032,7 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, return -1; } - mac = zvni_mac_lookup(zvni, &nbr->emac); + mac = zebra_evpn_mac_lookup(zevpn, &nbr->emac); if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { zlog_warn( @@ -7175,21 +3053,21 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, THREAD_OFF(nbr->dad_ip_auto_recovery_timer); if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { - zvni_neigh_send_add_to_client(zvni->vni, ip, - &nbr->emac, - nbr->flags, nbr->loc_seq); + zebra_evpn_neigh_send_add_to_client(zevpn->vni, ip, &nbr->emac, + nbr->mac, nbr->flags, + nbr->loc_seq); } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_neigh_install(zvni, nbr); + zebra_evpn_rem_neigh_install(zevpn, nbr, false /*was_static*/); } return 0; } -static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) +static void zevpn_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) { struct mac_walk_ctx *wctx = ctxt; zebra_mac_t *mac; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct listnode *node = NULL; zebra_neigh_t *nbr = NULL; @@ -7197,7 +3075,7 @@ static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) if (!mac) return; - zvni = wctx->zvni; + zevpn = wctx->zevpn; if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) return; @@ -7224,87 +3102,50 @@ static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) /* Local: Notify Peer VTEPs, Remote: Install the entry */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { /* Inform to BGP */ - if (zvni_mac_send_add_to_client(zvni->vni, - &mac->macaddr, - mac->flags, mac->loc_seq)) + if (zebra_evpn_mac_send_add_to_client(zevpn->vni, &mac->macaddr, + mac->flags, mac->loc_seq, + mac->es)) return; /* Process all neighbors associated with this MAC. */ - zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + zebra_evpn_process_neigh_on_local_mac_change(zevpn, mac, 0, + 0 /*es_change*/); } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); + zebra_evpn_process_neigh_on_remote_mac_add(zevpn, mac); /* Install the entry. */ - zvni_mac_install(zvni, mac); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); } } -static void zvni_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt) -{ - struct neigh_walk_ctx *wctx = ctxt; - zebra_neigh_t *nbr; - zebra_vni_t *zvni; - char buf[INET6_ADDRSTRLEN]; - - nbr = (zebra_neigh_t *)bucket->data; - if (!nbr) - return; - - zvni = wctx->zvni; - - if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) - return; - - if (IS_ZEBRA_DEBUG_VXLAN) { - ipaddr2str(&nbr->ip, buf, sizeof(buf)); - zlog_debug("%s: clear neigh %s dup state, flags 0x%x seq %u", - __func__, buf, nbr->flags, nbr->loc_seq); - } - - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->detect_start_time.tv_usec = 0; - nbr->dad_dup_detect_time = 0; - THREAD_OFF(nbr->dad_ip_auto_recovery_timer); - - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { - zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, - &nbr->emac, - nbr->flags, nbr->loc_seq); - } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_neigh_install(zvni, nbr); - } -} - -static void zvni_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket, +static void zevpn_clear_dup_detect_hash_vni_all(struct hash_bucket *bucket, void **args) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct zebra_vrf *zvrf; struct mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; - zvni = (zebra_vni_t *)bucket->data; - if (!zvni) + zevpn = (zebra_evpn_t *)bucket->data; + if (!zevpn) return; zvrf = (struct zebra_vrf *)args[0]; - if (hashcount(zvni->neigh_table)) { + if (hashcount(zevpn->neigh_table)) { memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; + n_wctx.zevpn = zevpn; n_wctx.zvrf = zvrf; - hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, - &n_wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_clear_dup_neigh_hash, &n_wctx); } - if (num_valid_macs(zvni)) { + if (num_valid_macs(zevpn)) { memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; + m_wctx.zevpn = zevpn; m_wctx.zvrf = zvrf; - hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx); } } @@ -7318,41 +3159,41 @@ int zebra_vxlan_clear_dup_detect_vni_all(struct zebra_vrf *zvrf) args[0] = zvrf; - hash_iterate(zvrf->vni_table, + hash_iterate(zvrf->evpn_table, (void (*)(struct hash_bucket *, void *)) - zvni_clear_dup_detect_hash_vni_all, args); + zevpn_clear_dup_detect_hash_vni_all, args); return 0; } int zebra_vxlan_clear_dup_detect_vni(struct zebra_vrf *zvrf, vni_t vni) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; if (!is_evpn_enabled()) return 0; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_warn("VNI %u does not exist\n", vni); - return -1; + return CMD_WARNING; } - if (hashcount(zvni->neigh_table)) { + if (hashcount(zevpn->neigh_table)) { memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; + n_wctx.zevpn = zevpn; n_wctx.zvrf = zvrf; - hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, - &n_wctx); + hash_iterate(zevpn->neigh_table, + zebra_evpn_clear_dup_neigh_hash, &n_wctx); } - if (num_valid_macs(zvni)) { + if (num_valid_macs(zevpn)) { memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; + m_wctx.zevpn = zevpn; m_wctx.zvrf = zvrf; - hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + hash_iterate(zevpn->mac_table, zevpn_clear_dup_mac_hash, &m_wctx); } return 0; @@ -7365,7 +3206,7 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct in_addr vtep_ip, bool use_json) { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; uint32_t num_macs; struct mac_walk_ctx wctx; json_object *json = NULL; @@ -7373,15 +3214,15 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, if (!is_evpn_enabled()) return; - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (use_json) vty_out(vty, "{}\n"); else vty_out(vty, "%% VNI %u does not exist\n", vni); return; } - num_macs = num_valid_macs(zvni); + num_macs = num_valid_macs(zevpn); if (!num_macs) return; @@ -7391,12 +3232,12 @@ void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, } memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; + wctx.zevpn = zevpn; wctx.vty = vty; wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP; wctx.r_vtep_ip = vtep_ip; wctx.json = json_mac; - hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx); + hash_iterate(zevpn->mac_table, zebra_evpn_print_mac_hash, &wctx); if (use_json) { json_object_int_add(json, "numMacs", wctx.count); @@ -7423,7 +3264,7 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, json_object *json = NULL; void *args[2]; zebra_l3vni_t *zl3vni = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; if (!is_evpn_enabled()) return; @@ -7438,9 +3279,9 @@ void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, if (zl3vni) { zl3vni_print(zl3vni, (void *)args); } else { - zvni = zvni_lookup(vni); - if (zvni) - zvni_print(zvni, (void *)args); + zevpn = zebra_evpn_lookup(vni); + if (zevpn) + zebra_evpn_print(zevpn, (void *)args); else if (!json) vty_out(vty, "%% VNI %u does not exist\n", vni); } @@ -7478,7 +3319,7 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) return; num_l3vnis = hashcount(zrouter.l3vni_table); - num_l2vnis = hashcount(zvrf->vni_table); + num_l2vnis = hashcount(zvrf->evpn_table); num_vnis = num_l2vnis + num_l3vnis; if (uj) { @@ -7550,9 +3391,10 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, args[1] = json; /* Display all L2-VNIs */ - hash_iterate(zvrf->vni_table, - (void (*)(struct hash_bucket *, void *))zvni_print_hash, - args); + hash_iterate( + zvrf->evpn_table, + (void (*)(struct hash_bucket *, void *))zebra_evpn_print_hash, + args); /* Display all L3-VNIs */ hash_iterate(zrouter.l3vni_table, @@ -7616,7 +3458,7 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, { json_object *json_array = NULL; struct zebra_ns *zns = NULL; - struct zvni_evpn_show zes; + struct zebra_evpn_show zes; if (!is_evpn_enabled()) return; @@ -7634,10 +3476,10 @@ void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, zes.use_json = use_json; /* Display all L2-VNIs */ - hash_iterate( - zvrf->vni_table, - (void (*)(struct hash_bucket *, void *))zvni_print_hash_detail, - &zes); + hash_iterate(zvrf->evpn_table, + (void (*)(struct hash_bucket *, + void *))zebra_evpn_print_hash_detail, + &zes); /* Display all L3-VNIs */ hash_iterate(zrouter.l3vni_table, @@ -7664,12 +3506,8 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, struct ipaddr *ip) { char buf[INET6_ADDRSTRLEN]; - char buf2[ETHER_ADDR_STRLEN]; - zebra_neigh_t *n = NULL; - zebra_vni_t *zvni = NULL; - zebra_mac_t *zmac = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; - struct zebra_vrf *zvrf; /* check if this is a remote neigh entry corresponding to remote * next-hop @@ -7681,80 +3519,29 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_from_svi(ifp, link_if); - if (!zvni) { + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (!zevpn) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "%s: Del neighbor %s VNI is not present for interface %s", + "%s: Del neighbor %s EVPN is not present for interface %s", __func__, ipaddr2str(ip, buf, sizeof(buf)), ifp->name); return 0; } - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug( "VNI %u hash %p doesn't have intf upon local neighbor DEL", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Del neighbor %s intf %s(%u) -> L2-VNI %u", ipaddr2str(ip, buf, sizeof(buf)), ifp->name, - ifp->ifindex, zvni->vni); - - /* If entry doesn't exist, nothing to do. */ - n = zvni_neigh_lookup(zvni, ip); - if (!n) - return 0; + ifp->ifindex, zevpn->vni); - zmac = zvni_mac_lookup(zvni, &n->emac); - if (!zmac) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "Trying to del a neigh %s without a mac %s on VNI %u", - ipaddr2str(ip, buf, sizeof(buf)), - prefix_mac2str(&n->emac, buf2, sizeof(buf2)), - zvni->vni); - - return 0; - } - - /* If it is a remote entry, the kernel has aged this out or someone has - * deleted it, it needs to be re-installed as Quagga is the owner. - */ - if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_neigh_install(zvni, n); - return 0; - } - - zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); - if (!zvrf) { - zlog_debug("%s: VNI %u vrf lookup failed.", __func__, - zvni->vni); - return -1; - } - - /* In case of feeze action, if local neigh is in duplicate state, - * Mark the Neigh as inactive before sending delete request to BGPd, - * If BGPd has remote entry, it will re-install - */ - if (zvrf->dad_freeze && - CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) - ZEBRA_NEIGH_SET_INACTIVE(n); - - /* Remove neighbor from BGP. */ - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac, 0, n->state); - - /* Delete this neighbor entry. */ - zvni_neigh_del(zvni, n); - - /* see if the AUTO mac needs to be deleted */ - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO) - && !listcount(zmac->neigh_list)) - zvni_mac_del(zvni, zmac); - - return 0; + return zebra_evpn_neigh_del_ip(zevpn, ip); } /* @@ -7769,11 +3556,12 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp, struct ethaddr *macaddr, uint16_t state, bool is_ext, - bool is_router) + bool is_router, + bool local_inactive, bool dp_static) { char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* check if this is a remote neigh entry corresponding to remote @@ -7786,32 +3574,34 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp, /* We are only interested in neighbors on an SVI that resides on top * of a VxLAN bridge. */ - zvni = zvni_from_svi(ifp, link_if); - if (!zvni) + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (!zevpn) return 0; - if (IS_ZEBRA_DEBUG_VXLAN) + if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_NEIGH) zlog_debug( - "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s %s-> L2-VNI %u", + "Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x %s%s%s-> L2-VNI %u", ipaddr2str(ip, buf2, sizeof(buf2)), prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, state, is_ext ? "ext-learned " : "", is_router ? "router " : "", - zvni->vni); + local_inactive ? "local_inactive " : "", + zevpn->vni); /* Is this about a local neighbor or a remote one? */ if (!is_ext) - return zvni_local_neigh_update(zvni, ifp, ip, macaddr, - is_router); + return zebra_evpn_local_neigh_update(zevpn, ifp, ip, macaddr, + is_router, local_inactive, + dp_static); - return zvni_remote_neigh_update(zvni, ifp, ip, macaddr, state); + return zebra_evpn_remote_neigh_update(zevpn, ifp, ip, macaddr, state); } static int32_t zebra_vxlan_remote_macip_helper(bool add, struct stream *s, vni_t *vni, struct ethaddr *macaddr, uint16_t *ipa_len, struct ipaddr *ip, struct in_addr *vtep_ip, - uint8_t *flags, uint32_t *seq) + uint8_t *flags, uint32_t *seq, esi_t *esi) { uint16_t l = 0; @@ -7849,6 +3639,8 @@ zebra_vxlan_remote_macip_helper(bool add, struct stream *s, vni_t *vni, STREAM_GETC(s, *flags); STREAM_GETL(s, *seq); l += 5; + STREAM_GET(esi, s, sizeof(esi_t)); + l += sizeof(esi_t); } return l; @@ -7880,7 +3672,7 @@ void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS) while (l < hdr->length) { int res_length = zebra_vxlan_remote_macip_helper( false, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip, NULL, - NULL); + NULL, NULL); if (res_length == -1) goto stream_failure; @@ -7921,6 +3713,8 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS) uint32_t seq; char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; + esi_t esi; + char esi_buf[ESI_STR_LEN]; memset(&macaddr, 0, sizeof(struct ethaddr)); memset(&ip, 0, sizeof(struct ipaddr)); @@ -7936,25 +3730,32 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS) while (l < hdr->length) { int res_length = zebra_vxlan_remote_macip_helper( true, s, &vni, &macaddr, &ipa_len, &ip, &vtep_ip, - &flags, &seq); + &flags, &seq, &esi); if (res_length == -1) goto stream_failure; l += res_length; - if (IS_ZEBRA_DEBUG_VXLAN) + if (IS_ZEBRA_DEBUG_VXLAN) { + if (memcmp(&esi, zero_esi, sizeof(esi_t))) + esi_to_str(&esi, esi_buf, sizeof(esi_buf)); + else + strlcpy(esi_buf, "-", ESI_STR_LEN); zlog_debug( - "Recv MACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s from %s", + "Recv %sMACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s ESI %s from %s", + (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) ? + "sync-" : "", vni, prefix_mac2str(&macaddr, buf, sizeof(buf)), ipa_len ? " IP " : "", ipa_len ? ipaddr2str(&ip, buf1, sizeof(buf1)) : "", - flags, seq, inet_ntoa(vtep_ip), + flags, seq, inet_ntoa(vtep_ip), esi_buf, zebra_route_string(client->proto)); + } process_remote_macip_add(vni, &macaddr, ipa_len, &ip, - flags, seq, vtep_ip); + flags, seq, vtep_ip, &esi); } stream_failure: @@ -7971,7 +3772,7 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp, struct zebra_vrf *zvrf = NULL; struct zebra_l2info_vxlan *vxl; vni_t vni; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_vtep_t *zvtep = NULL; zif = ifp->info; @@ -7989,12 +3790,12 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp, return -1; /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; /* If the remote vtep entry doesn't exists nothing to do */ - zvtep = zvni_vtep_find(zvni, &vtep_ip); + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); if (!zvtep) return 0; @@ -8003,7 +3804,7 @@ int zebra_vxlan_check_readd_vtep(struct interface *ifp, "Del MAC for remote VTEP %s intf %s(%u) VNI %u - readd", inet_ntoa(vtep_ip), ifp->name, ifp->ifindex, vni); - zvni_vtep_install(zvni, zvtep); + zebra_evpn_vtep_install(zevpn, zvtep); return 0; } @@ -8019,7 +3820,7 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, struct zebra_if *zif; struct zebra_l2info_vxlan *vxl; vni_t vni; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_mac_t *mac; char buf[ETHER_ADDR_STRLEN]; @@ -8033,12 +3834,12 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, return 0; /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; /* If entry doesn't exist, nothing to do. */ - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) return 0; @@ -8053,16 +3854,17 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, ifp->ifindex, vni, mac->flags); /* Remove MAC from BGP. */ - zvni_mac_send_del_to_client(zvni->vni, macaddr); + zebra_evpn_mac_send_del_to_client(zevpn->vni, macaddr, mac->flags, + false /* force */); /* * If there are no neigh associated with the mac delete the mac * else mark it as AUTO for forward reference */ if (!listcount(mac->neigh_list)) { - zvni_mac_del(zvni, mac); + zebra_evpn_mac_del(zevpn, mac); } else { - UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); + UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_LOCAL_FLAGS); UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); } @@ -8082,7 +3884,7 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; vni_t vni; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; zebra_mac_t *mac = NULL; char buf[ETHER_ADDR_STRLEN]; @@ -8102,12 +3904,12 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, return zebra_vxlan_readd_remote_rmac(zl3vni, macaddr); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return 0; /* If entry doesn't exist, nothing to do. */ - mac = zvni_mac_lookup(zvni, macaddr); + mac = zebra_evpn_mac_lookup(zevpn, macaddr); if (!mac) return 0; @@ -8120,7 +3922,7 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, vni); - zvni_mac_install(zvni, mac); + zebra_evpn_rem_mac_install(zevpn, mac, false /* was_static */); return 0; } @@ -8130,57 +3932,22 @@ int zebra_vxlan_check_readd_remote_mac(struct interface *ifp, int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, struct ethaddr *macaddr, vlanid_t vid) { - zebra_vni_t *zvni; - zebra_mac_t *mac; - char buf[ETHER_ADDR_STRLEN]; + zebra_evpn_t *zevpn; /* We are interested in MACs only on ports or (port, VLAN) that * map to a VNI. */ - zvni = zvni_map_vlan(ifp, br_if, vid); - if (!zvni) + zevpn = zebra_evpn_map_vlan(ifp, br_if, vid); + if (!zevpn) return 0; - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug( "VNI %u hash %p doesn't have intf upon local MAC DEL", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } - /* If entry doesn't exist, nothing to do. */ - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) - return 0; - - /* Is it a local entry? */ - if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) - return 0; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug("DEL MAC %s intf %s(%u) VID %u -> VNI %u seq %u flags 0x%x nbr count %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, - ifp->ifindex, vid, zvni->vni, mac->loc_seq, - mac->flags, listcount(mac->neigh_list)); - - /* Update all the neigh entries associated with this mac */ - zvni_process_neigh_on_local_mac_del(zvni, mac); - - /* Remove MAC from BGP. */ - zvni_mac_send_del_to_client(zvni->vni, macaddr); - - /* - * If there are no neigh associated with the mac delete the mac - * else mark it as AUTO for forward reference - */ - if (!listcount(mac->neigh_list)) { - zvni_mac_del(zvni, mac); - } else { - UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - } - - return 0; + return zebra_evpn_del_local_mac(zevpn, macaddr, ifp); } /* @@ -8189,201 +3956,52 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, int zebra_vxlan_local_mac_add_update(struct interface *ifp, struct interface *br_if, struct ethaddr *macaddr, vlanid_t vid, - bool sticky) + bool sticky, bool local_inactive, + bool dp_static) { - zebra_vni_t *zvni; - zebra_mac_t *mac; + zebra_evpn_t *zevpn; struct zebra_vrf *zvrf; char buf[ETHER_ADDR_STRLEN]; - bool mac_sticky = false; - bool inform_client = false; - bool upd_neigh = false; - bool is_dup_detect = false; - struct in_addr vtep_ip = {.s_addr = 0}; - ns_id_t local_ns_id = NS_DEFAULT; - zvrf = zebra_vrf_lookup_by_id(ifp->vrf_id); - if (zvrf && zvrf->zns) - local_ns_id = zvrf->zns->ns_id; + assert(ifp); /* We are interested in MACs only on ports or (port, VLAN) that - * map to a VNI. + * map to an EVPN. */ - zvni = zvni_map_vlan(ifp, br_if, vid); - if (!zvni) { + zevpn = zebra_evpn_map_vlan(ifp, br_if, vid); + if (!zevpn) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - " Add/Update %sMAC %s intf %s(%u) VID %u, could not find VNI", + " Add/Update %sMAC %s intf %s(%u) VID %u, could not find EVPN", sticky ? "sticky " : "", prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name, ifp->ifindex, vid); return 0; } - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( " VNI %u hash %p doesn't have intf upon local MAC ADD", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } - zvrf = zebra_vrf_get_evpn(); + zvrf = vrf_info_lookup(zevpn->vxlan_if->vrf_id); if (!zvrf) { if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug(" No Evpn Global Vrf found"); + zlog_debug(" No Vrf found for vrf_id: %d", + zevpn->vxlan_if->vrf_id); return -1; } - /* Check if we need to create or update or it is a NO-OP. */ - mac = zvni_mac_lookup(zvni, macaddr); - if (!mac) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid, zvni->vni); - - mac = zvni_mac_add(zvni, macaddr); - if (!mac) { - flog_err( - EC_ZEBRA_MAC_ADD_FAILED, - "Failed to add MAC %s intf %s(%u) VID %u VNI %u", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid, zvni->vni); - return -1; - } - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.ns_id = local_ns_id; - mac->fwd_info.local.vid = vid; - if (sticky) - SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - inform_client = true; - - } else { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u curFlags 0x%x", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, sizeof(buf)), - ifp->name, ifp->ifindex, vid, zvni->vni, - mac->flags); - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) - mac_sticky = true; - - /* - * Update any changes and if changes are relevant to - * BGP, note it. - */ - if (mac_sticky == sticky - && mac->fwd_info.local.ifindex == ifp->ifindex - && mac->fwd_info.local.ns_id == local_ns_id - && mac->fwd_info.local.vid == vid) { - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - " Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u, " - "entry exists and has not changed ", - sticky ? "sticky " : "", - prefix_mac2str(macaddr, buf, - sizeof(buf)), - ifp->name, ifp->ifindex, vid, - zvni->vni); - return 0; - } - if (mac_sticky != sticky) { - if (sticky) - SET_FLAG(mac->flags, - ZEBRA_MAC_STICKY); - else - UNSET_FLAG(mac->flags, - ZEBRA_MAC_STICKY); - inform_client = true; - } - - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.ns_id = local_ns_id; - mac->fwd_info.local.vid = vid; - - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) || - CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { - bool do_dad = false; - - /* - * MAC has either moved or was "internally" created due - * to a neighbor learn and is now actually learnt. If - * it was learnt as a remote sticky MAC, this is an - * operator error. - */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) { - flog_warn( - EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT, - "MAC %s already learnt as remote sticky MAC behind VTEP %s VNI %u", - prefix_mac2str(macaddr, buf, - sizeof(buf)), - inet_ntoa(mac->fwd_info.r_vtep_ip), - zvni->vni); - return 0; - } - - /* If an actual move, compute MAC's seq number */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - mac->loc_seq = MAX(mac->rem_seq + 1, - mac->loc_seq); - vtep_ip = mac->fwd_info.r_vtep_ip; - /* Trigger DAD for remote MAC */ - do_dad = true; - } - - UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); - UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); - SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); - memset(&mac->fwd_info, 0, sizeof(mac->fwd_info)); - mac->fwd_info.local.ifindex = ifp->ifindex; - mac->fwd_info.local.ns_id = local_ns_id; - mac->fwd_info.local.vid = vid; - if (sticky) - SET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - else - UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY); - /* - * We have to inform BGP of this MAC as well as process - * all neighbors. - */ - inform_client = true; - upd_neigh = true; - - zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, vtep_ip, - do_dad, - &is_dup_detect, - true); - if (is_dup_detect) { - inform_client = false; - upd_neigh = false; - } - } - } - - /* Inform BGP if required. */ - if (inform_client) { - if (zvni_mac_send_add_to_client(zvni->vni, macaddr, - mac->flags, mac->loc_seq)) - return -1; - } - - /* Process all neighbors associated with this MAC, if required. */ - if (upd_neigh) - zvni_process_neigh_on_local_mac_change(zvni, mac, 0); - - return 0; + return zebra_evpn_add_update_local_mac(zvrf, zevpn, ifp, macaddr, vid, + sticky, local_inactive, + dp_static); } /* - * Handle message from client to delete a remote VTEP for a VNI. + * Handle message from client to delete a remote VTEP for an EVPN. */ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) { @@ -8391,7 +4009,7 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) unsigned short l = 0; vni_t vni; struct in_addr vtep_ip; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_vtep_t *zvtep; struct interface *ifp; struct zebra_if *zif; @@ -8430,21 +4048,20 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) zebra_route_string(client->proto)); /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( - "Failed to locate VNI hash upon remote VTEP DEL, " - "VNI %u", + "Failed to locate VNI hash upon remote VTEP DEL, VNI %u", vni); continue; } - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) { zlog_debug( "VNI %u hash %p doesn't have intf upon remote VTEP DEL", - zvni->vni, zvni); + zevpn->vni, zevpn); continue; } zif = ifp->info; @@ -8459,12 +4076,12 @@ void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS) * and * then, the VTEP entry itself and remove it. */ - zvtep = zvni_vtep_find(zvni, &vtep_ip); + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); if (!zvtep) continue; - zvni_vtep_uninstall(zvni, &vtep_ip); - zvni_vtep_del(zvni, zvtep); + zebra_evpn_vtep_uninstall(zevpn, &vtep_ip); + zebra_evpn_vtep_del(zevpn, zvtep); } stream_failure: @@ -8472,7 +4089,7 @@ stream_failure: } /* - * Handle message from client to add a remote VTEP for a VNI. + * Handle message from client to add a remote VTEP for an EVPN. */ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) { @@ -8480,7 +4097,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) unsigned short l = 0; vni_t vni; struct in_addr vtep_ip; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; struct interface *ifp; struct zebra_if *zif; int flood_control; @@ -8515,21 +4132,21 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) zebra_route_string(client->proto)); /* Locate VNI hash entry - expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { flog_err( EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to locate VNI hash upon remote VTEP ADD, VNI %u", + "Failed to locate EVPN hash upon remote VTEP ADD, VNI %u", vni); continue; } - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) { flog_err( EC_ZEBRA_VTEP_ADD_FAILED, "VNI %u hash %p doesn't have intf upon remote VTEP ADD", - zvni->vni, zvni); + zevpn->vni, zevpn); continue; } @@ -8539,7 +4156,7 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) if (!if_is_operative(ifp) || !zif->brslave_info.br_if) continue; - zvtep = zvni_vtep_find(zvni, &vtep_ip); + zvtep = zebra_evpn_vtep_find(zevpn, &vtep_ip); if (zvtep) { /* If the remote VTEP already exists check if * the flood mode has changed @@ -8551,18 +4168,20 @@ void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS) * is no longer; get rid of the HER fdb * entry installed before */ - zvni_vtep_uninstall(zvni, &vtep_ip); + zebra_evpn_vtep_uninstall(zevpn, + &vtep_ip); zvtep->flood_control = flood_control; - zvni_vtep_install(zvni, zvtep); + zebra_evpn_vtep_install(zevpn, zvtep); } } else { - zvtep = zvni_vtep_add(zvni, &vtep_ip, flood_control); + zvtep = zebra_evpn_vtep_add(zevpn, &vtep_ip, + flood_control); if (zvtep) - zvni_vtep_install(zvni, zvtep); + zebra_evpn_vtep_install(zevpn, zvtep); else flog_err(EC_ZEBRA_VTEP_ADD_FAILED, - "Failed to add remote VTEP, VNI %u zvni %p", - vni, zvni); + "Failed to add remote VTEP, VNI %u zevpn %p", + vni, zevpn); } } @@ -8583,7 +4202,7 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, { struct ipaddr ip; struct ethaddr macaddr; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; memset(&ip, 0, sizeof(struct ipaddr)); memset(&macaddr, 0, sizeof(struct ethaddr)); @@ -8627,14 +4246,15 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, svi_if_link = if_lookup_by_index_per_ns( zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); - zvni = zvni_from_svi(svi_if, svi_if_link); + zevpn = zebra_evpn_from_svi(svi_if, + svi_if_link); } } else if (IS_ZEBRA_IF_BRIDGE(svi_if)) { /* * If it is a vlan unaware bridge then svi is the bridge * itself */ - zvni = zvni_from_svi(svi_if, svi_if); + zevpn = zebra_evpn_from_svi(svi_if, svi_if); } } else if (IS_ZEBRA_IF_VLAN(ifp)) { struct zebra_if *svi_if_zif = @@ -8648,18 +4268,18 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, zebra_ns_lookup(NS_DEFAULT), svi_if_zif->link_ifindex); if (svi_if_link) - zvni = zvni_from_svi(ifp, svi_if_link); + zevpn = zebra_evpn_from_svi(ifp, svi_if_link); } } else if (IS_ZEBRA_IF_BRIDGE(ifp)) { - zvni = zvni_from_svi(ifp, ifp); + zevpn = zebra_evpn_from_svi(ifp, ifp); } - if (!zvni) + if (!zevpn) return 0; - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug("VNI %u hash %p doesn't have intf upon MACVLAN up", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } @@ -8678,9 +4298,9 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, if (add) - zvni_gw_macip_add(ifp, zvni, &macaddr, &ip); + zebra_evpn_gw_macip_add(ifp, zevpn, &macaddr, &ip); else - zvni_gw_macip_del(ifp, zvni, &ip); + zebra_evpn_gw_macip_del(ifp, zevpn, &ip); return 0; } @@ -8690,7 +4310,7 @@ int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p, * SVI can be associated to either L3-VNI or L2-VNI. * For L2-VNI: At this point, this is a NOP since * the kernel deletes the neighbor entries on this SVI (if any). - * We only need to update the vrf corresponding to zvni. + * We only need to update the vrf corresponding to zevpn. * For L3-VNI: L3-VNI is operationally down, update mac-ip routes and delete * from bgp */ @@ -8707,17 +4327,17 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) /* remove association with svi-if */ zl3vni->svi_if = NULL; } else { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; - /* since we dont have svi corresponding to zvni, we associate it + /* since we dont have svi corresponding to zevpn, we associate it * to default vrf. Note: the corresponding neigh entries on the * SVI would have already been deleted */ - zvni = zvni_from_svi(ifp, link_if); - if (zvni) { - zvni->vrf_id = VRF_DEFAULT; + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (zevpn) { + zevpn->vrf_id = VRF_DEFAULT; /* update the tenant vrf in BGP */ - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); } } return 0; @@ -8733,7 +4353,7 @@ int zebra_vxlan_svi_down(struct interface *ifp, struct interface *link_if) */ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) { - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; zl3vni = zl3vni_from_svi(ifp, link_if); @@ -8750,31 +4370,31 @@ int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if) /* process SVI up for l2-vni */ struct neigh_walk_ctx n_wctx; - zvni = zvni_from_svi(ifp, link_if); - if (!zvni) + zevpn = zebra_evpn_from_svi(ifp, link_if); + if (!zevpn) return 0; - if (!zvni->vxlan_if) { + if (!zevpn->vxlan_if) { zlog_debug( "VNI %u hash %p doesn't have intf upon SVI up", - zvni->vni, zvni); + zevpn->vni, zevpn); return -1; } if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "SVI %s(%u) VNI %u VRF %s is UP, installing neighbors", - ifp->name, ifp->ifindex, zvni->vni, + ifp->name, ifp->ifindex, zevpn->vni, vrf_id_to_name(ifp->vrf_id)); /* update the vrf information for l2-vni and inform bgp */ - zvni->vrf_id = ifp->vrf_id; - zvni_send_add_to_client(zvni); + zevpn->vrf_id = ifp->vrf_id; + zebra_evpn_send_add_to_client(zevpn); /* Install any remote neighbors for this VNI. */ memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, + n_wctx.zevpn = zevpn; + hash_iterate(zevpn->neigh_table, zebra_evpn_install_neigh_hash, &n_wctx); } @@ -8861,7 +4481,7 @@ int zebra_vxlan_if_down(struct interface *ifp) struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; zebra_l3vni_t *zl3vni = NULL; - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; /* Check if EVPN is enabled. */ if (!is_evpn_enabled()) @@ -8887,25 +4507,25 @@ int zebra_vxlan_if_down(struct interface *ifp) ifp->ifindex, vni); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( "Failed to locate VNI hash at DOWN, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } - assert(zvni->vxlan_if == ifp); + assert(zevpn->vxlan_if == ifp); /* Delete this VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + zebra_evpn_send_del_to_client(zevpn); /* Free up all neighbors and MACs, if any. */ - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 1); + zebra_evpn_vtep_del_all(zevpn, 1); } return 0; } @@ -8918,7 +4538,7 @@ int zebra_vxlan_if_up(struct interface *ifp) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -8956,29 +4576,29 @@ int zebra_vxlan_if_up(struct interface *ifp) ifp->ifindex, vni); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( - "Failed to locate VNI hash at UP, IF %s(%u) VNI %u", + "Failed to locate EVPN hash at UP, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } - assert(zvni->vxlan_if == ifp); + assert(zevpn->vxlan_if == ifp); vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { - zvni->vrf_id = vlan_if->vrf_id; + zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) - listnode_add_sort(zl3vni->l2vnis, zvni); + listnode_add_sort(zl3vni->l2vnis, zevpn); } /* If part of a bridge, inform BGP about this VNI. */ /* Also, read and populate local MACs and neighbors. */ if (zif->brslave_info.br_if) { - zvni_send_add_to_client(zvni); - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_send_add_to_client(zevpn); + zebra_evpn_read_mac_neigh(zevpn, ifp); } } @@ -8994,7 +4614,7 @@ int zebra_vxlan_if_del(struct interface *ifp) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -9027,8 +4647,8 @@ int zebra_vxlan_if_del(struct interface *ifp) ifp->ifindex); /* Locate hash entry; it is expected to exist. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( "Failed to locate VNI hash at del, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); @@ -9036,25 +4656,24 @@ int zebra_vxlan_if_del(struct interface *ifp) } /* remove from l3-vni list */ - zl3vni = zl3vni_from_vrf(zvni->vrf_id); + zl3vni = zl3vni_from_vrf(zevpn->vrf_id); if (zl3vni) - listnode_delete(zl3vni->l2vnis, zvni); - + listnode_delete(zl3vni->l2vnis, zevpn); /* Delete VNI from BGP. */ - zvni_send_del_to_client(zvni->vni); + zebra_evpn_send_del_to_client(zevpn); /* Free up all neighbors and MAC, if any. */ - zvni_neigh_del_all(zvni, 0, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 0, 0, DEL_ALL_MAC); + zebra_evpn_neigh_del_all(zevpn, 0, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 0, 0, DEL_ALL_MAC); /* Free up all remote VTEPs, if any. */ - zvni_vtep_del_all(zvni, 0); + zebra_evpn_vtep_del_all(zevpn, 0); /* Delete the hash entry. */ - if (zvni_del(zvni)) { + if (zebra_evpn_vxlan_del(zevpn)) { flog_err(EC_ZEBRA_VNI_DEL_FAILED, - "Failed to del VNI hash %p, IF %s(%u) VNI %u", - zvni, ifp->name, ifp->ifindex, zvni->vni); + "Failed to del EVPN hash %p, IF %s(%u) VNI %u", + zevpn, ifp->name, ifp->ifindex, zevpn->vni); return -1; } } @@ -9069,7 +4688,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -9140,10 +4759,10 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) } else { /* Update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { zlog_debug( - "Failed to find L2-VNI hash on update, IF %s(%u) VNI %u", + "Failed to find EVPN hash on update, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } @@ -9160,10 +4779,10 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) && (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL)) { /* Delete from client, remove all remote VTEPs */ /* Also, free up all MACs and neighbors. */ - zvni_send_del_to_client(zvni->vni); - zvni_neigh_del_all(zvni, 1, 0, DEL_ALL_NEIGH); - zvni_mac_del_all(zvni, 1, 0, DEL_ALL_MAC); - zvni_vtep_del_all(zvni, 1); + zebra_evpn_send_del_to_client(zevpn); + zebra_evpn_neigh_del_all(zevpn, 1, 0, DEL_ALL_NEIGH); + zebra_evpn_mac_del_all(zevpn, 1, 0, DEL_ALL_MAC); + zebra_evpn_vtep_del_all(zevpn, 1); return 0; } @@ -9172,20 +4791,23 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) /* Remove all existing local neigh and MACs for this VNI * (including from BGP) */ - zvni_neigh_del_all(zvni, 0, 1, DEL_LOCAL_MAC); - zvni_mac_del_all(zvni, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_neigh_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); + zebra_evpn_mac_del_all(zevpn, 0, 1, DEL_LOCAL_MAC); } - if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref(zvni->local_vtep_ip, - zvni->mcast_grp); + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->mcast_grp = vxl->mcast_grp; + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vxl->mcast_grp; + /* on local vtep-ip check if ES orig-ip + * needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); } - zvni->vxlan_if = ifp; - + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); /* Take further actions needed. * Note that if we are here, there is a change of interest. */ @@ -9198,7 +4820,7 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE | ZEBRA_VXLIF_MCAST_GRP_CHANGE)) - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); /* If there is a valid new master or a VLAN mapping change, * read and populate local MACs and neighbors. @@ -9206,22 +4828,22 @@ int zebra_vxlan_if_update(struct interface *ifp, uint16_t chgflags) * for this VNI (based on new VLAN). */ if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_read_mac_neigh(zevpn, ifp); else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE) { struct mac_walk_ctx m_wctx; struct neigh_walk_ctx n_wctx; - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_read_mac_neigh(zevpn, ifp); memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); - m_wctx.zvni = zvni; - hash_iterate(zvni->mac_table, zvni_install_mac_hash, - &m_wctx); + m_wctx.zevpn = zevpn; + hash_iterate(zevpn->mac_table, + zebra_evpn_install_mac_hash, &m_wctx); memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); - n_wctx.zvni = zvni; - hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, - &n_wctx); + n_wctx.zevpn = zevpn; + hash_iterate(zevpn->neigh_table, + zebra_evpn_install_neigh_hash, &n_wctx); } } @@ -9236,7 +4858,7 @@ int zebra_vxlan_if_add(struct interface *ifp) vni_t vni; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan *vxl = NULL; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; zebra_l3vni_t *zl3vni = NULL; /* Check if EVPN is enabled. */ @@ -9276,35 +4898,39 @@ int zebra_vxlan_if_add(struct interface *ifp) /* process if-add for l2-vni */ struct interface *vlan_if = NULL; - /* Create or update VNI hash. */ - zvni = zvni_lookup(vni); - if (!zvni) { - zvni = zvni_add(vni); - if (!zvni) { + /* Create or update EVPN hash. */ + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) { + zevpn = zebra_evpn_add(vni); + if (!zevpn) { flog_err( EC_ZEBRA_VNI_ADD_FAILED, - "Failed to add VNI hash, IF %s(%u) VNI %u", + "Failed to add EVPN hash, IF %s(%u) VNI %u", ifp->name, ifp->ifindex, vni); return -1; } } - if (zvni->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || - zvni->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { - zebra_vxlan_sg_deref(zvni->local_vtep_ip, - zvni->mcast_grp); + if (zevpn->local_vtep_ip.s_addr != vxl->vtep_ip.s_addr || + zevpn->mcast_grp.s_addr != vxl->mcast_grp.s_addr) { + zebra_vxlan_sg_deref(zevpn->local_vtep_ip, + zevpn->mcast_grp); zebra_vxlan_sg_ref(vxl->vtep_ip, vxl->mcast_grp); - zvni->local_vtep_ip = vxl->vtep_ip; - zvni->mcast_grp = vxl->mcast_grp; + zevpn->local_vtep_ip = vxl->vtep_ip; + zevpn->mcast_grp = vxl->mcast_grp; + /* on local vtep-ip check if ES orig-ip + * needs to be updated + */ + zebra_evpn_es_set_base_evpn(zevpn); } - zvni->vxlan_if = ifp; + zevpn_vxlan_if_set(zevpn, ifp, true /* set */); vlan_if = zvni_map_to_svi(vxl->access_vlan, zif->brslave_info.br_if); if (vlan_if) { - zvni->vrf_id = vlan_if->vrf_id; + zevpn->vrf_id = vlan_if->vrf_id; zl3vni = zl3vni_from_vrf(vlan_if->vrf_id); if (zl3vni) - listnode_add_sort(zl3vni->l2vnis, zvni); + listnode_add_sort(zl3vni->l2vnis, zevpn); } if (IS_ZEBRA_DEBUG_VXLAN) { @@ -9331,10 +4957,10 @@ int zebra_vxlan_if_add(struct interface *ifp) return 0; /* Inform BGP */ - zvni_send_add_to_client(zvni); + zebra_evpn_send_add_to_client(zevpn); /* Read and populate local MACs and neighbors */ - zvni_read_mac_neigh(zvni, ifp); + zebra_evpn_read_mac_neigh(zevpn, ifp); } return 0; @@ -9412,7 +5038,7 @@ int zebra_vxlan_process_vrf_vni_cmd(struct zebra_vrf *zvrf, vni_t vni, : "NIL"); /* formulate l2vni list */ - hash_iterate(zvrf_evpn->vni_table, zvni_add_to_l3vni_list, + hash_iterate(zvrf_evpn->evpn_table, zevpn_add_to_l3vni_list, zl3vni); if (is_l3vni_oper_up(zl3vni)) @@ -9480,8 +5106,15 @@ int zebra_vxlan_vrf_disable(struct zebra_vrf *zvrf) if (!zl3vni) return 0; - zl3vni->vrf_id = VRF_UNKNOWN; zebra_vxlan_process_l3vni_oper_down(zl3vni); + + /* delete and uninstall all rmacs */ + hash_iterate(zl3vni->rmac_table, zl3vni_del_rmac_hash_entry, zl3vni); + /* delete and uninstall all next-hops */ + hash_iterate(zl3vni->nh_table, zl3vni_del_nh_hash_entry, zl3vni); + + zl3vni->vrf_id = VRF_UNKNOWN; + return 0; } @@ -9535,7 +5168,7 @@ void zebra_vxlan_flood_control(ZAPI_HANDLER_ARGS) /* Install or uninstall flood entries corresponding to * remote VTEPs. */ - hash_iterate(zvrf->vni_table, zvni_handle_flooding_remote_vteps, + hash_iterate(zvrf->evpn_table, zebra_evpn_handle_flooding_remote_vteps, zvrf); stream_failure: @@ -9551,7 +5184,7 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) struct stream *s; int advertise; vni_t vni = 0; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; struct interface *ifp = NULL; if (!EVPN_ENABLED(zvrf)) { @@ -9578,11 +5211,13 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) if (advertise) { zvrf->advertise_svi_macip = advertise; - hash_iterate(zvrf->vni_table, - zvni_gw_macip_add_for_vni_hash, NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_add_for_evpn_hash, + NULL); } else { - hash_iterate(zvrf->vni_table, - zvni_svi_macip_del_for_vni_hash, NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_svi_macip_del_for_evpn_hash, + NULL); zvrf->advertise_svi_macip = advertise; } @@ -9591,27 +5226,27 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) struct zebra_l2info_vxlan zl2_info; struct interface *vlan_if = NULL; - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "EVPN SVI macip Adv %s on VNI %d , currently %s", advertise ? "enabled" : "disabled", vni, - advertise_svi_macip_enabled(zvni) + advertise_svi_macip_enabled(zevpn) ? "enabled" : "disabled"); - if (zvni->advertise_svi_macip == advertise) + if (zevpn->advertise_svi_macip == advertise) return; /* Store flag even though SVI is not present. * Once SVI comes up triggers self MAC-IP route add. */ - zvni->advertise_svi_macip = advertise; + zevpn->advertise_svi_macip = advertise; - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) return; @@ -9629,10 +5264,10 @@ void zebra_vxlan_advertise_svi_macip(ZAPI_HANDLER_ARGS) if (advertise) { /* Add primary SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); } else { /* Del primary SVI MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); } } @@ -9649,7 +5284,7 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) struct stream *s; int advertise; vni_t vni = 0; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; struct interface *ifp = NULL; struct zebra_if *zif = NULL; struct zebra_l2info_vxlan zl2_info; @@ -9665,22 +5300,22 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) STREAM_GETC(s, advertise); STREAM_GET(&vni, s, 3); - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return; - if (zvni->advertise_subnet == advertise) + if (zevpn->advertise_subnet == advertise) return; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("EVPN subnet Adv %s on VNI %d , currently %s", advertise ? "enabled" : "disabled", vni, - zvni->advertise_subnet ? "enabled" : "disabled"); + zevpn->advertise_subnet ? "enabled" : "disabled"); - zvni->advertise_subnet = advertise; + zevpn->advertise_subnet = advertise; - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) return; @@ -9697,10 +5332,10 @@ void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS) if (!vlan_if) return; - if (zvni->advertise_subnet) - zvni_advertise_subnet(zvni, vlan_if, 1); + if (zevpn->advertise_subnet) + zebra_evpn_advertise_subnet(zevpn, vlan_if, 1); else - zvni_advertise_subnet(zvni, vlan_if, 0); + zebra_evpn_advertise_subnet(zevpn, vlan_if, 0); stream_failure: return; @@ -9715,7 +5350,7 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) struct stream *s; int advertise; vni_t vni = 0; - zebra_vni_t *zvni = NULL; + zebra_evpn_t *zevpn = NULL; struct interface *ifp = NULL; if (!EVPN_ENABLED(zvrf)) { @@ -9741,12 +5376,14 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) zvrf->advertise_gw_macip = advertise; - if (advertise_gw_macip_enabled(zvni)) - hash_iterate(zvrf->vni_table, - zvni_gw_macip_add_for_vni_hash, NULL); + if (advertise_gw_macip_enabled(zevpn)) + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_add_for_evpn_hash, + NULL); else - hash_iterate(zvrf->vni_table, - zvni_gw_macip_del_for_vni_hash, NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_del_for_evpn_hash, + NULL); } else { struct zebra_if *zif = NULL; @@ -9754,23 +5391,23 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) struct interface *vlan_if = NULL; struct interface *vrr_if = NULL; - zvni = zvni_lookup(vni); - if (!zvni) + zevpn = zebra_evpn_lookup(vni); + if (!zevpn) return; if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug( "EVPN gateway macip Adv %s on VNI %d , currently %s", advertise ? "enabled" : "disabled", vni, - advertise_gw_macip_enabled(zvni) ? "enabled" + advertise_gw_macip_enabled(zevpn) ? "enabled" : "disabled"); - if (zvni->advertise_gw_macip == advertise) + if (zevpn->advertise_gw_macip == advertise) return; - zvni->advertise_gw_macip = advertise; + zevpn->advertise_gw_macip = advertise; - ifp = zvni->vxlan_if; + ifp = zevpn->vxlan_if; if (!ifp) return; @@ -9787,22 +5424,22 @@ void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS) if (!vlan_if) return; - if (advertise_gw_macip_enabled(zvni)) { + if (advertise_gw_macip_enabled(zevpn)) { /* Add primary SVI MAC-IP */ - zvni_add_macip_for_intf(vlan_if, zvni); + zebra_evpn_add_macip_for_intf(vlan_if, zevpn); /* Add VRR MAC-IP - if any*/ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); if (vrr_if) - zvni_add_macip_for_intf(vrr_if, zvni); + zebra_evpn_add_macip_for_intf(vrr_if, zevpn); } else { /* Del primary MAC-IP */ - zvni_del_macip_for_intf(vlan_if, zvni); + zebra_evpn_del_macip_for_intf(vlan_if, zevpn); /* Del VRR MAC-IP - if any*/ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if); if (vrr_if) - zvni_del_macip_for_intf(vrr_if, zvni); + zebra_evpn_del_macip_for_intf(vrr_if, zevpn); } } @@ -9810,25 +5447,6 @@ stream_failure: return; } -static int macfdb_read_ns(struct ns *ns, - void *_in_param __attribute__((unused)), - void **out_param __attribute__((unused))) -{ - struct zebra_ns *zns = ns->info; - - macfdb_read(zns); - return NS_WALK_CONTINUE; -} - -static int neigh_read_ns(struct ns *ns, - void *_in_param __attribute__((unused)), - void **out_param __attribute__((unused))) -{ - struct zebra_ns *zns = ns->info; - - neigh_read(zns); - return NS_WALK_CONTINUE; -} /* * Handle message from client to learn (or stop learning) about VNIs and MACs. @@ -9870,23 +5488,30 @@ void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS) /* Note BUM handling */ zvrf->vxlan_flood_ctrl = flood_ctrl; - /* Build VNI hash table and inform BGP. */ - zvni_build_hash_table(); + /* Replay all ESs */ + zebra_evpn_es_send_all_to_client(true /* add */); + + /* Build EVPN hash table and inform BGP. */ + zevpn_build_hash_table(); /* Add all SVI (L3 GW) MACs to BGP*/ - hash_iterate(zvrf->vni_table, zvni_gw_macip_add_for_vni_hash, - NULL); + hash_iterate(zvrf->evpn_table, + zebra_evpn_gw_macip_add_for_evpn_hash, NULL); /* Read the MAC FDB */ - ns_walk_func(macfdb_read_ns, NULL, NULL); + macfdb_read(zvrf->zns); /* Read neighbors */ - ns_walk_func(neigh_read_ns, NULL, NULL); + neigh_read(zvrf->zns); } else { - /* Cleanup VTEPs for all VNIs - uninstall from + /* Cleanup VTEPs for all EVPNs - uninstall from * kernel and free entries. */ - hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, + zvrf); + + /* Delete all ESs in BGP */ + zebra_evpn_es_send_all_to_client(false /* add */); /* cleanup all l3vnis */ hash_iterate(zrouter.l3vni_table, zl3vni_cleanup_all, NULL); @@ -9900,35 +5525,41 @@ stream_failure: } /* - * Allocate VNI hash table for this VRF and do other initialization. + * Allocate EVPN hash table for this VRF and do other initialization. * NOTE: Currently supported only for default VRF. */ void zebra_vxlan_init_tables(struct zebra_vrf *zvrf) { if (!zvrf) return; - zvrf->vni_table = hash_create(vni_hash_keymake, vni_hash_cmp, - "Zebra VRF VNI Table"); + zvrf->evpn_table = + hash_create(zebra_evpn_hash_keymake, zebra_evpn_hash_cmp, + "Zebra VRF EVPN Table"); zvrf->vxlan_sg_table = hash_create(zebra_vxlan_sg_hash_key_make, zebra_vxlan_sg_hash_eq, "Zebra VxLAN SG Table"); } -/* Cleanup VNI info, but don't free the table. */ +/* Cleanup EVPN info, but don't free the table. */ void zebra_vxlan_cleanup_tables(struct zebra_vrf *zvrf) { + struct zebra_vrf *evpn_zvrf = zebra_vrf_get_evpn(); + if (!zvrf) return; - hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf); hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_cleanup, NULL); + + if (zvrf == evpn_zvrf) + zebra_evpn_es_cleanup(); } -/* Close all VNI handling */ +/* Close all EVPN handling */ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf) { if (!zvrf) return; - hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf); - hash_free(zvrf->vni_table); + hash_iterate(zvrf->evpn_table, zebra_evpn_vxlan_cleanup_all, zvrf); + hash_free(zvrf->evpn_table); } /* init the l3vni table */ @@ -9937,12 +5568,14 @@ void zebra_vxlan_init(void) zrouter.l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp, "Zebra VRF L3 VNI table"); zrouter.evpn_vrf = NULL; + zebra_evpn_mh_init(); } /* free l3vni table */ void zebra_vxlan_disable(void) { hash_free(zrouter.l3vni_table); + zebra_evpn_mh_terminate(); } /* get the l3vni svi ifindex */ @@ -9957,128 +5590,6 @@ ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) return zl3vni->svi_if->ifindex; } -static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t) -{ - struct zebra_vrf *zvrf = NULL; - zebra_neigh_t *nbr = NULL; - zebra_vni_t *zvni = NULL; - char buf1[INET6_ADDRSTRLEN]; - char buf2[ETHER_ADDR_STRLEN]; - - nbr = THREAD_ARG(t); - - /* since this is asynchronous we need sanity checks*/ - zvrf = vrf_info_lookup(nbr->zvni->vrf_id); - if (!zvrf) - return 0; - - zvni = zvni_lookup(nbr->zvni->vni); - if (!zvni) - return 0; - - nbr = zvni_neigh_lookup(zvni, &nbr->ip); - if (!nbr) - return 0; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired", - __func__, - prefix_mac2str(&nbr->emac, buf2, sizeof(buf2)), - ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), nbr->flags, - nbr->dad_count, zvni->vni); - - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->detect_start_time.tv_usec = 0; - nbr->dad_dup_detect_time = 0; - nbr->dad_ip_auto_recovery_timer = NULL; - ZEBRA_NEIGH_SET_ACTIVE(nbr); - - /* Send to BGP */ - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { - zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, &nbr->emac, - nbr->flags, nbr->loc_seq); - } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { - zvni_neigh_install(zvni, nbr); - } - - return 0; -} - -static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t) -{ - struct zebra_vrf *zvrf = NULL; - zebra_mac_t *mac = NULL; - zebra_vni_t *zvni = NULL; - struct listnode *node = NULL; - zebra_neigh_t *nbr = NULL; - char buf[ETHER_ADDR_STRLEN]; - - mac = THREAD_ARG(t); - - /* since this is asynchronous we need sanity checks*/ - zvrf = vrf_info_lookup(mac->zvni->vrf_id); - if (!zvrf) - return 0; - - zvni = zvni_lookup(mac->zvni->vni); - if (!zvni) - return 0; - - mac = zvni_mac_lookup(zvni, &mac->macaddr); - if (!mac) - return 0; - - if (IS_ZEBRA_DEBUG_VXLAN) - zlog_debug( - "%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired", - __func__, - prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), - mac->flags, mac->dad_count, listcount(mac->neigh_list)); - - /* Remove all IPs as duplicate associcated with this MAC */ - for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { - if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) - ZEBRA_NEIGH_SET_INACTIVE(nbr); - else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) - zvni_neigh_install(zvni, nbr); - } - - UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); - nbr->dad_count = 0; - nbr->detect_start_time.tv_sec = 0; - nbr->dad_dup_detect_time = 0; - } - - UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); - mac->dad_count = 0; - mac->detect_start_time.tv_sec = 0; - mac->detect_start_time.tv_usec = 0; - mac->dad_dup_detect_time = 0; - mac->dad_mac_auto_recovery_timer = NULL; - - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { - /* Inform to BGP */ - if (zvni_mac_send_add_to_client(zvni->vni, &mac->macaddr, - mac->flags, mac->loc_seq)) - return -1; - - /* Process all neighbors associated with this MAC. */ - zvni_process_neigh_on_local_mac_change(zvni, mac, 0); - - } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { - zvni_process_neigh_on_remote_mac_add(zvni, mac); - - /* Install the entry. */ - zvni_mac_install(zvni, mac); - } - - return 0; -} - /************************** vxlan SG cache management ************************/ /* Inform PIM about the mcast group */ static int zebra_vxlan_sg_send(struct zebra_vrf *zvrf, @@ -10330,87 +5841,6 @@ void zebra_vxlan_sg_replay(ZAPI_HANDLER_ARGS) hash_iterate(zvrf->vxlan_sg_table, zebra_vxlan_sg_replay_send, NULL); } -/************************** EVPN BGP config management ************************/ -/* Notify Local MACs to the clienti, skips GW MAC */ -static void zvni_send_mac_hash_entry_to_client(struct hash_bucket *bucket, - void *arg) -{ - struct mac_walk_ctx *wctx = arg; - zebra_mac_t *zmac = bucket->data; - - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_DEF_GW)) - return; - - if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) - zvni_mac_send_add_to_client(wctx->zvni->vni, &zmac->macaddr, - zmac->flags, zmac->loc_seq); -} - -/* Iterator to Notify Local MACs of a L2VNI */ -static void zvni_send_mac_to_client(zebra_vni_t *zvni) -{ - struct mac_walk_ctx wctx; - - if (!zvni->mac_table) - return; - - memset(&wctx, 0, sizeof(struct mac_walk_ctx)); - wctx.zvni = zvni; - - hash_iterate(zvni->mac_table, zvni_send_mac_hash_entry_to_client, - &wctx); -} - -/* Notify Neighbor entries to the Client, skips the GW entry */ -static void zvni_send_neigh_hash_entry_to_client(struct hash_bucket *bucket, - void *arg) -{ - struct mac_walk_ctx *wctx = arg; - zebra_neigh_t *zn = bucket->data; - zebra_mac_t *zmac = NULL; - - if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_DEF_GW)) - return; - - if (CHECK_FLAG(zn->flags, ZEBRA_NEIGH_LOCAL) && - IS_ZEBRA_NEIGH_ACTIVE(zn)) { - zmac = zvni_mac_lookup(wctx->zvni, &zn->emac); - if (!zmac) - return; - - zvni_neigh_send_add_to_client(wctx->zvni->vni, &zn->ip, - &zn->emac, zn->flags, - zn->loc_seq); - } -} - -/* Iterator of a specific L2VNI */ -static void zvni_send_neigh_to_client(zebra_vni_t *zvni) -{ - struct neigh_walk_ctx wctx; - - memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); - wctx.zvni = zvni; - - hash_iterate(zvni->neigh_table, zvni_send_neigh_hash_entry_to_client, - &wctx); -} - -static void zvni_evpn_cfg_cleanup(struct hash_bucket *bucket, void *ctxt) -{ - zebra_vni_t *zvni = NULL; - - zvni = (zebra_vni_t *)bucket->data; - zvni->advertise_gw_macip = 0; - zvni->advertise_svi_macip = 0; - zvni->advertise_subnet = 0; - - zvni_neigh_del_all(zvni, 1, 0, - DEL_REMOTE_NEIGH | DEL_REMOTE_NEIGH_FROM_VTEP); - zvni_mac_del_all(zvni, 1, 0, - DEL_REMOTE_MAC | DEL_REMOTE_MAC_FROM_VTEP); - zvni_vtep_del_all(zvni, 1); -} /* Cleanup EVPN configuration of a specific VRF */ static void zebra_evpn_vrf_cfg_cleanup(struct zebra_vrf *zvrf) @@ -10422,7 +5852,7 @@ static void zebra_evpn_vrf_cfg_cleanup(struct zebra_vrf *zvrf) zvrf->advertise_svi_macip = 0; zvrf->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL; - hash_iterate(zvrf->vni_table, zvni_evpn_cfg_cleanup, NULL); + hash_iterate(zvrf->evpn_table, zebra_evpn_cfg_cleanup, NULL); if (zvrf->l3vni) zl3vni = zl3vni_lookup(zvrf->l3vni); diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 064dda6cd0..9c8af9d1fc 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -165,14 +165,15 @@ extern int zebra_vxlan_svi_down(struct interface *ifp, extern int zebra_vxlan_handle_kernel_neigh_update( struct interface *ifp, struct interface *link_if, struct ipaddr *ip, struct ethaddr *macaddr, uint16_t state, bool is_ext, - bool is_router); + bool is_router, bool local_inactive, bool dp_static); extern int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, struct interface *link_if, struct ipaddr *ip); extern int zebra_vxlan_local_mac_add_update(struct interface *ifp, struct interface *br_if, struct ethaddr *mac, vlanid_t vid, - bool sticky); + bool sticky, bool local_inactive, + bool dp_static); extern int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, struct ethaddr *mac, vlanid_t vid); @@ -217,6 +218,7 @@ extern void zebra_vxlan_handle_result(struct zebra_dplane_ctx *ctx); extern void zebra_evpn_init(void); extern void zebra_vxlan_macvlan_up(struct interface *ifp); extern void zebra_vxlan_macvlan_down(struct interface *ifp); +extern int vni_list_cmp(void *p1, void *p2); #ifdef __cplusplus } diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index d2b02df2ad..9a88d98b81 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -29,6 +29,8 @@ #include "if.h" #include "linklist.h" #include "zebra_vxlan.h" +#include "zebra_evpn.h" +#include "zebra_evpn_mac.h" #ifdef __cplusplus extern "C" { @@ -37,72 +39,8 @@ extern "C" { #define ERR_STR_SZ 256 /* definitions */ -typedef struct zebra_vni_t_ zebra_vni_t; -typedef struct zebra_vtep_t_ zebra_vtep_t; -typedef struct zebra_mac_t_ zebra_mac_t; -typedef struct zebra_neigh_t_ zebra_neigh_t; typedef struct zebra_l3vni_t_ zebra_l3vni_t; -/* - * VTEP info - * - * Right now, this just has each remote VTEP's IP address. - */ -struct zebra_vtep_t_ { - /* Remote IP. */ - /* NOTE: Can only be IPv4 right now. */ - struct in_addr vtep_ip; - /* Flood mode (one of enum vxlan_flood_control) based on the PMSI - * tunnel type advertised by the remote VTEP - */ - int flood_control; - - /* Links. */ - struct zebra_vtep_t_ *next; - struct zebra_vtep_t_ *prev; -}; - - -/* - * VNI hash table - * - * Contains information pertaining to a VNI: - * - the list of remote VTEPs (with this VNI) - */ -struct zebra_vni_t_ { - /* VNI - key */ - vni_t vni; - - /* Flag for advertising gw macip */ - uint8_t advertise_gw_macip; - - /* Flag for advertising svi macip */ - uint8_t advertise_svi_macip; - - /* Flag for advertising gw macip */ - uint8_t advertise_subnet; - - /* Corresponding VxLAN interface. */ - struct interface *vxlan_if; - - /* List of remote VTEPs */ - zebra_vtep_t *vteps; - - /* Local IP */ - struct in_addr local_vtep_ip; - - /* PIM-SM MDT group for BUM flooding */ - struct in_addr mcast_grp; - - /* tenant VRF, if any */ - vrf_id_t vrf_id; - - /* List of local or remote MAC */ - struct hash *mac_table; - - /* List of local or remote neighbors (MAC+IP) */ - struct hash *neigh_table; -}; /* L3 VNI hash table */ struct zebra_l3vni_t_ { @@ -266,202 +204,11 @@ static inline void zl3vni_get_svi_rmac(zebra_l3vni_t *zl3vni, memcpy(rmac->octet, zl3vni->svi_if->hw_addr, ETH_ALEN); } -struct host_rb_entry { - RB_ENTRY(host_rb_entry) hl_entry; - - struct prefix p; -}; - -RB_HEAD(host_rb_tree_entry, host_rb_entry); -RB_PROTOTYPE(host_rb_tree_entry, host_rb_entry, hl_entry, - host_rb_entry_compare); -/* - * MAC hash table. - * - * This table contains the MAC addresses pertaining to this VNI. - * This includes local MACs learnt on an attached VLAN that maps - * to this VNI as well as remote MACs learnt and installed by BGP. - * Local MACs will be known either on a VLAN sub-interface or - * on (port, VLAN); however, it is sufficient for zebra to maintain - * against the VNI i.e., it does not need to retain the local "port" - * information. The correct VNI will be obtained as zebra maintains - * the mapping (of VLAN to VNI). - */ -struct zebra_mac_t_ { - /* MAC address. */ - struct ethaddr macaddr; - - uint32_t flags; -#define ZEBRA_MAC_LOCAL 0x01 -#define ZEBRA_MAC_REMOTE 0x02 -#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */ -#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */ -#define ZEBRA_MAC_REMOTE_RMAC 0x10 /* remote router mac */ -#define ZEBRA_MAC_DEF_GW 0x20 -/* remote VTEP advertised MAC as default GW */ -#define ZEBRA_MAC_REMOTE_DEF_GW 0x40 -#define ZEBRA_MAC_DUPLICATE 0x80 -#define ZEBRA_MAC_FPM_SENT 0x100 /* whether or not this entry was sent. */ - - /* back pointer to zvni */ - zebra_vni_t *zvni; - - /* Local or remote info. */ - union { - struct { - ifindex_t ifindex; - ns_id_t ns_id; - vlanid_t vid; - } local; - - struct in_addr r_vtep_ip; - } fwd_info; - - /* Mobility sequence numbers associated with this entry. */ - uint32_t rem_seq; - uint32_t loc_seq; - - /* List of neigh associated with this mac */ - struct list *neigh_list; - - /* list of hosts pointing to this remote RMAC */ - struct host_rb_tree_entry host_rb; - - /* Duplicate mac detection */ - uint32_t dad_count; - - struct thread *dad_mac_auto_recovery_timer; - - struct timeval detect_start_time; - - time_t dad_dup_detect_time; -}; - -/* - * Context for MAC hash walk - used by callbacks. - */ -struct mac_walk_ctx { - zebra_vni_t *zvni; /* VNI hash */ - struct zebra_vrf *zvrf; /* VRF - for client notification. */ - int uninstall; /* uninstall from kernel? */ - int upd_client; /* uninstall from client? */ - - uint32_t flags; -#define DEL_LOCAL_MAC 0x1 -#define DEL_REMOTE_MAC 0x2 -#define DEL_ALL_MAC (DEL_LOCAL_MAC | DEL_REMOTE_MAC) -#define DEL_REMOTE_MAC_FROM_VTEP 0x4 -#define SHOW_REMOTE_MAC_FROM_VTEP 0x8 - - struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */ - - struct vty *vty; /* Used by VTY handlers */ - uint32_t count; /* Used by VTY handlers */ - struct json_object *json; /* Used for JSON Output */ - bool print_dup; /* Used to print dup addr list */ -}; - -struct rmac_walk_ctx { - struct vty *vty; - struct json_object *json; -}; - -#define IS_ZEBRA_NEIGH_ACTIVE(n) (n->state == ZEBRA_NEIGH_ACTIVE) - -#define IS_ZEBRA_NEIGH_INACTIVE(n) (n->state == ZEBRA_NEIGH_INACTIVE) - -#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE - -#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE - -/* - * Neighbor hash table. - * - * This table contains the neighbors (IP to MAC bindings) pertaining to - * this VNI. This includes local neighbors learnt on the attached VLAN - * device that maps to this VNI as well as remote neighbors learnt and - * installed by BGP. - * Local neighbors will be known against the VLAN device (SVI); however, - * it is sufficient for zebra to maintain against the VNI. The correct - * VNI will be obtained as zebra maintains the mapping (of VLAN to VNI). - */ -struct zebra_neigh_t_ { - /* IP address. */ - struct ipaddr ip; - - /* MAC address. */ - struct ethaddr emac; - - /* Underlying interface. */ - ifindex_t ifindex; - - zebra_vni_t *zvni; - - uint32_t flags; -#define ZEBRA_NEIGH_LOCAL 0x01 -#define ZEBRA_NEIGH_REMOTE 0x02 -#define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ -#define ZEBRA_NEIGH_DEF_GW 0x08 -#define ZEBRA_NEIGH_ROUTER_FLAG 0x10 -#define ZEBRA_NEIGH_DUPLICATE 0x20 -#define ZEBRA_NEIGH_SVI_IP 0x40 - - enum zebra_neigh_state state; - - /* Remote VTEP IP - applicable only for remote neighbors. */ - struct in_addr r_vtep_ip; - - /* - * Mobility sequence numbers associated with this entry. The rem_seq - * represents the sequence number from the client (BGP) for the most - * recent add or update of this entry while the loc_seq represents - * the sequence number informed (or to be informed) by zebra to BGP - * for this entry. - */ - uint32_t rem_seq; - uint32_t loc_seq; - - /* list of hosts pointing to this remote NH entry */ - struct host_rb_tree_entry host_rb; - - /* Duplicate ip detection */ - uint32_t dad_count; - - struct thread *dad_ip_auto_recovery_timer; - - struct timeval detect_start_time; - - time_t dad_dup_detect_time; -}; - -/* - * Context for neighbor hash walk - used by callbacks. - */ -struct neigh_walk_ctx { - zebra_vni_t *zvni; /* VNI hash */ - struct zebra_vrf *zvrf; /* VRF - for client notification. */ - int uninstall; /* uninstall from kernel? */ - int upd_client; /* uninstall from client? */ - - uint32_t flags; -#define DEL_LOCAL_NEIGH 0x1 -#define DEL_REMOTE_NEIGH 0x2 -#define DEL_ALL_NEIGH (DEL_LOCAL_NEIGH | DEL_REMOTE_NEIGH) -#define DEL_REMOTE_NEIGH_FROM_VTEP 0x4 -#define SHOW_REMOTE_NEIGH_FROM_VTEP 0x8 - - struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */ - - struct vty *vty; /* Used by VTY handlers */ - uint32_t count; /* Used by VTY handlers */ - uint8_t addr_width; /* Used by VTY handlers */ - struct json_object *json; /* Used for JSON Output */ -}; /* context for neigh hash walk - update l3vni and rmac */ struct neigh_l3info_walk_ctx { - zebra_vni_t *zvni; + zebra_evpn_t *zevpn; zebra_l3vni_t *zl3vni; int add; }; @@ -476,6 +223,7 @@ extern zebra_l3vni_t *zl3vni_from_vrf(vrf_id_t vrf_id); extern struct interface *zl3vni_map_to_vxlan_if(zebra_l3vni_t *zl3vni); extern struct interface *zl3vni_map_to_svi_if(zebra_l3vni_t *zl3vni); extern struct interface *zl3vni_map_to_mac_vlan_if(zebra_l3vni_t *zl3vni); +extern zebra_l3vni_t *zl3vni_lookup(vni_t vni); DECLARE_HOOK(zebra_rmac_update, (zebra_mac_t *rmac, zebra_l3vni_t *zl3vni, bool delete, const char *reason), (rmac, zl3vni, delete, reason)) @@ -507,4 +255,8 @@ typedef struct zebra_vxlan_sg_ { uint32_t ref_cnt; } zebra_vxlan_sg_t; +extern zebra_evpn_t *zevpn_lookup(vni_t vni); +extern void zebra_vxlan_sync_mac_dp_install(zebra_mac_t *mac, bool set_inactive, + bool force_clear_static, const char *caller); + #endif /* _ZEBRA_VXLAN_PRIVATE_H */ diff --git a/zebra/zserv.c b/zebra/zserv.c index 99a85fd2ce..cded6ea12b 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -628,8 +628,8 @@ static void zserv_client_free(struct zserv *client) } vrf_bitmap_free(client->redist_default[afi]); + vrf_bitmap_free(client->ridinfo[afi]); } - vrf_bitmap_free(client->ridinfo); /* * If any instance are graceful restart enabled, @@ -750,8 +750,8 @@ static struct zserv *zserv_client_create(int sock) for (i = 0; i < ZEBRA_ROUTE_MAX; i++) client->redist[afi][i] = vrf_bitmap_init(); client->redist_default[afi] = vrf_bitmap_init(); + client->ridinfo[afi] = vrf_bitmap_init(); } - client->ridinfo = vrf_bitmap_init(); /* Add this client to linked list. */ frr_with_mutex(&client_mutex) { @@ -1075,6 +1075,12 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) vty_out(vty, "L3-VNI delete notifications: %u\n", client->l3vnidel_cnt); vty_out(vty, "MAC-IP add notifications: %u\n", client->macipadd_cnt); vty_out(vty, "MAC-IP delete notifications: %u\n", client->macipdel_cnt); + vty_out(vty, "ES add notifications: %u\n", client->local_es_add_cnt); + vty_out(vty, "ES delete notifications: %u\n", client->local_es_del_cnt); + vty_out(vty, "ES-EVI add notifications: %u\n", + client->local_es_evi_add_cnt); + vty_out(vty, "ES-EVI delete notifications: %u\n", + client->local_es_evi_del_cnt); TAILQ_FOREACH (info, &client->gr_info_queue, gr_info) { vty_out(vty, "VRF : %s\n", vrf_id_to_name(info->vrf_id)); diff --git a/zebra/zserv.h b/zebra/zserv.h index f2a4523818..54e840cd56 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -135,7 +135,7 @@ struct zserv { vrf_bitmap_t redist_default[AFI_MAX]; /* Router-id information. */ - vrf_bitmap_t ridinfo; + vrf_bitmap_t ridinfo[AFI_MAX]; bool notify_owner; @@ -194,6 +194,10 @@ struct zserv { uint32_t v6_nh_watch_rem_cnt; uint32_t vxlan_sg_add_cnt; uint32_t vxlan_sg_del_cnt; + uint32_t local_es_add_cnt; + uint32_t local_es_del_cnt; + uint32_t local_es_evi_add_cnt; + uint32_t local_es_evi_del_cnt; uint32_t error_cnt; time_t nh_reg_time; |
