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, zebra_mac_t *zmac);
+ struct ethaddr *mac, zebra_mac_t *zmac,
+ uint32_t n_flags);
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 *mac, zebra_mac_t *zmac,
- uint8_t flags, uint32_t seq);
+ uint32_t flags, uint32_t seq);
static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip,
struct ethaddr *mac,
- uint8_t flags, int state);
-static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n);
+ uint32_t flags, int state, bool force);
+static int zvni_rem_neigh_install(zebra_vni_t *zvni,
+ zebra_neigh_t *n, bool was_static);
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,
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, struct zebra_evpn_es *es);
-static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr);
+ uint32_t flags, uint32_t seq, struct zebra_evpn_es *es);
+static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr,
+ uint32_t flags, bool force);
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 int zvni_rem_mac_install(zebra_vni_t *zvni,
+ zebra_mac_t *mac, bool was_static);
+static int zvni_rem_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_send_mac_to_client(zebra_vni_t *zvn);
static void zvni_send_neigh_to_client(zebra_vni_t *zvni);
+static void zebra_vxlan_rem_mac_del(zebra_vni_t *zvni,
+ zebra_mac_t *zmac);
+static inline void zebra_vxlan_mac_stop_hold_timer(zebra_mac_t *mac);
+static inline bool zebra_vxlan_mac_is_static(zebra_mac_t *mac);
+static void zebra_vxlan_local_neigh_ref_mac(zebra_neigh_t *n,
+ struct ethaddr *macaddr, zebra_mac_t *mac,
+ bool send_mac_update);
+static void zebra_vxlan_local_neigh_deref_mac(zebra_neigh_t *n,
+ bool send_mac_update);
+static inline bool zebra_vxlan_neigh_is_ready_for_bgp(zebra_neigh_t *n);
+static inline bool zebra_vxlan_neigh_clear_sync_info(zebra_neigh_t *n);
+static void zebra_vxlan_sync_neigh_dp_install(zebra_neigh_t *n,
+ bool set_inactive, bool force_clear_static, const char *caller);
+static inline bool zebra_vxlan_neigh_is_static(zebra_neigh_t *neigh);
+static void zebra_vxlan_neigh_send_add_del_to_client(zebra_neigh_t *n,
+ bool old_bgp_ready, bool new_bgp_ready);
/* Private functions */
static int host_rb_entry_compare(const struct host_rb_entry *hle1,
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)
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)));
+ 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)));
+ 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) {
}
}
+static void zvni_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 *zvni_print_neigh_flags(zebra_neigh_t *n, char *flags_buf)
+{
+ sprintf(flags_buf, "%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.
*/
char buf2[INET6_ADDRSTRLEN];
struct neigh_walk_ctx *wctx = ctxt;
const char *state_str;
+ char flags_buf[6];
vty = wctx->vty;
json_vni = wctx->json;
return;
if (json_vni == NULL) {
- vty_out(vty, "%*s %-6s %-8s %-17s %u/%u\n",
+ vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n",
-wctx->addr_width, buf2, "local",
- state_str, buf1, n->loc_seq, n->rem_seq);
+ zvni_print_neigh_flags(n, 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);
if (json_vni == NULL) {
if ((wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) &&
(wctx->count == 0))
- vty_out(vty, "%*s %-6s %-8s %-17s %-30s %s\n",
- -wctx->addr_width, "Neighbor", "Type",
- "State", "MAC", "Remote ES/VTEP",
- "Seq #'s");
- vty_out(vty, "%*s %-6s %-8s %-17s %-30s %u/%u\n",
- -wctx->addr_width, buf2, "remote", state_str,
- buf1,
+ zvni_print_neigh_hdr(vty, wctx);
+ vty_out(vty, "%*s %-6s %-5s %-8s %-17s %-30s %u/%u\n",
+ -wctx->addr_width, buf2, "remote",
+ zvni_print_neigh_flags(n, 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);
wctx.json = json_vni;
hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
- if (json == NULL) {
- vty_out(vty, "%*s %-6s %-8s %-17s %-30s %s\n",
- -wctx.addr_width, "IP", "Type",
- "State", "MAC", "Remote ES/VTEP", "Seq #'s");
- }
+ if (json == NULL)
+ zvni_print_neigh_hdr(vty, &wctx);
+
if (print_dup)
hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash,
&wctx);
}
}
+static void
+zebra_vxlan_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 VNI */
+ if (mac->zvni->vxlan_if) {
+ zif = mac->zvni->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);
+ }
+}
+
/*
* Print a specific MAC entry.
*/
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)
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;
+ vlanid_t vid;
- ifindex = mac->fwd_info.local.ifindex;
- zns = zebra_ns_lookup(NS_DEFAULT);
- ifp = if_lookup_by_index_per_ns(zns, ifindex);
- if (!ifp)
- return;
+ zebra_vxlan_mac_get_access_info(mac,
+ &ifp, &vid);
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)
+ 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",
- mac->fwd_info.local.vid);
+ vid);
} else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
json_object_string_add(json_mac, "type", "remote");
json_object_string_add(
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));
/* print all the associated neigh */
if (!listcount(mac->neigh_list))
json_object_string_add(json_mac, "neighbors", "none");
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;
+ vlanid_t vid;
- ifindex = mac->fwd_info.local.ifindex;
- zns = zebra_ns_lookup(NS_DEFAULT);
- 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);
+ zebra_vxlan_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",
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, " 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)) {
}
}
+static char *zvni_print_mac_flags(zebra_mac_t *mac, char *flags_buf)
+{
+ sprintf(flags_buf, "%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.
*/
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;
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(NS_DEFAULT);
- 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 %-30s", buf1, "local",
- ifp->name);
- else {
+ zebra_vxlan_mac_get_access_info(mac,
+ &ifp, &vid);
+ if (json_mac_hdr == NULL) {
+ vty_out(vty, "%-17s %-6s %-5s %-30s", buf1, "local",
+ zvni_print_mac_flags(mac, flags_buf),
+ ifp ? ifp->name : "-");
+ } else {
json_object_string_add(json_mac, "type", "local");
- json_object_string_add(json_mac, "intf", ifp->name);
+ if (ifp)
+ json_object_string_add(json_mac,
+ "intf", ifp->name);
}
if (vid) {
if (json_mac_hdr == NULL)
if (json_mac_hdr == NULL) {
if ((wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) &&
- (wctx->count == 0)) {
+ (wctx->count == 0)) {
vty_out(vty, "\nVNI %u\n\n", wctx->zvni->vni);
- vty_out(vty, "%-17s %-6s %-30s %-5s %s\n",
- "MAC", "Type", "Intf/Remote ES/VTEP",
+ 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 %-30s %-5s %u/%u\n", buf1,
+ vty_out(vty, "%-17s %-6s %-5s %-30s %-5s %u/%u\n", buf1,
"remote",
+ zvni_print_mac_flags(mac, flags_buf),
mac->es ? mac->es->esi_str :
inet_ntoa(mac->fwd_info.r_vtep_ip),
"", mac->loc_seq, mac->rem_seq);
if (json == NULL) {
vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n",
zvni->vni, num_macs);
- vty_out(vty, "%-17s %-6s %-30s %-5s %s\n", "MAC",
- "Type", "Intf/Remote ES/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_vni, "numMacs", num_macs);
}
* Add neighbor entry.
*/
static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip,
- struct ethaddr *mac, zebra_mac_t *zmac)
+ struct ethaddr *mac, zebra_mac_t *zmac,
+ uint32_t n_flags)
{
zebra_neigh_t tmp_n;
zebra_neigh_t *n = NULL;
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;
+ n->flags = n_flags;
- /* Associate the neigh to mac */
- if (zmac) {
- n->mac = zmac;
- listnode_add_sort(zmac->neigh_list, n);
- }
+ if (!zmac)
+ zmac = zvni_mac_lookup(zvni, mac);
+ zebra_vxlan_local_neigh_ref_mac(n, mac,
+ zmac, false /* send_mac_update */);
return n;
}
&& 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);
+ &n->emac, n->flags, n->state,
+ false /*force*/);
+
+ if (wctx->uninstall) {
+ if (zebra_vxlan_neigh_is_static(n))
+ zebra_vxlan_sync_neigh_dp_install(n,
+ false /* set_inactive */,
+ true /* force_clear_static */,
+ __func__);
+ if ((n->flags & ZEBRA_NEIGH_REMOTE))
+ zvni_neigh_uninstall(wctx->zvni, n);
+ }
zvni_neigh_del(wctx->zvni, 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);
+ &n->emac, n->flags,
+ ZEBRA_NEIGH_ACTIVE,
+ false /*force*/);
}
}
}
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);
+ &n->emac, n->flags,
+ ZEBRA_NEIGH_ACTIVE,
+ false /* force */);
}
}
}
static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip,
struct ethaddr *macaddr,
zebra_mac_t *zmac,
- uint8_t neigh_flags,
+ 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 */
* 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)
+ 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 zvni_macip_send_msg_to_client(vni, macaddr, ip, flags,
0, state, NULL, ZEBRA_MACIP_DEL);
}
/*
* Install remote neighbor into the kernel.
*/
-static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n)
+static int zvni_rem_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n,
+ bool was_static)
{
struct zebra_if *zif;
struct zebra_l2info_vxlan *vxl;
ZEBRA_NEIGH_SET_ACTIVE(n);
dplane_rem_neigh_add(vlan_if, &n->ip, &n->emac, flags,
- false /*was_static*/);
+ was_static);
return ret;
}
n = (zebra_neigh_t *)bucket->data;
if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
- zvni_neigh_install(wctx->zvni, n);
+ zvni_rem_neigh_install(wctx->zvni, n, false /*was_static*/);
}
/* Get the VRR interface for SVI if any */
n = zvni_neigh_lookup(zvni, ip);
if (!n) {
- n = zvni_neigh_add(zvni, ip, macaddr, mac);
+ n = zvni_neigh_add(zvni, ip, macaddr, mac, 0);
if (!n) {
flog_err(
EC_ZEBRA_MAC_ADD_FAILED,
/* Remove neighbor from BGP. */
zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac,
- ZEBRA_MACIP_TYPE_GW, ZEBRA_NEIGH_ACTIVE);
+ n->flags, ZEBRA_NEIGH_ACTIVE,
+ false /*force*/);
/* Delete this neighbor entry. */
zvni_neigh_del(zvni, n);
return;
}
+static inline void zvni_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->zvni->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);
+}
+
static int zvni_local_neigh_update(zebra_vni_t *zvni,
struct interface *ifp,
struct ipaddr *ip,
struct ethaddr *macaddr,
- bool is_router)
+ bool is_router,
+ bool local_inactive, bool dp_static)
{
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
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 = zvni_mac_lookup(zvni, macaddr);
n = zvni_neigh_lookup(zvni, ip);
if (!n) {
/* New neighbor - create */
- n = zvni_neigh_add(zvni, ip, macaddr, zmac);
+ n = zvni_neigh_add(zvni, ip, macaddr, zmac, 0);
if (!n) {
flog_err(
EC_ZEBRA_MAC_ADD_FAILED,
/* 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_vxlan_neigh_is_ready_for_bgp(n);
/* Note any changes and see if of interest to BGP. */
- mac_different = (memcmp(n->emac.octet,
- macaddr->octet, ETH_ALEN) != 0) ? 1 : 0;
+ mac_different = !!memcmp(&n->emac,
+ macaddr, ETH_ALEN);
cur_is_router = !!CHECK_FLAG(n->flags,
ZEBRA_NEIGH_ROUTER_FLAG);
- if (!mac_different && is_router == cur_is_router) {
+ new_static = zebra_vxlan_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");
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. */
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_vxlan_neigh_is_ready_for_bgp(n);
+
/* Neigh is in freeze state and freeze action
* is enabled, do not send update to client.
*/
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, &n->emac,
- n->mac, n->flags,
- n->loc_seq);
- else {
- if (IS_ZEBRA_DEBUG_VXLAN)
+ zvni_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_vxlan_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");
}
* We also need to update the MAC's sequence number
* in different situations.
*/
- if (IS_ZEBRA_NEIGH_ACTIVE(n))
+ if (old_bgp_ready) {
zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
- &n->emac, 0, n->state);
- old_zmac = n->mac;
+ &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;
- listnode_delete(old_zmac->neigh_list, n);
- n->mac = NULL;
- zvni_deref_ip2mac(zvni, old_zmac);
+ zebra_vxlan_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_vxlan_neigh_clear_sync_info(n))
+ inform_dataplane = true;
/* Update the forwarding info. */
n->ifindex = ifp->ifindex;
- memcpy(&n->emac, macaddr, ETH_ALEN);
/* Link to new MAC */
- n->mac = zmac;
- listnode_add_sort(zmac->neigh_list, n);
+ zebra_vxlan_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
old_zmac->rem_seq :
old_zmac->loc_seq;
neigh_mac_change = upd_mac_seq = true;
- listnode_delete(old_zmac->neigh_list,
- n);
- n->mac = NULL;
- zvni_deref_ip2mac(zvni, old_zmac);
+ zebra_vxlan_local_neigh_deref_mac(n,
+ true /* send_update */);
}
/* Link to new MAC */
- memcpy(&n->emac, macaddr, ETH_ALEN);
- n->mac = zmac;
- listnode_add_sort(zmac->neigh_list, n);
+ zebra_vxlan_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
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_vxlan_neigh_is_static(n);
+ if (!new_static)
+ inform_dataplane = true;
+ }
+
/* Check old and/or new MAC detected as duplicate mark
* the neigh as duplicate
*/
zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad,
&neigh_on_hold, true);
+ if (inform_dataplane)
+ zebra_vxlan_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)) {
+ zvni_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;
}
+ zvni_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 (!neigh_on_hold) {
ZEBRA_NEIGH_SET_ACTIVE(n);
-
- return zvni_neigh_send_add_to_client(zvni->vni, ip,
- &n->emac, n->mac, n->flags, n->loc_seq);
+ new_bgp_ready =
+ zebra_vxlan_neigh_is_ready_for_bgp(n);
+ zebra_vxlan_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");
if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
#ifdef GNU_LINUX
if (state & NUD_STALE)
- zvni_neigh_install(zvni, n);
+ zvni_rem_neigh_install(zvni, n, false /*was_static*/);
#endif
} else {
/* We got a "remote" neighbor notification for an entry
return -1;
}
- UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
+ 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;
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;
}
{
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_vxlan_mac_stop_hold_timer(mac);
+
/* Cancel auto recovery */
THREAD_OFF(mac->dad_mac_auto_recovery_timer);
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);
+ &mac->macaddr, mac->flags, false);
+ }
+ if (wctx->uninstall) {
+ if (zebra_vxlan_mac_is_static(mac))
+ zebra_vxlan_sync_mac_dp_install(mac,
+ false /* set_inactive */,
+ true /* force_clear_static */,
+ __func__);
+
+ if (mac->flags & ZEBRA_MAC_REMOTE)
+ zvni_rem_mac_uninstall(wctx->zvni, mac);
}
- if (wctx->uninstall)
- zvni_mac_uninstall(wctx->zvni, mac);
zvni_mac_del(wctx->zvni, mac);
}
* 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, struct zebra_evpn_es *es)
+ 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))
/*
* Inform BGP about local MAC deletion.
*/
-static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr)
+static int zvni_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 zvni_macip_send_msg_to_client(vni, macaddr, NULL, 0 /* flags */,
0 /* seq */, ZEBRA_NEIGH_ACTIVE, NULL,
ZEBRA_MACIP_DEL);
/*
* Install remote MAC into the forwarding plane.
*/
-static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac)
+static int zvni_rem_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac,
+ bool was_static)
{
const struct zebra_if *zif, *br_zif;
const struct zebra_l2info_vxlan *vxl;
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;
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);
vid = 0;
res = dplane_rem_mac_add(zvni->vxlan_if, br_ifp, vid,
- &mac->macaddr, mac->fwd_info.r_vtep_ip, sticky,
- nhg_id, false /*was_static*/);
+ &mac->macaddr, vtep_ip, sticky,
+ nhg_id, was_static);
if (res != ZEBRA_DPLANE_REQUEST_FAILURE)
return 0;
else
/*
* Uninstall remote MAC from the forwarding plane.
*/
-static int zvni_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac)
+static int zvni_rem_mac_uninstall(zebra_vni_t *zvni, zebra_mac_t *mac)
{
const struct zebra_if *zif, *br_zif;
const struct zebra_l2info_vxlan *vxl;
mac = (zebra_mac_t *)bucket->data;
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
- zvni_mac_install(wctx->zvni, mac);
+ zvni_rem_mac_install(wctx->zvni, mac, false);
}
/*
*/
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) &&
remote_neigh_count(mac) == 0) {
- zvni_mac_uninstall(zvni, mac);
+ zvni_rem_mac_uninstall(zvni, mac);
zebra_evpn_es_mac_deref_entry(mac);
UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
}
return 0;
}
-static void process_sync_macip_add(zebra_vni_t *zvni,
- struct ethaddr *macaddr,
- uint16_t ipa_len,
- struct ipaddr *ipaddr,
- uint8_t flags,
- uint32_t seq,
- struct zebra_evpn_es *es)
+/**************************** 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_vxlan_mac_is_static(zebra_mac_t *mac)
{
- /* XXX - sync route */
+ return ((mac->flags & ZEBRA_MAC_ALL_PEER_FLAGS) ||
+ mac->sync_neigh_cnt);
}
-/* 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,
- esi_t *esi)
+/* mac needs to be locally active or active on an ES peer */
+static inline bool zebra_vxlan_mac_is_ready_for_bgp(uint32_t flags)
{
- 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;
- struct zebra_evpn_es *es;
- esi_t *old_esi;
+ return (flags & ZEBRA_MAC_LOCAL) &&
+ (!(flags & ZEBRA_MAC_LOCAL_INACTIVE) ||
+ (flags & ZEBRA_MAC_ES_PEER_ACTIVE));
+}
- /* Locate VNI hash entry - expected to exist. */
- zvni = zvni_lookup(vni);
- if (!zvni) {
- zlog_warn("Unknown VNI %u upon remote MACIP ADD", vni);
+/* program sync mac flags in the dataplane */
+void zebra_vxlan_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_vni_t *zvni = mac->zvni;
+ vlanid_t vid;
+ struct zebra_if *zif;
+ struct interface *br_ifp;
+
+ /* get the access vlan from the vxlan_device */
+ zebra_vxlan_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,
+ zvni->vni,
+ prefix_mac2str(&mac->macaddr, macbuf,
+ sizeof(macbuf)),
+ mac->es ?
+ mac->es->esi_str : "-",
+ mac->flags,
+ set_inactive ? "inactive " : "");
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);
+ 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,
+ zvni->vni,
+ prefix_mac2str(&mac->macaddr, macbuf,
+ sizeof(macbuf)),
+ mac->es ?
+ mac->es->esi_str : "-",
+ mac->flags,
+ set_inactive ? "inactive " : "");
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
+ sticky = !!CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ if (force_clear_static)
+ set_static = false;
+ else
+ set_static = zebra_vxlan_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",
+ zvni->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);
+
+}
+
+static void zebra_vxlan_mac_send_add_del_to_client(zebra_mac_t *mac,
+ bool old_bgp_ready, bool new_bgp_ready)
+{
+ if (new_bgp_ready)
+ zvni_mac_send_add_to_client(mac->zvni->vni,
+ &mac->macaddr, mac->flags,
+ mac->loc_seq, mac->es);
+ else if (old_bgp_ready)
+ zvni_mac_send_del_to_client(mac->zvni->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_vxlan_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
*/
- es = zebra_evpn_es_find(esi);
- if (es && (es->flags & ZEBRA_EVPNES_LOCAL)) {
- process_sync_macip_add(zvni, macaddr, ipa_len, ipaddr, flags,
- seq, es);
- return;
- }
+ if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE))
+ return 0;
- /* 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.
+ old_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags);
+ old_static = zebra_vxlan_mac_is_static(mac);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE);
+ new_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags);
+ new_static = zebra_vxlan_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->zvni->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 (vtep_ip.s_addr) {
- 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;
- }
+ if (old_static != new_static)
+ zebra_vxlan_sync_mac_dp_install(mac, false /* set_inactive */,
+ false /* force_clear_static */, __func__);
- zvni_vtep_install(zvni, zvtep);
- }
- }
+ /* inform bgp if needed */
+ if (old_bgp_ready != new_bgp_ready)
+ zebra_vxlan_mac_send_add_del_to_client(mac,
+ old_bgp_ready, new_bgp_ready);
- 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);
+ return 0;
+}
- mac = zvni_mac_lookup(zvni, macaddr);
+static inline void zebra_vxlan_mac_start_hold_timer(zebra_mac_t *mac)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
- /* 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)) : "");
+ if (mac->hold_timer)
return;
- }
- zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
- if (!zvrf)
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug("sync-mac vni %u mac %s es %s 0x%x hold started",
+ mac->zvni->vni,
+ prefix_mac2str(&mac->macaddr, macbuf,
+ sizeof(macbuf)),
+ mac->es ?
+ mac->es->esi_str : "-",
+ mac->flags);
+ thread_add_timer(zrouter.master,
+ zebra_vxlan_mac_hold_exp_cb,
+ mac, zmh_info->mac_hold_time,
+ &mac->hold_timer);
+}
+
+static inline void zebra_vxlan_mac_stop_hold_timer(zebra_mac_t *mac)
+{
+ char macbuf[ETHER_ADDR_STRLEN];
+
+ if (!mac->hold_timer)
return;
- old_esi = (mac && mac->es) ? &mac->es->esi : zero_esi;
+ if (IS_ZEBRA_DEBUG_EVPN_MH_MAC)
+ zlog_debug("sync-mac vni %u mac %s es %s 0x%x hold stopped",
+ mac->zvni->vni,
+ prefix_mac2str(&mac->macaddr, macbuf,
+ sizeof(macbuf)),
+ mac->es ?
+ mac->es->esi_str : "-",
+ mac->flags);
+ THREAD_OFF(mac->hold_timer);
+}
- /* 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
+static inline void zebra_vxlan_mac_clear_sync_info(zebra_mac_t *mac)
+{
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_ALL_PEER_FLAGS);
+ zebra_vxlan_mac_stop_hold_timer(mac);
+}
+
+static void zebra_vxlan_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->zvni->vni,
+ prefix_mac2str(&mac->macaddr,
+ macbuf, sizeof(macbuf)),
+ mac->es ? mac->es->esi_str : "-",
+ mac->loc_seq,
+ mac->flags);
+ old_static = zebra_vxlan_mac_is_static(mac);
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_PROXY);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE))
+ zebra_vxlan_mac_start_hold_timer(mac);
+ new_static = zebra_vxlan_mac_is_static(mac);
+
+ if (old_static != new_static)
+ /* program the local mac in the kernel */
+ zebra_vxlan_sync_mac_dp_install(mac, false /* set_inactive */,
+ false /* force_clear_static */, __func__);
+}
+
+static inline bool zebra_vxlan_mac_is_bgp_seq_ok(zebra_vni_t *zvni,
+ 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_vxlan_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",
+ zvni->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",
+ zvni->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;
+}
+
+/* sync-path that is active on an ES peer */
+static zebra_mac_t *zebra_vxlan_proc_sync_mac_update(zebra_vni_t *zvni,
+ 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 = zvni_mac_lookup(zvni, 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 = zvni_mac_add(zvni, 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_vxlan_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",
+ zvni->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_vxlan_mac_is_bgp_seq_ok(zvni, mac, seq,
+ ipa_len, ipaddr)) {
+ ctx->ignore_macip = true;
+ return NULL;
+ }
+
+ old_local = !!CHECK_FLAG(old_flags, ZEBRA_MAC_LOCAL);
+ old_static = zebra_vxlan_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_vxlan_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_vxlan_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",
+ zvni->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_vxlan_mac_is_static(mac);
+ if (old_static != new_static)
+ inform_dataplane = true;
+
+ old_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(old_flags);
+ new_bgp_ready = zebra_vxlan_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",
+ zvni->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_vxlan_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)
+ zvni_process_neigh_on_local_mac_change(zvni, 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_vxlan_sync_mac_dp_install(mac,
+ ctx->mac_inactive,
+ false /* force_clear_static */,
+ __func__);
+ }
+
+ return mac;
+}
+
+/**************************** SYNC neigh handling **************************/
+static inline bool zebra_vxlan_neigh_is_static(zebra_neigh_t *neigh)
+{
+ return !!(neigh->flags & ZEBRA_NEIGH_ALL_PEER_FLAGS);
+}
+
+static inline bool zebra_vxlan_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 void zebra_vxlan_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->zvni->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_vxlan_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->zvni->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);
+}
+
+static void zebra_vxlan_neigh_send_add_del_to_client(zebra_neigh_t *n,
+ bool old_bgp_ready, bool new_bgp_ready)
+{
+ if (new_bgp_ready)
+ zvni_neigh_send_add_to_client(n->zvni->vni, &n->ip,
+ &n->emac, n->mac, n->flags, n->loc_seq);
+ else if (old_bgp_ready)
+ zvni_neigh_send_del_to_client(n->zvni->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.
+ */
+static void zebra_vxlan_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_vxlan_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_vxlan_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_vxlan_mac_is_static(mac);
+
+ /* update the mac sync references in the dataplane */
+ if ((old_mac_static != new_mac_static) && !defer_mac_dp)
+ zebra_vxlan_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->zvni->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_vxlan_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_vxlan_neigh_is_ready_for_bgp(n);
+ old_n_static = zebra_vxlan_neigh_is_static(n);
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE);
+ new_bgp_ready = zebra_vxlan_neigh_is_ready_for_bgp(n);
+ new_n_static = zebra_vxlan_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->zvni->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_vxlan_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_vxlan_neigh_send_add_del_to_client(n,
+ old_bgp_ready, new_bgp_ready);
+
+ return 0;
+}
+
+static inline void zebra_vxlan_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->zvni->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ n->flags);
+ thread_add_timer(zrouter.master,
+ zebra_vxlan_neigh_hold_exp_cb,
+ n, zmh_info->neigh_hold_time,
+ &n->hold_timer);
+}
+
+static inline void zebra_vxlan_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->zvni->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ n->flags);
+ THREAD_OFF(n->hold_timer);
+}
+
+static inline bool zebra_vxlan_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->zvni->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ n->flags);
+
+ old_n_static = zebra_vxlan_neigh_is_static(n);
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_PEER_FLAGS);
+ new_n_static = zebra_vxlan_neigh_is_static(n);
+ if (old_n_static != new_n_static)
+ zebra_vxlan_sync_neigh_static_chg(n, old_n_static,
+ new_n_static, true /*defer_dp)*/,
+ false/*defer_mac_dp*/, __func__);
+ }
+ zebra_vxlan_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;
+}
+
+static void zebra_vxlan_local_neigh_deref_mac(zebra_neigh_t *n,
+ bool send_mac_update)
+{
+ zebra_mac_t *mac = n->mac;
+ zebra_vni_t *zvni = n->zvni;
+ 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_vxlan_mac_is_static(mac);
+ --mac->sync_neigh_cnt;
+ new_static = zebra_vxlan_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->zvni->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_vxlan_sync_mac_dp_install(mac,
+ false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+ }
+
+ listnode_delete(mac->neigh_list, n);
+ zvni_deref_ip2mac(zvni, mac);
+}
+
+static void zebra_vxlan_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_vxlan_mac_is_static(mac);
+ ++mac->sync_neigh_cnt;
+ new_static = zebra_vxlan_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->zvni->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_vxlan_sync_mac_dp_install(mac,
+ false /*set_inactive*/,
+ false /*force_clear_static*/,
+ __func__);
+ }
+}
+
+static inline bool zebra_vxlan_neigh_is_bgp_seq_ok(zebra_vni_t *zvni,
+ 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_vxlan_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",
+ zvni->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",
+ zvni->vni,
+ prefix_mac2str(macaddr,
+ macbuf, sizeof(macbuf)),
+ ipaddr2str(&n->ip,
+ ipbuf, sizeof(ipbuf)),
+ tmp_seq, n->flags);
+ return false;
+ }
+
+ return true;
+}
+
+static void zebra_vxlan_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->zvni->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ n->flags);
+
+ old_n_static = zebra_vxlan_neigh_is_static(n);
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_PROXY);
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_ES_PEER_ACTIVE))
+ zebra_vxlan_neigh_start_hold_timer(n);
+ new_n_static = zebra_vxlan_neigh_is_static(n);
+
+ if (old_n_static != new_n_static)
+ zebra_vxlan_sync_neigh_static_chg(n, old_n_static,
+ new_n_static, false /*defer-dp*/,
+ false /*defer_mac_dp*/, __func__);
+}
+
+static zebra_neigh_t *zebra_vxlan_proc_sync_neigh_update(zebra_vni_t *zvni,
+ 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;
+ struct zebra_if *zif;
+ char macbuf[ETHER_ADDR_STRLEN];
+ char ipbuf[INET6_ADDRSTRLEN];
+ bool created;
+ ifindex_t ifindex = 0;
+
+ /* locate l3-svi */
+ zif = zvni->vxlan_if->info;
+ if (zif) {
+ struct zebra_l2info_vxlan *vxl;
+
+ vxl = &zif->l2info.vxl;
+ ifp = zvni_map_to_svi(vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (ifp)
+ ifindex = ifp->ifindex;
+ }
+
+ is_router = !!CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+ old_mac_static = zebra_vxlan_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 = zvni_neigh_add(zvni, 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_vxlan_neigh_is_static(n);
+ old_bgp_ready = zebra_vxlan_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) {
+ zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
+ &n->emac, n->flags, n->state,
+ false /*force*/);
+ old_bgp_ready = false;
+ }
+ if (n->mac)
+ zebra_vxlan_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_vxlan_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_vxlan_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->zvni->vni,
+ ipaddr2str(&n->ip, ipbuf, sizeof(ipbuf)),
+ prefix_mac2str(&n->emac, macbuf,
+ sizeof(macbuf)),
+ old_flags, n->flags);
+
+ new_n_static = zebra_vxlan_neigh_is_static(n);
+ if (mac_change) {
+ set_dp_inactive = true;
+ n->flags |= ZEBRA_NEIGH_LOCAL_INACTIVE;
+ inform_dataplane = true;
+ zebra_vxlan_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_vxlan_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_vxlan_neigh_is_ready_for_bgp(n);
+ if (old_bgp_ready != new_bgp_ready)
+ inform_bgp = true;
+
+ new_mac_static = zebra_vxlan_mac_is_static(mac);
+ if ((old_mac_static != new_mac_static) ||
+ ctx->mac_dp_update_deferred)
+ zebra_vxlan_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->zvni->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_vxlan_sync_neigh_dp_install(n, set_dp_inactive,
+ false /* force_clear_static */, __func__);
+
+ if (inform_bgp)
+ zebra_vxlan_neigh_send_add_del_to_client(n,
+ old_bgp_ready, new_bgp_ready);
+
+ return n;
+}
+
+static void zebra_vxlan_process_sync_macip_add(zebra_vni_t *zvni,
+ 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",
+ zvni->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 = zvni_neigh_lookup(zvni, ipaddr);
+ if (n &&
+ !zebra_vxlan_neigh_is_bgp_seq_ok(zvni,
+ n, macaddr, seq))
+ return;
+ }
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.mac = zebra_vxlan_proc_sync_mac_update(zvni, macaddr, ipa_len,
+ ipaddr, flags, seq, esi, &ctx);
+ if (ctx.ignore_macip || !ctx.mac || !ipa_len)
+ return;
+
+ zebra_vxlan_proc_sync_neigh_update(zvni, n, ipa_len,
+ ipaddr, flags, seq, esi, &ctx);
+}
+
+/************************** remote mac-ip handling **************************/
+/* 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,
+ esi_t *esi)
+{
+ 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;
+ esi_t *old_esi;
+ bool old_static = 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;
+ }
+
+ /* 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_vxlan_process_sync_macip_add(zvni, 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 = 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 = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ if (!zvrf)
+ return;
+
+ 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)
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);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+ /* force drop the sync flags */
+ old_static = zebra_vxlan_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",
+ zvni->vni,
+ prefix_mac2str(macaddr,
+ buf, sizeof(buf)),
+ mac->es ?
+ mac->es->esi_str : "-",
+ mac->loc_seq,
+ mac->flags);
+ zebra_vxlan_mac_clear_sync_info(mac);
+ zvni_mac_send_del_to_client(zvni->vni, macaddr,
+ mac->flags, false /* force */);
+ }
/* Set "auto" and "remote" forwarding info. */
- UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ 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 (!is_dup_detect) {
zvni_process_neigh_on_remote_mac_add(zvni, mac);
/* Install the entry. */
- zvni_mac_install(zvni, mac);
+ zvni_rem_mac_install(zvni, mac, old_static);
}
}
/* Reset flag */
do_dad = false;
+ old_static = false;
/* Check if the remote neighbor itself is unknown or has a
* change. If so, create or update and then install the entry.
if (update_neigh) {
if (!n) {
- n = zvni_neigh_add(zvni, ipaddr, macaddr, mac);
+ n = zvni_neigh_add(zvni, ipaddr, macaddr, mac, 0);
if (!n) {
zlog_warn(
"Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
tmp_seq);
return;
}
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ old_static = zebra_vxlan_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->zvni->vni,
+ ipaddr2str(&n->ip, buf1,
+ sizeof(buf1)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ seq, n->flags);
+ zebra_vxlan_neigh_clear_sync_info(n);
+ if (IS_ZEBRA_NEIGH_ACTIVE(n))
+ zvni_mac_send_del_to_client(zvni->vni,
+ macaddr, mac->flags,
+ false /*force*/);
+ }
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) {
}
/* Set "remote" forwarding info. */
- UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
+ UNSET_FLAG(n->flags, ZEBRA_NEIGH_ALL_LOCAL_FLAGS);
n->r_vtep_ip = vtep_ip;
SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
false);
/* Install the entry. */
if (!is_dup_detect)
- zvni_neigh_install(zvni, n);
+ zvni_rem_neigh_install(zvni, n, old_static);
}
zvni_probe_neigh_on_mac_add(zvni, mac);
n->rem_seq = seq;
}
+static void zebra_vxlan_rem_mac_del(zebra_vni_t *zvni,
+ zebra_mac_t *mac)
+{
+ 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_rem_mac_uninstall(zvni, mac);
+ zebra_evpn_es_mac_deref_entry(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);
+}
+
/* Process a remote MACIP delete from BGP. */
static void process_remote_macip_del(vni_t vni,
struct ethaddr *macaddr,
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);
* "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);
+ if (!memcmp(n->emac.octet, macaddr->octet, ETH_ALEN)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ zebra_vxlan_sync_neigh_del(n);
+ } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ 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,
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);
- zebra_evpn_es_mac_deref_entry(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);
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) {
+ if (!ipa_len)
+ zebra_vxlan_sync_mac_del(mac);
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_NEIGH_REMOTE)) {
+ zebra_vxlan_rem_mac_del(zvni, mac);
}
}
}
vty_out(vty,
"Number of ARPs (local and remote) known for this VNI: %u\n",
num_neigh);
- vty_out(vty, "%*s %-6s %-8s %-17s %-30s %s\n", -wctx.addr_width,
- "IP", "Type", "State", "MAC", "Remote ES/VTEP", "Seq #'s");
+ zvni_print_neigh_hdr(vty, &wctx);
} else
json_object_int_add(json, "numArpNd", num_neigh);
vty_out(vty,
"Number of MACs (local and remote) known for this VNI: %u\n",
num_macs);
- vty_out(vty, "%-17s %-6s %-30s %-5s %s\n", "MAC", "Type",
- "Intf/Remote ES/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);
vty_out(vty,
"Number of MACs (local and remote) known for this VNI: %u\n",
num_macs);
- vty_out(vty, "%-17s %-6s %-30s %-5s\n", "MAC", "Type",
- "Intf/Remote ES/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);
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);
+ zvni_rem_neigh_install(zvni, nbr,
+ false /*was_static*/);
}
UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
zvni_process_neigh_on_remote_mac_add(zvni, mac);
/* Install the entry. */
- zvni_mac_install(zvni, mac);
+ zvni_rem_mac_install(zvni, mac, false /* was_static */);
}
return 0;
&nbr->emac, nbr->mac,
nbr->flags, nbr->loc_seq);
} else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
- zvni_neigh_install(zvni, nbr);
+ zvni_rem_neigh_install(zvni, nbr, false /*was_static*/);
}
return 0;
zvni_process_neigh_on_remote_mac_add(zvni, mac);
/* Install the entry. */
- zvni_mac_install(zvni, mac);
+ zvni_rem_mac_install(zvni, mac, false /* was_static */);
}
}
&nbr->emac, nbr->mac,
nbr->flags, nbr->loc_seq);
} else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
- zvni_neigh_install(zvni, nbr);
+ zvni_rem_neigh_install(zvni, nbr, false /*was_static*/);
}
}
zebra_mac_t *zmac = NULL;
zebra_l3vni_t *zl3vni = NULL;
struct zebra_vrf *zvrf;
+ bool old_bgp_ready;
+ bool new_bgp_ready;
/* check if this is a remote neigh entry corresponding to remote
* next-hop
* 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);
+ zvni_rem_neigh_install(zvni, 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_vxlan_neigh_is_ready_for_bgp(n);
+ if (zebra_vxlan_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->zvni->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_vxlan_neigh_is_ready_for_bgp(n);
+ zebra_vxlan_neigh_send_add_del_to_client(n,
+ old_bgp_ready, new_bgp_ready);
+
+ /* re-install the entry in the kernel */
+ zebra_vxlan_sync_neigh_dp_install(n, false /* set_inactive */,
+ false /* force_clear_static */, __func__);
+
return 0;
}
/* Remove neighbor from BGP. */
zvni_neigh_send_del_to_client(zvni->vni, &n->ip,
- &n->emac, 0, n->state);
+ &n->emac, n->flags, n->state,
+ false /* force */);
/* Delete this neighbor entry. */
zvni_neigh_del(zvni, n);
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];
if (!zvni)
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 " : "",
+ local_inactive ? "local_inactive " : "",
zvni->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);
+ is_router, local_inactive, dp_static);
return zvni_remote_neigh_update(zvni, ifp, ip, macaddr, state);
}
else
strcpy(esi_buf, "-");
zlog_debug(
- "Recv MACIP ADD VNI %u MAC %s%s%s flags 0x%x seq %u VTEP %s ESI %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 " : "",
ifp->ifindex, vni, mac->flags);
/* Remove MAC from BGP. */
- zvni_mac_send_del_to_client(zvni->vni, macaddr);
+ zvni_mac_send_del_to_client(zvni->vni, macaddr,
+ mac->flags, false /* force */);
/*
* If there are no neigh associated with the mac delete the mac
if (!listcount(mac->neigh_list)) {
zvni_mac_del(zvni, 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);
}
prefix_mac2str(macaddr, buf, sizeof(buf)), ifp->name,
ifp->ifindex, vni);
- zvni_mac_install(zvni, mac);
+ zvni_rem_mac_install(zvni, mac, false /* was_static */);
return 0;
}
zebra_vni_t *zvni;
zebra_mac_t *mac;
char buf[ETHER_ADDR_STRLEN];
+ bool old_bgp_ready;
+ bool new_bgp_ready;
/* We are interested in MACs only on ports or (port, VLAN) that
* map to a VNI.
ifp->ifindex, vid, zvni->vni, mac->loc_seq,
mac->flags, listcount(mac->neigh_list));
+ old_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(mac->flags);
+ if (zebra_vxlan_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",
+ zvni->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_vxlan_mac_is_ready_for_bgp(mac->flags);
+ zebra_vxlan_mac_send_add_del_to_client(mac,
+ old_bgp_ready, new_bgp_ready);
+ }
+
+ /* re-install the entry in the kernel */
+ zebra_vxlan_sync_mac_dp_install(mac, false /* set_inactive */,
+ false /* force_clear_static */,
+ __func__);
+
+ return 0;
+ }
+
/* 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);
+ zvni_mac_send_del_to_client(zvni->vni, macaddr,
+ mac->flags, false /* force */);
zebra_evpn_es_mac_deref_entry(mac);
if (!listcount(mac->neigh_list)) {
zvni_mac_del(zvni, 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);
}
return 0;
}
+/* update local fowarding info. return true if a dest-ES change
+ * is detected
+ */
+static bool zebra_vxlan_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;
+}
+
/*
* Handle local MAC add (on a port or VLAN corresponding to this VNI).
*/
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;
bool upd_neigh = false;
bool is_dup_detect = false;
struct in_addr vtep_ip = {.s_addr = 0};
- struct zebra_if *zif = ifp->info;
- bool es_change;
+ 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;
/* We are interested in MACs only on ports or (port, VLAN) that
* map to a VNI.
/* 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)
+ if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
- "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u",
+ "ADD %sMAC %s intf %s(%u) VID %u -> VNI %u%s",
sticky ? "sticky " : "",
prefix_mac2str(macaddr, buf, sizeof(buf)),
- ifp->name, ifp->ifindex, vid, zvni->vni);
+ ifp->name, ifp->ifindex, vid, zvni->vni,
+ local_inactive ? " local-inactive" : "");
mac = zvni_mac_add(zvni, macaddr);
if (!mac) {
return -1;
}
SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
- mac->fwd_info.local.ifindex = ifp->ifindex;
- mac->fwd_info.local.vid = vid;
+ es_change = zebra_vxlan_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)
+ if (IS_ZEBRA_DEBUG_VXLAN || IS_ZEBRA_DEBUG_EVPN_MH_MAC)
zlog_debug(
- "UPD %sMAC %s intf %s(%u) VID %u -> VNI %u curFlags 0x%x",
+ "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, zvni->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_vxlan_mac_get_access_info(mac,
+ &old_ifp, &old_vid);
+ old_bgp_ready = zebra_vxlan_mac_is_ready_for_bgp(
+ mac->flags);
+ old_local_inactive = !!(mac->flags &
+ ZEBRA_MAC_LOCAL_INACTIVE);
+ old_static = zebra_vxlan_mac_is_static(mac);
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY))
mac_sticky = true;
* BGP, note it.
*/
if (mac_sticky == sticky
- && mac->fwd_info.local.ifindex == ifp->ifindex
- && mac->fwd_info.local.vid == vid) {
+ && 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, entry exists and has not changed ",
+ " 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,
- zvni->vni);
+ zvni->vni,
+ local_inactive ?
+ " local_inactive" : "");
return 0;
}
if (mac_sticky != sticky) {
inform_client = true;
}
- memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
- mac->fwd_info.local.ifindex = ifp->ifindex;
- mac->fwd_info.local.vid = vid;
-
+ es_change = zebra_vxlan_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_vxlan_mac_clear_sync_info(mac);
+ new_static =
+ zebra_vxlan_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;
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.vid = vid;
+ es_change = zebra_vxlan_local_mac_update_fwd_info(mac,
+ ifp, vid);
if (sticky)
SET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
else
}
}
- if (zebra_evpn_es_mac_ref_entry(mac, zif->es_info.es)) {
- /* if ES associated with the MAC changed the neigh must be
- * updated as well
- */
+ /* 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_vxlan_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_vxlan_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",
+ zvni->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;
- es_change = true;
- } else {
- es_change = false;
}
+ /* Inform dataplane if required. */
+ if (inform_dataplane)
+ zebra_vxlan_sync_mac_dp_install(mac, false /* set_inactive */,
+ false /* force_clear_static */, __func__);
+
/* Inform BGP if required. */
- if (inform_client) {
- if (zvni_mac_send_add_to_client(zvni->vni, macaddr,
- mac->flags, mac->loc_seq, mac->es))
- return -1;
- }
+ if (inform_client)
+ zebra_vxlan_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)
zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, &nbr->emac,
nbr->mac, nbr->flags, nbr->loc_seq);
} else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) {
- zvni_neigh_install(zvni, nbr);
+ zvni_rem_neigh_install(zvni, nbr, false /*was_static*/);
}
return 0;
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);
+ zvni_rem_neigh_install(zvni, nbr,
+ false /*was_static*/);
}
UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE);
zvni_process_neigh_on_remote_mac_add(zvni, mac);
/* Install the entry. */
- zvni_mac_install(zvni, mac);
+ zvni_rem_mac_install(zvni, mac, false /* was_static */);
}
return 0;