summaryrefslogtreecommitdiff
path: root/zebra
diff options
context:
space:
mode:
Diffstat (limited to 'zebra')
-rw-r--r--zebra/connected.c9
-rw-r--r--zebra/debug.c79
-rw-r--r--zebra/debug.h15
-rw-r--r--zebra/dplane_fpm_nl.c171
-rw-r--r--zebra/if_netlink.c215
-rw-r--r--zebra/if_netlink.h4
-rw-r--r--zebra/interface.c60
-rw-r--r--zebra/interface.h31
-rw-r--r--zebra/ipforward_solaris.c3
-rw-r--r--zebra/irdp_interface.c9
-rw-r--r--zebra/kernel_netlink.c406
-rw-r--r--zebra/kernel_netlink.h46
-rw-r--r--zebra/kernel_socket.c105
-rw-r--r--zebra/label_manager.c2
-rw-r--r--zebra/main.c17
-rw-r--r--zebra/rib.h21
-rw-r--r--zebra/router-id.c490
-rw-r--r--zebra/router-id.h10
-rw-r--r--zebra/rt.h17
-rw-r--r--zebra/rt_netlink.c568
-rw-r--r--zebra/rt_netlink.h20
-rw-r--r--zebra/rt_socket.c38
-rw-r--r--zebra/rtadv.c9
-rw-r--r--zebra/rtread_getmsg.c3
-rw-r--r--zebra/rule_netlink.c107
-rw-r--r--zebra/rule_netlink.h3
-rw-r--r--zebra/subdir.am13
-rw-r--r--zebra/zapi_msg.c335
-rw-r--r--zebra/zapi_msg.h8
-rw-r--r--zebra/zebra_dplane.c571
-rw-r--r--zebra/zebra_dplane.h68
-rw-r--r--zebra/zebra_errors.h1
-rw-r--r--zebra/zebra_evpn.c1448
-rw-r--r--zebra/zebra_evpn.h208
-rw-r--r--zebra/zebra_evpn_mac.c2231
-rw-r--r--zebra/zebra_evpn_mac.h263
-rw-r--r--zebra/zebra_evpn_mh.c2147
-rw-r--r--zebra/zebra_evpn_mh.h239
-rw-r--r--zebra/zebra_evpn_neigh.c2453
-rw-r--r--zebra/zebra_evpn_neigh.h294
-rw-r--r--zebra/zebra_evpn_vxlan.h71
-rw-r--r--zebra/zebra_fpm.c3
-rw-r--r--zebra/zebra_fpm_netlink.c17
-rw-r--r--zebra/zebra_l2.c85
-rw-r--r--zebra/zebra_l2.h11
-rw-r--r--zebra/zebra_memory.c2
-rw-r--r--zebra/zebra_mpls.c152
-rw-r--r--zebra/zebra_mpls.h13
-rw-r--r--zebra/zebra_mpls_netlink.c34
-rw-r--r--zebra/zebra_mpls_openbsd.c3
-rw-r--r--zebra/zebra_nb.c74
-rw-r--r--zebra/zebra_nb.h16
-rw-r--r--zebra/zebra_nb_config.c252
-rw-r--r--zebra/zebra_nb_state.c60
-rw-r--r--zebra/zebra_netns_id.c43
-rw-r--r--zebra/zebra_netns_id.h2
-rw-r--r--zebra/zebra_netns_notify.c33
-rw-r--r--zebra/zebra_nhg.c93
-rw-r--r--zebra/zebra_ns.c17
-rw-r--r--zebra/zebra_ns.h9
-rw-r--r--zebra/zebra_pbr.c17
-rw-r--r--zebra/zebra_pbr.h9
-rw-r--r--zebra/zebra_ptm.c9
-rw-r--r--zebra/zebra_pw.c113
-rw-r--r--zebra/zebra_rib.c62
-rw-r--r--zebra/zebra_rnh.c56
-rw-r--r--zebra/zebra_rnh.h3
-rw-r--r--zebra/zebra_routemap.c48
-rw-r--r--zebra/zebra_router.c1
-rw-r--r--zebra/zebra_router.h6
-rw-r--r--zebra/zebra_srte.c378
-rw-r--r--zebra/zebra_srte.h74
-rw-r--r--zebra/zebra_vrf.c1
-rw-r--r--zebra/zebra_vrf.h9
-rw-r--r--zebra/zebra_vty.c410
-rw-r--r--zebra/zebra_vxlan.c5948
-rw-r--r--zebra/zebra_vxlan.h6
-rw-r--r--zebra/zebra_vxlan_private.h264
-rw-r--r--zebra/zserv.c10
-rw-r--r--zebra/zserv.h6
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;