diff options
| -rw-r--r-- | zebra/rt_netlink.c | 2 | ||||
| -rw-r--r-- | zebra/zebra_dplane.h | 3 | ||||
| -rw-r--r-- | zebra/zebra_evpn_mh.c | 91 | ||||
| -rw-r--r-- | zebra/zebra_evpn_mh.h | 17 | ||||
| -rw-r--r-- | zebra/zebra_vxlan.c | 2191 | ||||
| -rw-r--r-- | zebra/zebra_vxlan.h | 5 | ||||
| -rw-r--r-- | zebra/zebra_vxlan_private.h | 55 |
7 files changed, 2097 insertions, 267 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,17 +5901,1167 @@ static int zebra_vxlan_readd_remote_rmac(zebra_l3vni_t *zl3vni, return 0; } -static void process_sync_macip_add(zebra_vni_t *zvni, +/**************************** 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) +{ + return ((mac->flags & ZEBRA_MAC_ALL_PEER_FLAGS) || + mac->sync_neigh_cnt); +} + +/* mac needs to be locally active or active on an ES peer */ +static inline bool zebra_vxlan_mac_is_ready_for_bgp(uint32_t flags) +{ + return (flags & ZEBRA_MAC_LOCAL) && + (!(flags & ZEBRA_MAC_LOCAL_INACTIVE) || + (flags & ZEBRA_MAC_ES_PEER_ACTIVE)); +} + +/* 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; + } + + 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; + } + + 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 + */ + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_ES_PEER_ACTIVE)) + return 0; + + 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 (old_static != new_static) + zebra_vxlan_sync_mac_dp_install(mac, false /* set_inactive */, + false /* force_clear_static */, __func__); + + /* inform bgp if needed */ + if (old_bgp_ready != new_bgp_ready) + zebra_vxlan_mac_send_add_del_to_client(mac, + old_bgp_ready, new_bgp_ready); + + return 0; +} + +static inline void zebra_vxlan_mac_start_hold_timer(zebra_mac_t *mac) +{ + char macbuf[ETHER_ADDR_STRLEN]; + + if (mac->hold_timer) + return; + + if (IS_ZEBRA_DEBUG_EVPN_MH_MAC) + zlog_debug("sync-mac vni %u mac %s es %s 0x%x hold started", + mac->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; + + 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); +} + +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, - struct zebra_evpn_es *es) + esi_t *esi) { - /* XXX - sync route */ + 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, @@ -5594,8 +7088,8 @@ static void process_remote_macip_add(vni_t vni, bool is_router; bool do_dad = false; bool is_dup_detect = false; - struct zebra_evpn_es *es; esi_t *old_esi; + bool old_static = false; /* Locate VNI hash entry - expected to exist. */ zvni = zvni_lookup(vni); @@ -5621,10 +7115,9 @@ static void process_remote_macip_add(vni_t vni, * SYNC - if ES is local * REMOTE - if ES is not local */ - 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); + if (flags & ZEBRA_MACIP_TYPE_SYNC_PATH) { + zebra_vxlan_process_sync_macip_add(zvni, macaddr, ipa_len, + ipaddr, flags, seq, esi); return; } @@ -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 */ |
