From b169fd6fd59ed0c745c6f32a878d7fcc981e8d33 Mon Sep 17 00:00:00 2001 From: Anuradha Karuppiah Date: Sun, 29 Mar 2020 08:43:55 -0700 Subject: [PATCH] zebra: support for MAC-IP sync routes MAC-IP routes are used for syncing local entries across redundant switches in an EVPN-MH setup. A path from a peer that has a local ES as destination is tagged as a SYNC path. The SYNC path results in the addition of local MAC and/or local neigh entry in zebra and in the dataplane. Implementation overview ======================= 1. Three new flags "local-inactive", "peer-active" and "peer-proxy" are maintained per-local-MAC and per-local-Neigh entry. 2. The "peer-XXX" flags are set and cleared via SYNC path updates from BGP. Proxy sync paths result in the setting of "peer-proxy" flag (and non-proxies result in the "peer-active"). 3. A neigh entry that has a "peer-XXX" flag set is programmed as "static" in the dataplane. 4. A MAC entry that has a "peer-XXX" flag set or is referenced by a sync-neigh entry (that has a "peer-XXX" flags set) is programmed as "static" in the dataplane. 5. The sync-seq number is used to normalize the MM seq number across all the redundant switches i.e. the max MM seq number across all switches is used by each of the switches. This commit also includes the changes needed for extended MM seq syncing. 6. A MAC/neigh entry has to be local-active or peer-active to sent to BGP. An entry that is NOT local-active is sent with the proxy flag (so BGP can "proxy" advertise it). 7. The "peer-active" flag is aged out by zebra by using a hold_timer (this is instead of being abruptly dropped on SYNC path delete). This age-out is needed to handle peer-switch restart (procedures are specified in draft-rbickhart-evpn-ip-mac-proxy-adv). The holdtime needs to be sufficiently long to allow an external neighmgr daemon or the dataplane component to independently probe and establish local reachability of a host. The MAC and neigh hold time values are configurable. PS: In the future this probing may happen in FRR itself. CLI changes to display sync info ================================ MAC === >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> root@torm-11:mgmt:~# net show evpn mac vni 1000 Number of MACs (local and remote) known for this VNI: 6 Flags: N=sync-neighs, I=local-inactive, P=peer-active, X=peer-proxy MAC Type Flags Intf/Remote ES/VTEP VLAN Seq #'s 00:02:00:00:00:25 local vlan1000 1000 0/0 02:02:00:00:00:02 local PI hostbond1 1000 0/0 02:02:00:00:00:06 remote 03:00:00:00:00:02:11:00:00:01 0/0 02:02:00:00:00:01 local X hostbond1 1000 0/0 00:00:00:00:00:11 local PI hostbond1 1000 0/0 02:02:00:00:00:05 remote 03:00:00:00:00:02:11:00:00:01 0/0 root@torm-11:mgmt:~# root@torm-11:mgmt:~# net show evpn mac vni 1000 mac 00:00:00:00:00:11 MAC: 00:00:00:00:00:11 ESI: 03:00:00:00:00:01:11:00:00:01 Intf: hostbond1(58) VLAN: 1000 Sync-info: neigh#: 0 local-inactive peer-active >>>>>>>>>>>> Local Seq: 0 Remote Seq: 0 Neighbors: No Neighbors root@torm-11:mgmt:~# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> neigh ===== >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> root@torm-11:mgmt:~# net show evpn arp vni 1003 Number of ARPs (local and remote) known for this VNI: 4 Flags: I=local-inactive, P=peer-active, X=peer-proxy Neighbor Type Flags State MAC Remote ES/VTEP Seq #'s 2001:fee1:0:3::6 local active 00:02:00:00:00:25 0/0 45.0.3.66 local P active 00:02:00:00:00:66 0/0 45.0.3.6 local active 00:02:00:00:00:25 0/0 fe80::202:ff:fe00:25 local active 00:02:00:00:00:25 0/0 root@torm-11:mgmt:~# root@torm-11:mgmt:~# net show evpn arp vni 1003 ip 45.0.3.66 IP: 45.0.3.66 Type: local State: active MAC: 00:02:00:00:00:66 Sync-info: peer-active >>>>>>>>>>>>>>>> Local Seq: 0 Remote Seq: 0 root@torm-11:mgmt:~# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Signed-off-by: Anuradha Karuppiah --- zebra/rt_netlink.c | 2 +- zebra/zebra_dplane.h | 3 +- zebra/zebra_evpn_mh.c | 91 +- zebra/zebra_evpn_mh.h | 17 +- zebra/zebra_vxlan.c | 2375 ++++++++++++++++++++++++++++++----- zebra/zebra_vxlan.h | 5 +- zebra/zebra_vxlan_private.h | 55 + 7 files changed, 2189 insertions(+), 359 deletions(-) diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c index edab6d4646..d571d7f5e3 100644 --- a/zebra/rt_netlink.c +++ b/zebra/rt_netlink.c @@ -2733,7 +2733,7 @@ static ssize_t netlink_neigh_update_msg_encode( return 0; } if (nfy) { - if (!nl_attr_put32(&req->n, datalen, NDA_NOTIFY, + if (!nl_attr_put(&req->n, datalen, NDA_NOTIFY, &nfy_flags, sizeof(nfy_flags))) return 0; } diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 71818f2cfe..32032ed77d 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -543,8 +543,7 @@ void dplane_mac_init(struct zebra_dplane_ctx *ctx, const struct ethaddr *mac, struct in_addr vtep_ip, bool sticky, - uint32_t nhg_id, - uint32_t update_flags); + uint32_t nhg_id, uint32_t update_flags); /* * Enqueue evpn neighbor updates for the dataplane. diff --git a/zebra/zebra_evpn_mh.c b/zebra/zebra_evpn_mh.c index c55e9d3ec2..435e110212 100644 --- a/zebra/zebra_evpn_mh.c +++ b/zebra/zebra_evpn_mh.c @@ -1085,6 +1085,10 @@ static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi) 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); @@ -1098,15 +1102,15 @@ static struct zebra_evpn_es *zebra_evpn_es_new(esi_t *esi) * This just frees appropriate memory, caller should have taken other * needed actions. */ -static void zebra_evpn_es_free(struct zebra_evpn_es *es) +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)) || - es->mac_cnt) - return; + listcount(es->mac_list)) + return es; if (IS_ZEBRA_DEBUG_EVPN_MH_ES) zlog_debug("es %s free", es->esi_str); @@ -1121,11 +1125,14 @@ static void zebra_evpn_es_free(struct zebra_evpn_es *es) /* 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 */ @@ -1278,6 +1285,21 @@ static void zebra_evpn_es_setup_evis(struct zebra_evpn_es *es) } } +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_vxlan_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) { @@ -1314,6 +1336,12 @@ static void zebra_evpn_es_local_info_set(struct zebra_evpn_es *es, * 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) @@ -1324,6 +1352,12 @@ static void zebra_evpn_es_local_info_clear(struct zebra_evpn_es *es) 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; @@ -1539,11 +1573,11 @@ void zebra_evpn_es_mac_deref_entry(zebra_mac_t *mac) struct zebra_evpn_es *es = mac->es; mac->es = NULL; - if (!es || !es->mac_cnt) + if (!es) return; - --es->mac_cnt; - if (!es->mac_cnt) + list_delete_node(es->mac_list, &mac->es_listnode); + if (!listcount(es->mac_list)) zebra_evpn_es_free(es); } @@ -1562,11 +1596,13 @@ bool zebra_evpn_es_mac_ref_entry(zebra_mac_t *mac, struct zebra_evpn_es *es) return true; mac->es = es; - ++es->mac_cnt; + listnode_init(&mac->es_listnode, mac); + listnode_add(es->mac_list, &mac->es_listnode); + return true; } -void zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi) +bool zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi) { struct zebra_evpn_es *es; @@ -1577,7 +1613,7 @@ void zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi) zlog_debug("auto es %s add on mac ref", es->esi_str); } - zebra_evpn_es_mac_ref_entry(mac, es); + return zebra_evpn_es_mac_ref_entry(mac, es); } /* Inform BGP about local ES-EVI add or del */ @@ -1769,7 +1805,7 @@ static void zebra_evpn_es_show_entry_detail(struct vty *vty, (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", es->mac_cnt); + 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)) @@ -2037,6 +2073,39 @@ static void zebra_evpn_es_get_one_base_vni(void) } /*****************************************************************************/ +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); @@ -2047,6 +2116,8 @@ 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(); diff --git a/zebra/zebra_evpn_mh.h b/zebra/zebra_evpn_mh.h index 795053d649..46c25a04bc 100644 --- a/zebra/zebra_evpn_mh.h +++ b/zebra/zebra_evpn_mh.h @@ -69,8 +69,8 @@ struct zebra_evpn_es { /* [!EVPNES_LOCAL] List of remote VTEPs (zebra_evpn_es_vtep) */ struct list *es_vtep_list; - /* zebra_mac_t entries using this ES as destination */ - uint32_t mac_cnt; + /* list of zebra_mac entries using this ES as destination */ + struct list *mac_list; /* Nexthop group id */ uint32_t nhg_id; @@ -171,6 +171,12 @@ struct zebra_evpn_mh_info { */ #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) @@ -215,7 +221,7 @@ extern void zebra_evpn_es_evi_show_vni(struct vty *vty, bool uj, 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 void zebra_evpn_es_mac_ref(zebra_mac_t *mac, esi_t *esi); +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); @@ -224,5 +230,10 @@ 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_vxlan.c b/zebra/zebra_vxlan.c index d2916cfcee..5f6c94c323 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -101,18 +101,20 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, 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, @@ -159,12 +161,14 @@ 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, 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); @@ -225,6 +229,22 @@ 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); +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, @@ -731,6 +751,7 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) 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) @@ -743,17 +764,58 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) 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) { @@ -821,6 +883,29 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) } } +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. */ @@ -833,6 +918,7 @@ static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) 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; @@ -849,9 +935,10 @@ static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) 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); @@ -881,13 +968,11 @@ static void zvni_print_neigh_hash(struct hash_bucket *bucket, void *ctxt) 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); @@ -1003,11 +1088,9 @@ static void zvni_print_neigh_hash_all_vni(struct hash_bucket *bucket, 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); @@ -1180,6 +1263,35 @@ static void zl3vni_print_rmac(zebra_mac_t *zrmac, struct vty *vty, } } +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. */ @@ -1193,6 +1305,7 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) 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) @@ -1205,21 +1318,21 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *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; + 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( @@ -1248,6 +1361,22 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) 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"); @@ -1287,19 +1416,21 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) 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", @@ -1321,8 +1452,22 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) 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)) { @@ -1359,6 +1504,21 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) } } +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. */ @@ -1369,6 +1529,7 @@ static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt) 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; @@ -1380,26 +1541,23 @@ static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt) 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) @@ -1439,14 +1597,16 @@ static void zvni_print_mac_hash(struct hash_bucket *bucket, void *ctxt) 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); @@ -1563,8 +1723,11 @@ static void zvni_print_mac_hash_all_vni(struct hash_bucket *bucket, void *ctxt) 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); } @@ -2250,7 +2413,8 @@ static void *zvni_neigh_alloc(void *p) * 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; @@ -2260,16 +2424,15 @@ static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, 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; } @@ -2310,10 +2473,18 @@ static void zvni_neigh_del_hash_entry(struct hash_bucket *bucket, void *arg) && 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); } @@ -2426,7 +2597,9 @@ static void zvni_process_neigh_on_local_mac_del(zebra_vni_t *zvni, 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*/); } } } @@ -2457,7 +2630,9 @@ static void zvni_process_neigh_on_remote_mac_add(zebra_vni_t *zvni, 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 */); } } } @@ -2491,11 +2666,26 @@ static void zvni_probe_neigh_on_mac_add(zebra_vni_t *zvni, zebra_mac_t *zmac) 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 */ @@ -2514,9 +2704,16 @@ static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, * 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); } @@ -2524,7 +2721,8 @@ static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, /* * 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; @@ -2550,7 +2748,7 @@ static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n) ZEBRA_NEIGH_SET_ACTIVE(n); dplane_rem_neigh_add(vlan_if, &n->ip, &n->emac, flags, - false /*was_static*/); + was_static); return ret; } @@ -2624,7 +2822,7 @@ static void zvni_install_neigh_hash(struct hash_bucket *bucket, void *ctxt) 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 */ @@ -2788,7 +2986,7 @@ static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni, 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, @@ -2883,7 +3081,8 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, /* 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); @@ -3031,11 +3230,36 @@ static void zvni_svi_macip_del_for_vni_hash(struct hash_bucket *bucket, 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]; @@ -3049,6 +3273,11 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, 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); @@ -3096,7 +3325,7 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, 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, @@ -3109,17 +3338,28 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, /* 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"); @@ -3127,7 +3367,9 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, 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. */ @@ -3138,6 +3380,15 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, 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. */ @@ -3146,14 +3397,20 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, 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"); } @@ -3166,27 +3423,32 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, * 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 @@ -3202,16 +3464,13 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, 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 @@ -3240,12 +3499,27 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, 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 */ @@ -3268,16 +3542,28 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, 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. @@ -3300,9 +3586,10 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, 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"); @@ -3330,7 +3617,7 @@ static int zvni_remote_neigh_update(zebra_vni_t *zvni, 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 @@ -3348,7 +3635,7 @@ static int zvni_remote_neigh_update(zebra_vni_t *zvni, 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; @@ -3419,6 +3706,15 @@ static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr) 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; } @@ -3429,9 +3725,22 @@ static int zvni_mac_del(zebra_vni_t *zvni, 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_vxlan_mac_stop_hold_timer(mac); + /* Cancel auto recovery */ THREAD_OFF(mac->dad_mac_auto_recovery_timer); @@ -3487,10 +3796,18 @@ static void zvni_mac_del_hash_entry(struct hash_bucket *bucket, void *arg) 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); } @@ -3537,10 +3854,25 @@ static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *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)) @@ -3554,8 +3886,16 @@ static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, /* * 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); @@ -3790,7 +4130,8 @@ static struct interface *zvni_map_to_macvlan(struct interface *br_if, /* * 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; @@ -3799,6 +4140,7 @@ static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac) 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; @@ -3823,8 +4165,10 @@ static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac) 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); @@ -3835,8 +4179,8 @@ static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac) 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 @@ -3846,7 +4190,7 @@ static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac) /* * 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; @@ -3903,7 +4247,7 @@ static void zvni_install_mac_hash(struct hash_bucket *bucket, void *ctxt) 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); } /* @@ -3937,7 +4281,7 @@ static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac) */ 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); } @@ -5557,129 +5901,1278 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni, 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) @@ -5754,11 +7247,25 @@ static void process_remote_macip_add(vni_t vni, 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; @@ -5781,7 +7288,7 @@ static void process_remote_macip_add(vni_t vni, 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); } } @@ -5796,6 +7303,7 @@ static void process_remote_macip_add(vni_t vni, /* 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. @@ -5811,7 +7319,7 @@ static void process_remote_macip_add(vni_t vni, 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", @@ -5853,16 +7361,23 @@ static void process_remote_macip_add(vni_t vni, 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) { @@ -5896,7 +7411,7 @@ static void process_remote_macip_add(vni_t vni, } /* 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); @@ -5926,7 +7441,7 @@ static void process_remote_macip_add(vni_t vni, 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); @@ -5935,6 +7450,32 @@ static void process_remote_macip_add(vni_t vni, 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, @@ -5976,11 +7517,6 @@ static void process_remote_macip_del(vni_t vni, 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); @@ -6039,11 +7575,14 @@ static void process_remote_macip_del(vni_t vni, * "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, @@ -6066,28 +7605,11 @@ static void process_remote_macip_del(vni_t vni, 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); } } } @@ -6523,8 +8045,7 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, 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); @@ -6789,8 +8310,11 @@ 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 %-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); @@ -6982,8 +8506,8 @@ 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 %-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); @@ -7040,7 +8564,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); + zvni_rem_neigh_install(zvni, nbr, + false /*was_static*/); } UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); @@ -7077,7 +8602,7 @@ int zebra_vxlan_clear_dup_detect_vni_mac(struct zebra_vrf *zvrf, vni_t vni, 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; @@ -7140,7 +8665,7 @@ int zebra_vxlan_clear_dup_detect_vni_ip(struct zebra_vrf *zvrf, vni_t vni, &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; @@ -7198,7 +8723,7 @@ static void zvni_clear_dup_mac_hash(struct hash_bucket *bucket, void *ctxt) 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 */); } } @@ -7236,7 +8761,7 @@ static void zvni_clear_dup_neigh_hash(struct hash_bucket *bucket, void *ctxt) &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*/); } } @@ -7632,6 +9157,8 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, 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 @@ -7686,7 +9213,36 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, * 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; } @@ -7707,7 +9263,8 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, /* 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); @@ -7732,7 +9289,8 @@ 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]; @@ -7753,19 +9311,20 @@ int zebra_vxlan_handle_kernel_neigh_update(struct interface *ifp, 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); } @@ -7915,7 +9474,9 @@ void zebra_vxlan_remote_macip_add(ZAPI_HANDLER_ARGS) 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 " : "", @@ -8025,7 +9586,8 @@ 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); + 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 @@ -8034,7 +9596,7 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp, 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); } @@ -8092,7 +9654,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); + zvni_rem_mac_install(zvni, mac, false /* was_static */); return 0; } @@ -8105,6 +9667,8 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, 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. @@ -8134,11 +9698,44 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, 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); @@ -8149,7 +9746,7 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, 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); } @@ -8157,13 +9754,36 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if, 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; @@ -8174,8 +9794,13 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, 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. @@ -8210,12 +9835,13 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, /* 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) { @@ -8227,22 +9853,33 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, 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; @@ -8251,16 +9888,20 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, * 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) { @@ -8273,10 +9914,31 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, 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; @@ -8310,9 +9972,8 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, 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 @@ -8335,22 +9996,53 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, } } - 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) @@ -9984,7 +11676,7 @@ static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t) 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; @@ -10027,7 +11719,8 @@ static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t) 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); @@ -10057,7 +11750,7 @@ static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t) 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; diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 0183b73664..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); diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index a9f62fde10..e2eae56873 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -315,6 +315,23 @@ struct zebra_mac_t_ { #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 zvni */ zebra_vni_t *zvni; @@ -331,6 +348,8 @@ struct zebra_mac_t_ { /* 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; @@ -350,6 +369,14 @@ struct zebra_mac_t_ { 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; }; /* @@ -381,6 +408,17 @@ struct rmac_walk_ctx { 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; +}; + #define IS_ZEBRA_NEIGH_ACTIVE(n) (n->state == ZEBRA_NEIGH_ACTIVE) #define IS_ZEBRA_NEIGH_INACTIVE(n) (n->state == ZEBRA_NEIGH_INACTIVE) @@ -423,6 +461,18 @@ struct zebra_neigh_t_ { #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; @@ -450,6 +500,9 @@ struct zebra_neigh_t_ { struct timeval detect_start_time; time_t dad_dup_detect_time; + + /* used for ageing out the PEER_ACTIVE flag */ + struct thread *hold_timer; }; /* @@ -527,5 +580,7 @@ typedef struct zebra_vxlan_sg_ { } zebra_vxlan_sg_t; extern zebra_vni_t *zvni_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 */ -- 2.39.5