diff options
Diffstat (limited to 'zebra/zebra_vxlan.c')
| -rw-r--r-- | zebra/zebra_vxlan.c | 1835 |
1 files changed, 1783 insertions, 52 deletions
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index d372d3e832..49af4a9205 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -66,6 +66,7 @@ static int ip_prefix_send_to_client(vrf_id_t vrf_id, struct prefix *p, uint16_t cmd); static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json); static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt); +static void zvni_print_dad_neigh_hash(struct hash_backet *backet, void *ctxt); static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, void **args); static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, @@ -80,7 +81,7 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]); static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, struct ipaddr *ip, uint8_t flags, - uint32_t seq, uint16_t cmd); + uint32_t seq, int state, uint16_t cmd); static unsigned int neigh_hash_keymake(void *p); static void *zvni_neigh_alloc(void *p); static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip, @@ -96,7 +97,7 @@ static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, uint8_t flags, uint32_t seq); static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, struct ethaddr *macaddr, - uint8_t flags); + uint8_t flags, int state); static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n); static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n); static zebra_vni_t *zvni_from_svi(struct interface *ifp, @@ -162,7 +163,7 @@ static zebra_vni_t *zvni_add(vni_t vni); static int zvni_del(zebra_vni_t *zvni); static int zvni_send_add_to_client(zebra_vni_t *zvni); static int zvni_send_del_to_client(vni_t vni); -static void zvni_build_hash_table(); +static void zvni_build_hash_table(void); static int zvni_vtep_match(struct in_addr *vtep_ip, zebra_vtep_t *zvtep); static zebra_vtep_t *zvni_vtep_find(zebra_vni_t *zvni, struct in_addr *vtep_ip); static zebra_vtep_t *zvni_vtep_add(zebra_vni_t *zvni, struct in_addr *vtep_ip); @@ -178,8 +179,26 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni, struct ipaddr *ip); struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp); static int advertise_gw_macip_enabled(zebra_vni_t *zvni); +static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, + zebra_mac_t *old_zmac, + zebra_mac_t *new_zmac, + zebra_neigh_t *nbr); static int remote_neigh_count(zebra_mac_t *zmac); static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac); +static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t); +static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t); +static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, + zebra_neigh_t *nbr, + struct in_addr vtep_ip, + bool do_dad, + bool *is_dup_detect, + bool is_local); +static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, + zebra_mac_t *mac, + struct in_addr vtep_ip, + bool do_dad, + bool *is_dup_detect, + bool is_local); /* Private functions */ static int host_rb_entry_compare(const struct host_rb_entry *hle1, @@ -255,6 +274,50 @@ static uint32_t num_valid_macs(zebra_vni_t *zvni) return num_macs; } +static uint32_t num_dup_detected_macs(zebra_vni_t *zvni) +{ + unsigned int i; + uint32_t num_macs = 0; + struct hash *hash; + struct hash_backet *hb; + zebra_mac_t *mac; + + hash = zvni->mac_table; + if (!hash) + return num_macs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + mac = (zebra_mac_t *)hb->data; + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + num_macs++; + } + } + + return num_macs; +} + +static uint32_t num_dup_detected_neighs(zebra_vni_t *zvni) +{ + unsigned int i; + uint32_t num_neighs = 0; + struct hash *hash; + struct hash_backet *hb; + zebra_neigh_t *nbr; + + hash = zvni->neigh_table; + if (!hash) + return num_neighs; + for (i = 0; i < hash->size; i++) { + for (hb = hash->index[i]; hb; hb = hb->next) { + nbr = (zebra_neigh_t *)hb->data; + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + num_neighs++; + } + } + + return num_neighs; +} + static int advertise_gw_macip_enabled(zebra_vni_t *zvni) { struct zebra_vrf *zvrf; @@ -269,6 +332,327 @@ static int advertise_gw_macip_enabled(zebra_vni_t *zvni) return 0; } +/* As part Duplicate Address Detection (DAD) for IP mobility + * MAC binding changes, ensure to inherit duplicate flag + * from MAC. + */ +static int zebra_vxlan_ip_inherit_dad_from_mac(struct zebra_vrf *zvrf, + zebra_mac_t *old_zmac, + zebra_mac_t *new_zmac, + zebra_neigh_t *nbr) +{ + bool is_old_mac_dup = false; + bool is_new_mac_dup = false; + + if (!zvrf->dup_addr_detect) + return 0; + /* Check old or new MAC is detected as duplicate + * mark this neigh as duplicate + */ + if (old_zmac) + is_old_mac_dup = CHECK_FLAG(old_zmac->flags, + ZEBRA_MAC_DUPLICATE); + if (new_zmac) + is_new_mac_dup = CHECK_FLAG(new_zmac->flags, + ZEBRA_MAC_DUPLICATE); + /* Old and/or new MAC can be in duplicate state, + * based on that IP/Neigh Inherits the flag. + * If New MAC is marked duplicate, inherit to the IP. + * If old MAC is duplicate but new MAC is not, clear + * duplicate flag for IP and reset detection params + * and let IP DAD retrigger. + */ + if (is_new_mac_dup && !CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + return 1; + } else if (is_old_mac_dup && !is_new_mac_dup) { + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + } + return 0; +} + +static void zebra_vxlan_dup_addr_detect_for_mac(struct zebra_vrf *zvrf, + zebra_mac_t *mac, + struct in_addr vtep_ip, + bool do_dad, + bool *is_dup_detect, + bool is_local) +{ + zebra_neigh_t *nbr; + struct listnode *node = NULL; + struct timeval elapsed = {0, 0}; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + bool reset_params = false; + + if (!(zvrf->dup_addr_detect && do_dad)) + return; + + /* MAC is detected as duplicate, + * Local MAC event -> hold on advertising to BGP. + * Remote MAC event -> hold on installing it. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x skip update to client, learn count %u recover time %u", + __PRETTY_FUNCTION__, + prefix_mac2str(&mac->macaddr, buf, + sizeof(buf)), + mac->flags, mac->dad_count, + zvrf->dad_freeze_time); + + /* For duplicate MAC do not update + * client but update neigh due to + * this MAC update. + */ + if (zvrf->dad_freeze) + *is_dup_detect = true; + + return; + } + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + */ + monotime_since(&mac->detect_start_time, &elapsed); + reset_params = (elapsed.tv_sec > zvrf->dad_time); + if (is_local && !reset_params) { + /* RFC-7432: A PE/VTEP that detects a MAC mobility + * event via LOCAL learning starts an M-second timer. + * + * NOTE: This is the START of the probe with count is + * 0 during LOCAL learn event. + * (mac->dad_count == 0 || elapsed.tv_sec >= zvrf->dad_time) + */ + reset_params = !mac->dad_count; + } + + if (reset_params) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x detection time passed, reset learn count %u" + , __PRETTY_FUNCTION__, + prefix_mac2str(&mac->macaddr, buf, + sizeof(buf)), + mac->flags, mac->dad_count); + + mac->dad_count = 0; + /* Start dup. addr detection (DAD) start time, + * ONLY during LOCAL learn. + */ + if (is_local) + monotime(&mac->detect_start_time); + + } else if (!is_local) { + /* For REMOTE MAC, increment detection count + * ONLY while in probe window, once window passed, + * next local learn event should trigger DAD. + */ + mac->dad_count++; + } + + /* For LOCAL MAC learn event, once count is reset above via either + * initial/start detection time or passed the probe time, the count + * needs to be incremented. + */ + if (is_local) + mac->dad_count++; + + if (mac->dad_count >= zvrf->dad_max_moves) { + flog_warn(EC_ZEBRA_DUP_MAC_DETECTED, + "VNI %u: MAC %s detected as duplicate during %s VTEP %s", + mac->zvni->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + is_local ? "local update, last" : + "remote update, from", inet_ntoa(vtep_ip)); + + SET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + + /* Capture Duplicate detection time */ + mac->dad_dup_detect_time = monotime(NULL); + + /* Mark all IPs/Neighs as duplicate + * associcated with this MAC + */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + + /* Ony Mark IPs which are Local */ + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + continue; + + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + + nbr->dad_dup_detect_time = monotime(NULL); + + flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during %s update, inherit duplicate from MAC", + mac->zvni->vni, + prefix_mac2str(&mac->macaddr, + buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + is_local ? "local" : "remote"); + } + + /* Start auto recovery timer for this MAC */ + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + if (zvrf->dad_freeze && zvrf->dad_freeze_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s flags 0x%x auto recovery time %u start" + , __PRETTY_FUNCTION__, + prefix_mac2str(&mac->macaddr, buf, + sizeof(buf)), + mac->flags, zvrf->dad_freeze_time); + + thread_add_timer(zebrad.master, + zebra_vxlan_dad_mac_auto_recovery_exp, + mac, zvrf->dad_freeze_time, + &mac->dad_mac_auto_recovery_timer); + } + + /* In case of local update, do not inform to client (BGPd), + * upd_neigh for neigh sequence change. + */ + if (zvrf->dad_freeze) + *is_dup_detect = true; + } +} + +static void zebra_vxlan_dup_addr_detect_for_neigh(struct zebra_vrf *zvrf, + zebra_neigh_t *nbr, + struct in_addr vtep_ip, + bool do_dad, + bool *is_dup_detect, + bool is_local) +{ + + struct timeval elapsed = {0, 0}; + char buf[ETHER_ADDR_STRLEN]; + char buf1[INET6_ADDRSTRLEN]; + bool reset_params = false; + + if (!zvrf->dup_addr_detect) + return; + + /* IP is detected as duplicate or inherit dup + * state, hold on to install as remote entry + * only if freeze is enabled. + */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x skip installing, learn count %u recover time %u", + __PRETTY_FUNCTION__, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + nbr->flags, nbr->dad_count, + zvrf->dad_freeze_time); + + if (zvrf->dad_freeze) + *is_dup_detect = true; + + /* warn-only action, neigh will be installed. + * freeze action, it wil not be installed. + */ + return; + } + + if (!do_dad) + return; + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + * During remote mac add, count should already be 1 + * via local learning. + */ + monotime_since(&nbr->detect_start_time, &elapsed); + reset_params = (elapsed.tv_sec > zvrf->dad_time); + + if (is_local && !reset_params) { + /* RFC-7432: A PE/VTEP that detects a MAC mobility + * event via LOCAL learning starts an M-second timer. + * + * NOTE: This is the START of the probe with count is + * 0 during LOCAL learn event. + */ + reset_params = !nbr->dad_count; + } + + if (reset_params) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x detection time passed, reset learn count %u", + __PRETTY_FUNCTION__, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + nbr->flags, nbr->dad_count); + /* Reset learn count but do not start detection + * during REMOTE learn event. + */ + nbr->dad_count = 0; + /* Start dup. addr detection (DAD) start time, + * ONLY during LOCAL learn. + */ + if (is_local) + monotime(&nbr->detect_start_time); + + } else if (!is_local) { + /* For REMOTE IP/Neigh, increment detection count + * ONLY while in probe window, once window passed, + * next local learn event should trigger DAD. + */ + nbr->dad_count++; + } + + /* For LOCAL IP/Neigh learn event, once count is reset above via either + * initial/start detection time or passed the probe time, the count + * needs to be incremented. + */ + if (is_local) + nbr->dad_count++; + + if (nbr->dad_count >= zvrf->dad_max_moves) { + flog_warn(EC_ZEBRA_DUP_IP_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during %s VTEP %s", + nbr->zvni->vni, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + is_local ? "local update, last" : + "remote update, from", + inet_ntoa(vtep_ip)); + + SET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + + /* Capture Duplicate detection time */ + nbr->dad_dup_detect_time = monotime(NULL); + + /* Start auto recovery timer for this IP */ + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + if (zvrf->dad_freeze && zvrf->dad_freeze_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s flags 0x%x auto recovery time %u start", + __PRETTY_FUNCTION__, + prefix_mac2str(&nbr->emac, buf, sizeof(buf)), + ipaddr2str(&nbr->ip, buf1, sizeof(buf1)), + nbr->flags, zvrf->dad_freeze_time); + + thread_add_timer(zebrad.master, + zebra_vxlan_dad_ip_auto_recovery_exp, + nbr, zvrf->dad_freeze_time, + &nbr->dad_ip_auto_recovery_timer); + } + if (zvrf->dad_freeze) + *is_dup_detect = true; + } +} + /* * Helper function to determine maximum width of neighbor IP address for * display - just because we're dealing with IPv6 addresses that can @@ -301,7 +685,14 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) const char *type_str; const char *state_str; bool flags_present = false; + struct zebra_vrf *zvrf = NULL; + struct timeval detect_start_time = {0, 0}; + + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); + if (!zvrf) + return; + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); ipaddr2str(&n->ip, buf2, sizeof(buf2)); prefix_mac2str(&n->emac, buf1, sizeof(buf1)); type_str = CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL) ? @@ -348,9 +739,36 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json) vty_out(vty, "\n"); vty_out(vty, " Local Seq: %u Remote Seq: %u\n", n->loc_seq, n->rem_seq); + + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { + vty_out(vty, " Duplicate, detected at %s", + time_to_string(n->dad_dup_detect_time)); + } else if (n->dad_count) { + monotime_since(&n->detect_start_time, + &detect_start_time); + if (detect_start_time.tv_sec <= zvrf->dad_time) { + char *buf = time_to_string( + n->detect_start_time.tv_sec); + char tmp_buf[30]; + + memset(tmp_buf, 0, 30); + strncpy(tmp_buf, buf, strlen(buf) - 1); + vty_out(vty, + " Duplicate detection started at %s, detection count %u\n", + tmp_buf, n->dad_count); + } + } } else { json_object_int_add(json, "localSequence", n->loc_seq); json_object_int_add(json, "remoteSequence", n->rem_seq); + json_object_int_add(json, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json, "isDuplicate"); + else + json_object_boolean_false_add(json, "isDuplicate"); + + } } @@ -396,6 +814,14 @@ static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt) n->loc_seq); json_object_int_add(json_row, "remoteSequence", n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); } wctx->count++; } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) { @@ -426,6 +852,14 @@ static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt) n->loc_seq); json_object_int_add(json_row, "remoteSequence", n->rem_seq); + json_object_int_add(json_row, "detectionCount", + n->dad_count); + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) + json_object_boolean_true_add(json_row, + "isDuplicate"); + else + json_object_boolean_false_add(json_row, + "isDuplicate"); } wctx->count++; } @@ -435,6 +869,33 @@ static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt) } /* + * Print neighbor hash entry in detail - called for display of all neighbors. + */ +static void zvni_print_neigh_hash_detail(struct hash_backet *backet, void *ctxt) +{ + struct vty *vty; + json_object *json_vni = NULL, *json_row = NULL; + zebra_neigh_t *n; + char buf[INET6_ADDRSTRLEN]; + struct neigh_walk_ctx *wctx = ctxt; + + vty = wctx->vty; + json_vni = wctx->json; + n = (zebra_neigh_t *)backet->data; + if (!n) + return; + + ipaddr2str(&n->ip, buf, sizeof(buf)); + if (json_vni) + json_row = json_object_new_object(); + + zvni_print_neigh(n, vty, json_row); + + if (json_vni) + json_object_object_add(json_vni, buf, json_row); +} + +/* * Print neighbors for all VNI. */ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, @@ -446,13 +907,19 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, uint32_t num_neigh; struct neigh_walk_ctx wctx; char vni_str[VNI_STR_LEN]; + uint32_t print_dup; vty = (struct vty *)args[0]; json = (json_object *)args[1]; + print_dup = (uint32_t)(uintptr_t)args[2]; zvni = (zebra_vni_t *)backet->data; num_neigh = hashcount(zvni->neigh_table); + + if (print_dup) + num_neigh = num_dup_detected_neighs(zvni); + if (json == NULL) { vty_out(vty, "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", @@ -462,6 +929,7 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, json_object_int_add(json_vni, "numArpNd", num_neigh); snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); } + if (!num_neigh) { if (json) json_object_object_add(json, vni_str, json_vni); @@ -484,7 +952,97 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet, -wctx.addr_width, "IP", "Type", "State", "MAC", "Remote VTEP"); } - hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + if (print_dup) + hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, + &wctx); + else + hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx); + + if (json) + json_object_object_add(json, vni_str, json_vni); +} + +static void zvni_print_dad_neigh_hash(struct hash_backet *backet, void *ctxt) +{ + zebra_neigh_t *nbr; + + nbr = (zebra_neigh_t *)backet->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zvni_print_neigh_hash(backet, ctxt); +} + +static void zvni_print_dad_neigh_hash_detail(struct hash_backet *backet, + void *ctxt) +{ + zebra_neigh_t *nbr; + + nbr = (zebra_neigh_t *)backet->data; + if (!nbr) + return; + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + zvni_print_neigh_hash_detail(backet, ctxt); +} + +/* + * Print neighbors for all VNIs in detail. + */ +static void zvni_print_neigh_hash_all_vni_detail(struct hash_backet *backet, + void **args) +{ + struct vty *vty; + json_object *json = NULL, *json_vni = NULL; + zebra_vni_t *zvni; + uint32_t num_neigh; + struct neigh_walk_ctx wctx; + char vni_str[VNI_STR_LEN]; + uint32_t print_dup; + + vty = (struct vty *)args[0]; + json = (json_object *)args[1]; + print_dup = (uint32_t)(uintptr_t)args[2]; + + zvni = (zebra_vni_t *)backet->data; + if (!zvni) { + if (json) + vty_out(vty, "{}\n"); + return; + } + num_neigh = hashcount(zvni->neigh_table); + + if (print_dup && num_dup_detected_neighs(zvni) == 0) + return; + + if (json == NULL) { + vty_out(vty, + "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n", + zvni->vni, num_neigh); + } else { + json_vni = json_object_new_object(); + json_object_int_add(json_vni, "numArpNd", num_neigh); + snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); + } + if (!num_neigh) { + if (json) + json_object_object_add(json, vni_str, json_vni); + return; + } + + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zvni = zvni; + wctx.vty = vty; + wctx.addr_width = 15; + wctx.json = json_vni; + + if (print_dup) + hash_iterate(zvni->neigh_table, + zvni_print_dad_neigh_hash_detail, &wctx); + else + hash_iterate(zvni->neigh_table, zvni_print_neigh_hash_detail, + &wctx); if (json) json_object_object_add(json, vni_str, json_vni); @@ -576,6 +1134,10 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) struct listnode *node = NULL; char buf1[20]; char buf2[INET6_ADDRSTRLEN]; + struct zebra_vrf *zvrf; + struct timeval detect_start_time = {0, 0}; + + zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT); vty = (struct vty *)ctxt; prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); @@ -621,6 +1183,12 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) json_object_int_add(json_mac, "localSequence", mac->loc_seq); json_object_int_add(json_mac, "remoteSequence", mac->rem_seq); + json_object_int_add(json_mac, "detectionCount", mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, "isDuplicate"); + else + json_object_boolean_false_add(json_mac, "isDuplicate"); + /* print all the associated neigh */ if (!listcount(mac->neigh_list)) json_object_string_add(json_mac, "neighbors", "none"); @@ -656,10 +1224,6 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) } json_object_object_add(json, buf1, json_mac); - vty_out(vty, "%s\n", - json_object_to_json_string_ext( - json, JSON_C_TO_STRING_PRETTY)); - json_object_free(json); } else { vty_out(vty, "MAC: %s\n", buf1); @@ -698,6 +1262,25 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt, json_object *json) mac->rem_seq); vty_out(vty, "\n"); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + vty_out(vty, " Duplicate, detected at %s", + time_to_string(mac->dad_dup_detect_time)); + } else if (mac->dad_count) { + monotime_since(&mac->detect_start_time, + &detect_start_time); + if (detect_start_time.tv_sec <= zvrf->dad_time) { + char *buf = time_to_string( + mac->detect_start_time.tv_sec); + char tmp_buf[30]; + + memset(tmp_buf, 0, 30); + strncpy(tmp_buf, buf, strlen(buf) - 1); + vty_out(vty, + " Duplicate detection started at %s, detection count %u\n", + tmp_buf, mac->dad_count); + } + } + /* print all the associated neigh */ vty_out(vty, " Neighbors:\n"); if (!listcount(mac->neigh_list)) @@ -771,6 +1354,14 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt) mac->loc_seq); json_object_int_add(json_mac, "remoteSequence", mac->rem_seq); + json_object_int_add(json_mac, "detectionCount", + mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, + "isDuplicate"); + else + json_object_boolean_false_add(json_mac, + "isDuplicate"); json_object_object_add(json_mac_hdr, buf1, json_mac); } @@ -801,12 +1392,71 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt) mac->loc_seq); json_object_int_add(json_mac, "remoteSequence", mac->rem_seq); + json_object_int_add(json_mac, "detectionCount", + mac->dad_count); + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + json_object_boolean_true_add(json_mac, + "isDuplicate"); + else + json_object_boolean_false_add(json_mac, + "isDuplicate"); + } wctx->count++; } } +/* Print Duplicate MAC */ +static void zvni_print_dad_mac_hash(struct hash_backet *backet, void *ctxt) +{ + zebra_mac_t *mac; + + mac = (zebra_mac_t *)backet->data; + if (!mac) + return; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + zvni_print_mac_hash(backet, ctxt); +} + +/* + * Print MAC hash entry in detail - called for display of all MACs. + */ +static void zvni_print_mac_hash_detail(struct hash_backet *backet, void *ctxt) +{ + struct vty *vty; + json_object *json_mac_hdr = NULL; + zebra_mac_t *mac; + struct mac_walk_ctx *wctx = ctxt; + char buf1[20]; + + vty = wctx->vty; + json_mac_hdr = wctx->json; + mac = (zebra_mac_t *)backet->data; + if (!mac) + return; + + wctx->count++; + prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); + + zvni_print_mac(mac, vty, json_mac_hdr); +} + +/* Print Duplicate MAC in detail */ +static void zvni_print_dad_mac_hash_detail(struct hash_backet *backet, + void *ctxt) +{ + zebra_mac_t *mac; + + mac = (zebra_mac_t *)backet->data; + if (!mac) + return; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + zvni_print_mac_hash_detail(backet, ctxt); +} + /* * Print MACs for all VNI. */ @@ -833,6 +1483,9 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt) if (!num_macs) return; + if (wctx->print_dup) + num_macs = num_dup_detected_macs(zvni); + if (json) { json_vni = json_object_new_object(); json_mac = json_object_new_object(); @@ -848,12 +1501,90 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt) } else json_object_int_add(json_vni, "numMacs", num_macs); } + + if (!num_macs) { + if (json) { + json_object_int_add(json_vni, "numMacs", num_macs); + json_object_object_add(json, vni_str, json_vni); + } + return; + } + + /* assign per-vni to wctx->json object to fill macs + * under the vni. Re-assign primary json object to fill + * next vni information. + */ + wctx->json = json_mac; + if (wctx->print_dup) + hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, wctx); + else + hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx); + wctx->json = json; + if (json) { + if (wctx->count) + json_object_object_add(json_vni, "macs", json_mac); + json_object_object_add(json, vni_str, json_vni); + } +} + +/* + * Print MACs in detail for all VNI. + */ +static void zvni_print_mac_hash_all_vni_detail(struct hash_backet *backet, + void *ctxt) +{ + struct vty *vty; + json_object *json = NULL, *json_vni = NULL; + json_object *json_mac = NULL; + zebra_vni_t *zvni; + uint32_t num_macs; + struct mac_walk_ctx *wctx = ctxt; + char vni_str[VNI_STR_LEN]; + + vty = (struct vty *)wctx->vty; + json = (struct json_object *)wctx->json; + + zvni = (zebra_vni_t *)backet->data; + if (!zvni) { + if (json) + vty_out(vty, "{}\n"); + return; + } + wctx->zvni = zvni; + + /*We are iterating over a new VNI, set the count to 0*/ + wctx->count = 0; + + num_macs = num_valid_macs(zvni); + if (!num_macs) + return; + + if (wctx->print_dup && (num_dup_detected_macs(zvni) == 0)) + return; + + if (json) { + json_vni = json_object_new_object(); + json_mac = json_object_new_object(); + snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni); + } + + if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) { + if (json == NULL) { + vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n", + zvni->vni, num_macs); + } else + json_object_int_add(json_vni, "numMacs", num_macs); + } /* assign per-vni to wctx->json object to fill macs * under the vni. Re-assign primary json object to fill * next vni information. */ wctx->json = json_mac; - hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx); + if (wctx->print_dup) + hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash_detail, + wctx); + else + hash_iterate(zvni->mac_table, zvni_print_mac_hash_detail, wctx); wctx->json = json; if (json) { if (wctx->count) @@ -1195,6 +1926,35 @@ static void zl3vni_print_hash(struct hash_backet *backet, void *ctx[]) } } +/* Private Structure to pass callback data for hash iterator */ +struct zvni_evpn_show { + struct vty *vty; + json_object *json; + struct zebra_vrf *zvrf; +}; + +/* print a L3 VNI hash entry in detail*/ +static void zl3vni_print_hash_detail(struct hash_backet *backet, void *data) +{ + struct vty *vty = NULL; + zebra_l3vni_t *zl3vni = NULL; + json_object *json = NULL; + bool use_json = false; + struct zvni_evpn_show *zes = data; + + vty = zes->vty; + json = zes->json; + + if (json) + use_json = true; + + zl3vni = (zebra_l3vni_t *)backet->data; + + zebra_vxlan_print_vni(vty, zes->zvrf, zl3vni->vni, use_json); + vty_out(vty, "\n"); +} + + /* * Print a VNI hash entry - called for display of all VNIs. */ @@ -1260,11 +2020,34 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]) } /* + * Print a VNI hash entry in detail - called for display of all VNIs. + */ +static void zvni_print_hash_detail(struct hash_backet *backet, void *data) +{ + struct vty *vty; + zebra_vni_t *zvni; + json_object *json = NULL; + bool use_json = false; + struct zvni_evpn_show *zes = data; + + vty = zes->vty; + json = zes->json; + + if (json) + use_json = true; + + zvni = (zebra_vni_t *)backet->data; + + zebra_vxlan_print_vni(vty, zes->zvrf, zvni->vni, use_json); + vty_out(vty, "\n"); +} + +/* * Inform BGP about local MACIP. */ static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, struct ipaddr *ip, uint8_t flags, - uint32_t seq, uint16_t cmd) + uint32_t seq, int state, uint16_t cmd) { char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; @@ -1298,6 +2081,8 @@ static int zvni_macip_send_msg_to_client(vni_t vni, struct ethaddr *macaddr, if (cmd == ZEBRA_MACIP_ADD) { stream_putc(s, flags); /* sticky mac/gateway mac */ stream_putl(s, seq); /* sequence number */ + } else { + stream_putl(s, state); /* state - active/inactive */ } @@ -1436,7 +2221,7 @@ static void zvni_neigh_del_hash_entry(struct hash_backet *backet, 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->emac, 0, n->state); if (wctx->uninstall) zvni_neigh_uninstall(wctx->zvni, n); @@ -1512,8 +2297,11 @@ static void zvni_process_neigh_on_local_mac_change(zebra_vni_t *zvni, { zebra_neigh_t *n = NULL; struct listnode *node = NULL; + struct zebra_vrf *zvrf = NULL; char buf[ETHER_ADDR_STRLEN]; + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("Processing neighbors on local MAC %s %s, VNI %u", prefix_mac2str(&zmac->macaddr, buf, sizeof(buf)), @@ -1530,9 +2318,12 @@ static void zvni_process_neigh_on_local_mac_change(zebra_vni_t *zvni, if (IS_ZEBRA_NEIGH_INACTIVE(n) || seq_change) { ZEBRA_NEIGH_SET_ACTIVE(n); n->loc_seq = zmac->loc_seq; - zvni_neigh_send_add_to_client( - zvni->vni, &n->ip, &n->emac, - n->flags, n->loc_seq); + if (!(zvrf->dup_addr_detect && + zvrf->dad_freeze && !!CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE))) + zvni_neigh_send_add_to_client( + zvni->vni, &n->ip, &n->emac, + n->flags, n->loc_seq); } } } @@ -1566,7 +2357,7 @@ 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); + &n->emac, 0, ZEBRA_NEIGH_ACTIVE); } } } @@ -1597,7 +2388,7 @@ 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); + &n->emac, 0, ZEBRA_NEIGH_ACTIVE); } } } @@ -1630,17 +2421,18 @@ static int zvni_neigh_send_add_to_client(vni_t vni, struct ipaddr *ip, SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG); return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags, - seq, ZEBRA_MACIP_ADD); + seq, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_ADD); } /* * Inform BGP about local neighbor deletion. */ static int zvni_neigh_send_del_to_client(vni_t vni, struct ipaddr *ip, - struct ethaddr *macaddr, uint8_t flags) + struct ethaddr *macaddr, uint8_t flags, + int state) { return zvni_macip_send_msg_to_client(vni, macaddr, ip, flags, - 0, ZEBRA_MACIP_DEL); + 0, state, ZEBRA_MACIP_DEL); } /* @@ -1961,7 +2753,7 @@ 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_MACIP_TYPE_GW, ZEBRA_NEIGH_ACTIVE); /* Delete this neighbor entry. */ zvni_neigh_del(zvni, n); @@ -2059,12 +2851,16 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, { char buf[ETHER_ADDR_STRLEN]; char buf2[INET6_ADDRSTRLEN]; + struct zebra_vrf *zvrf; zebra_neigh_t *n = NULL; zebra_mac_t *zmac = NULL, *old_zmac = NULL; uint32_t old_mac_seq = 0, mac_new_seq = 0; bool upd_mac_seq = false; bool neigh_mac_change = false; - bool check_rbit = false; + bool neigh_on_hold = false; + bool neigh_was_remote = false; + bool do_dad = false; + struct in_addr vtep_ip = {.s_addr = 0}; /* Check if the MAC exists. */ zmac = zvni_mac_lookup(zvni, macaddr); @@ -2100,6 +2896,10 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, } } + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return -1; + /* Check if the neighbor exists. */ n = zvni_neigh_lookup(zvni, ip); if (!n) { @@ -2117,7 +2917,6 @@ 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; - check_rbit = true; } else { if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) { bool mac_different; @@ -2134,6 +2933,8 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, } if (!mac_different) { + bool is_neigh_freezed = false; + /* Only the router flag has changed. */ if (is_router) SET_FLAG(n->flags, @@ -2142,7 +2943,16 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); - if (IS_ZEBRA_NEIGH_ACTIVE(n)) + /* Neigh is in freeze state and freeze action + * is enabled, do not send update to client. + */ + is_neigh_freezed = (zvrf->dup_addr_detect && + zvrf->dad_freeze && + CHECK_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE)); + + if (IS_ZEBRA_NEIGH_ACTIVE(n) && + !is_neigh_freezed) return zvni_neigh_send_add_to_client( zvni->vni, ip, macaddr, n->flags, n->loc_seq); @@ -2157,7 +2967,7 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, */ if (IS_ZEBRA_NEIGH_ACTIVE(n)) zvni_neigh_send_del_to_client(zvni->vni, &n->ip, - &n->emac, 0); + &n->emac, 0, n->state); old_zmac = zvni_mac_lookup(zvni, &n->emac); if (old_zmac) { old_mac_seq = CHECK_FLAG(old_zmac->flags, @@ -2198,13 +3008,17 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, memcpy(&n->emac, macaddr, ETH_ALEN); listnode_add_sort(zmac->neigh_list, n); } - + /* Based on Mobility event Scenario-B from the + * draft, neigh's previous state was remote treat this + * event for DAD. + */ + neigh_was_remote = true; + vtep_ip = n->r_vtep_ip; /* Mark appropriately */ UNSET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE); n->r_vtep_ip.s_addr = 0; SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL); n->ifindex = ifp->ifindex; - check_rbit = true; } } @@ -2221,12 +3035,34 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, MAX(seq1, seq2) : zmac->loc_seq; } - /*Mark Router flag (R-bit) */ + /* Mark Router flag (R-bit) */ if (is_router) SET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); else UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + /* Check old and/or new MAC detected as duplicate mark + * the neigh as duplicate + */ + if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_zmac, zmac, n)) { + flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during local update, inherit duplicate from MAC", + zvni->vni, + prefix_mac2str(macaddr, buf, sizeof(buf)), + ipaddr2str(&n->ip, buf2, sizeof(buf2))); + } + + /* For IP Duplicate Address Detection (DAD) is trigger, + * when the event is extended mobility based on scenario-B + * from the draft, IP/Neigh's MAC binding changed and + * neigh's previous state was remote. + */ + if (neigh_mac_change && neigh_was_remote) + do_dad = true; + + zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, vtep_ip, do_dad, + &neigh_on_hold, true); + /* Before we program this in BGP, we need to check if MAC is locally * learnt. If not, force neighbor to be inactive and reset its seq. */ @@ -2237,9 +3073,6 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, return 0; } - if (!check_rbit) - return 0; - /* If the MAC's sequence number has changed, inform the MAC and all * neighbors associated with the MAC to BGP, else just inform this * neighbor. @@ -2260,8 +3093,10 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, ZEBRA_NEIGH_SET_ACTIVE(n); n->loc_seq = zmac->loc_seq; - return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, + if (!neigh_on_hold) + return zvni_neigh_send_add_to_client(zvni->vni, ip, macaddr, n->flags, n->loc_seq); + return 0; } static int zvni_remote_neigh_update(zebra_vni_t *zvni, @@ -2488,7 +3323,7 @@ static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW); return zvni_macip_send_msg_to_client(vni, macaddr, NULL, flags, - seq, ZEBRA_MACIP_ADD); + seq, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_ADD); } /* @@ -2497,7 +3332,7 @@ static int zvni_mac_send_add_to_client(vni_t vni, struct ethaddr *macaddr, static int zvni_mac_send_del_to_client(vni_t vni, struct ethaddr *macaddr) { return zvni_macip_send_msg_to_client(vni, macaddr, NULL, 0 /* flags */, - 0 /* seq */, ZEBRA_MACIP_DEL); + 0 /* seq */, ZEBRA_NEIGH_ACTIVE, ZEBRA_MACIP_DEL); } /* @@ -3010,7 +3845,7 @@ static int zvni_send_del_to_client(vni_t vni) * Build the VNI hash table by going over the VxLAN interfaces. This * is called when EVPN (advertise-all-vni) is enabled. */ -static void zvni_build_hash_table() +static void zvni_build_hash_table(void) { struct zebra_ns *zns; struct route_node *rn; @@ -4167,17 +5002,20 @@ static void process_remote_macip_add(vni_t vni, { zebra_vni_t *zvni; zebra_vtep_t *zvtep; - zebra_mac_t *mac, *old_mac; + zebra_mac_t *mac = NULL, *old_mac = NULL; zebra_neigh_t *n = NULL; int update_mac = 0, update_neigh = 0; char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; struct interface *ifp = NULL; struct zebra_if *zif = NULL; + struct zebra_vrf *zvrf; uint32_t tmp_seq; bool sticky; bool remote_gw; bool is_router; + bool do_dad = false; + bool is_dup_detect = false; /* Locate VNI hash entry - expected to exist. */ zvni = zvni_lookup(vni); @@ -4235,6 +5073,10 @@ static void process_remote_macip_add(vni_t vni, return; } + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return; + /* check if the remote MAC is unknown or has a change. * If so, that needs to be updated first. Note that client could * install MAC and MACIP separately or just install the latter. @@ -4296,6 +5138,23 @@ static void process_remote_macip_add(vni_t vni, } } + /* Check MAC's curent state is local (this is the case + * where MAC has moved from L->R) and check previous + * detection started via local learning. + * RFC-7432: A PE/VTEP that detects a MAC mobility + * event via local learning starts an M-second timer. + * + * VTEP-IP or seq. change alone is not considered + * for dup. detection. + * + * MAC is already marked duplicate set dad, then + * is_dup_detect will be set to not install the entry. + */ + if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) && + mac->dad_count) || + CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + do_dad = true; + /* Remove local MAC from BGP. */ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) zvni_mac_send_del_to_client(zvni->vni, macaddr); @@ -4316,11 +5175,16 @@ static void process_remote_macip_add(vni_t vni, else UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); - zvni_process_neigh_on_remote_mac_add(zvni, mac); - - /* Install the entry. */ - zvni_mac_install(zvni, mac); + zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, + mac->fwd_info.r_vtep_ip, + do_dad, &is_dup_detect, + false); + if (!is_dup_detect) { + zvni_process_neigh_on_remote_mac_add(zvni, mac); + /* Install the entry. */ + zvni_mac_install(zvni, mac); + } } /* Update seq number. */ @@ -4332,6 +5196,9 @@ static void process_remote_macip_add(vni_t vni, return; } + /* Reset flag */ + do_dad = false; + /* Check if the remote neighbor itself is unknown or has a * change. If so, create or update and then install the entry. */ @@ -4396,7 +5263,7 @@ static void process_remote_macip_add(vni_t vni, IS_ZEBRA_NEIGH_ACTIVE(n)) zvni_neigh_send_del_to_client( zvni->vni, &n->ip, - &n->emac, 0); + &n->emac, 0, n->state); /* update neigh list for macs */ old_mac = zvni_mac_lookup(zvni, &n->emac); @@ -4406,6 +5273,25 @@ static void process_remote_macip_add(vni_t vni, } listnode_add_sort(mac->neigh_list, n); memcpy(&n->emac, macaddr, ETH_ALEN); + + /* Check Neigh's curent state is local + * (this is the case where neigh/host has moved + * from L->R) and check previous detction + * started via local learning. + * + * RFC-7432: A PE/VTEP that detects a MAC + * mobilit event via local learning starts + * an M-second timer. + * VTEP-IP or seq. change along is not + * considered for dup. detection. + * + * Mobilty event scenario-B IP-MAC binding + * changed. + */ + if ((!CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) + && n->dad_count) + do_dad = true; + } } @@ -4420,8 +5306,27 @@ static void process_remote_macip_add(vni_t vni, else UNSET_FLAG(n->flags, ZEBRA_NEIGH_ROUTER_FLAG); + /* Check old or new MAC detected as duplicate, + * inherit duplicate flag to this neigh. + */ + if (zebra_vxlan_ip_inherit_dad_from_mac(zvrf, old_mac, + mac, n)) { + flog_warn(EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during remote update, inherit duplicate from MAC", + zvni->vni, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + ipaddr2str(&n->ip, buf1, sizeof(buf1))); + } + + /* Check duplicate address detection for IP */ + zebra_vxlan_dup_addr_detect_for_neigh(zvrf, n, + n->r_vtep_ip, + do_dad, + &is_dup_detect, + false); /* Install the entry. */ - zvni_neigh_install(zvni, n); + if (!is_dup_detect) + zvni_neigh_install(zvni, n); } /* Update seq number. */ @@ -4440,6 +5345,9 @@ static void process_remote_macip_del(vni_t vni, zebra_neigh_t *n = NULL; struct interface *ifp = NULL; struct zebra_if *zif = NULL; + struct zebra_ns *zns; + struct zebra_l2info_vxlan *vxl; + struct zebra_vrf *zvrf; char buf[ETHER_ADDR_STRLEN]; char buf1[INET6_ADDRSTRLEN]; @@ -4463,6 +5371,8 @@ static void process_remote_macip_del(vni_t vni, vni); return; } + zns = zebra_ns_lookup(NS_DEFAULT); + vxl = &zif->l2info.vxl; /* The remote VTEP specified is normally expected to exist, but * it is possible that the peer may delete the VTEP before deleting @@ -4489,9 +5399,10 @@ static void process_remote_macip_del(vni_t vni, if (!mac && !n) return; + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + /* Ignore the delete if this mac is a gateway mac-ip */ - if (mac - && CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) && CHECK_FLAG(mac->flags, ZEBRA_MAC_DEF_GW)) { zlog_warn( "Ignore remote MACIP DEL VNI %u MAC %s%s%s as MAC is already configured as gateway MAC", @@ -4505,6 +5416,23 @@ static void process_remote_macip_del(vni_t vni, /* Uninstall remote neighbor or MAC. */ if (n) { + if (zvrf->dad_freeze && + CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE) && + CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE) && + (memcmp(n->emac.octet, macaddr->octet, ETH_ALEN) == 0)) { + struct interface *vlan_if; + + vlan_if = zvni_map_to_svi(vxl->access_vlan, + zif->brslave_info.br_if); + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: IP %s (flags 0x%x intf %s) is remote and duplicate, read kernel for local entry", + __PRETTY_FUNCTION__, + ipaddr2str(ipaddr, buf1, + sizeof(buf1)), n->flags, + vlan_if->name); + neigh_read_specific_ip(ipaddr, vlan_if); + } + /* When the MAC changes for an IP, it is possible the * client may update the new MAC before trying to delete the * "old" neighbor (as these are two different MACIP routes). @@ -4517,6 +5445,25 @@ static void process_remote_macip_del(vni_t vni, zvni_deref_ip2mac(zvni, mac); } } else { + /* DAD: when MAC is freeze state as remote learn event, + * remote mac-ip delete event is received will result in freeze + * entry removal, first fetch kernel for the same entry present + * as LOCAL and reachable, avoid deleting this entry instead + * use kerenel local entry to update during unfreeze time. + */ + if (zvrf->dad_freeze && + CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE) && + CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: MAC %s (flags 0x%x) is remote and duplicate, read kernel for local entry", + __PRETTY_FUNCTION__, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + mac->flags); + macfdb_read_specific_mac(zns, zif->brslave_info.br_if, + macaddr, vxl->access_vlan); + } + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { zvni_process_neigh_on_remote_mac_del(zvni, mac); /* @@ -4991,10 +5938,10 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, * Display neighbors across all VNIs (VTY command handler). */ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, - bool use_json) + bool print_dup, bool use_json) { json_object *json = NULL; - void *args[2]; + void *args[3]; if (!is_evpn_enabled()) return; @@ -5004,6 +5951,8 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, args[0] = vty; args[1] = json; + args[2] = (void *)(ptrdiff_t)print_dup; + hash_iterate(zvrf->vni_table, (void (*)(struct hash_backet *, void *))zvni_print_neigh_hash_all_vni, @@ -5016,6 +5965,37 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, } /* + * Display neighbors across all VNIs in detail(VTY command handler). + */ +void zebra_vxlan_print_neigh_all_vni_detail(struct vty *vty, + struct zebra_vrf *zvrf, + bool print_dup, bool use_json) +{ + json_object *json = NULL; + void *args[3]; + + if (!is_evpn_enabled()) + return; + + if (use_json) + json = json_object_new_object(); + + args[0] = vty; + args[1] = json; + args[2] = (void *)(ptrdiff_t)print_dup; + + hash_iterate(zvrf->vni_table, + (void (*)(struct hash_backet *, + void *))zvni_print_neigh_hash_all_vni_detail, + args); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* * Display specific neighbor for a VNI, if present (VTY command handler). */ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, @@ -5101,6 +6081,70 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, } /* + * Display Duplicate detected Neighbors for a VNI + * (VTY command handler). + */ +void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, + bool use_json) +{ + zebra_vni_t *zvni; + uint32_t num_neigh; + struct neigh_walk_ctx wctx; + json_object *json = NULL; + + if (!is_evpn_enabled()) + return; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + num_neigh = hashcount(zvni->neigh_table); + if (!num_neigh) + return; + + num_neigh = num_dup_detected_neighs(zvni); + if (!num_neigh) + return; + + if (use_json) + json = json_object_new_object(); + + /* Since we have IPv6 addresses to deal with which can vary widely in + * size, we try to be a bit more elegant in display by first computing + * the maximum width. + */ + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zvni = zvni; + wctx.vty = vty; + wctx.addr_width = 15; + wctx.json = json; + hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + + if (!use_json) { + vty_out(vty, + "Number of ARPs (local and remote) known for this VNI: %u\n", + num_neigh); + vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + -wctx.addr_width, "IP", "Type", + "State", "MAC", "Remote VTEP"); + } else + json_object_int_add(json, "numArpNd", num_neigh); + + hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* * Display MACs for a VNI (VTY command handler). */ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, @@ -5159,7 +6203,7 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, * Display MACs for all VNIs (VTY command handler). */ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, - bool use_json) + bool print_dup, bool use_json) { struct mac_walk_ctx wctx; json_object *json = NULL; @@ -5175,6 +6219,7 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.vty = vty; wctx.json = json; + wctx.print_dup = print_dup; hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); if (use_json) { @@ -5185,6 +6230,38 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, } /* + * Display MACs in detail for all VNIs (VTY command handler). + */ +void zebra_vxlan_print_macs_all_vni_detail(struct vty *vty, + struct zebra_vrf *zvrf, + bool print_dup, bool use_json) +{ + struct mac_walk_ctx wctx; + json_object *json = NULL; + + if (!is_evpn_enabled()) { + if (use_json) + vty_out(vty, "{}\n"); + return; + } + if (use_json) + json = json_object_new_object(); + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.vty = vty; + wctx.json = json; + wctx.print_dup = print_dup; + hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni_detail, + &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* * Display MACs for all VNIs (VTY command handler). */ void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, @@ -5251,6 +6328,408 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, json = json_object_new_object(); zvni_print_mac(mac, vty, json); + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* Print Duplicate MACs per VNI */ +void zebra_vxlan_print_macs_vni_dad(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, bool use_json) +{ + zebra_vni_t *zvni; + struct mac_walk_ctx wctx; + uint32_t num_macs; + json_object *json = NULL; + json_object *json_mac = NULL; + + if (!is_evpn_enabled()) + return; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + num_macs = num_valid_macs(zvni); + if (!num_macs) + return; + + num_macs = num_dup_detected_macs(zvni); + if (!num_macs) + return; + + if (use_json) { + json = json_object_new_object(); + json_mac = json_object_new_object(); + } + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zvni = zvni; + wctx.vty = vty; + wctx.json = json_mac; + + if (!use_json) { + vty_out(vty, + "Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", + "Intf/Remote VTEP", "VLAN"); + } else + json_object_int_add(json, "numMacs", num_macs); + + hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, &wctx); + + if (use_json) { + json_object_object_add(json, "macs", json_mac); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + +} + +int zebra_vxlan_clear_dup_detect_vni_mac(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, struct ethaddr *macaddr) +{ + zebra_vni_t *zvni; + zebra_mac_t *mac; + struct listnode *node = NULL; + zebra_neigh_t *nbr = NULL; + + if (!is_evpn_enabled()) + return CMD_SUCCESS; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return CMD_WARNING; + } + + mac = zvni_mac_lookup(zvni, macaddr); + if (!mac) { + vty_out(vty, "%% Requested MAC does not exist in VNI %u\n", + vni); + return CMD_WARNING; + } + + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + vty_out(vty, "%% Requested MAC is not duplicate detected\n"); + return CMD_WARNING; + } + + /* Remove all IPs as duplicate associcated with this MAC */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + /* For local neigh mark inactive so MACIP update is generated + * to BGP. This is a scenario where MAC update received + * and detected as duplicate which marked neigh as duplicate. + * Later local neigh update did not get a chance to relay + * to BGP. Similarly remote macip update, neigh needs to be + * installed locally. + */ + if (zvrf->dad_freeze && + CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + ZEBRA_NEIGH_SET_INACTIVE(nbr); + else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) + zvni_neigh_install(zvni, nbr); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->dad_dup_detect_time = 0; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + mac->dad_count = 0; + mac->detect_start_time.tv_sec = 0; + mac->detect_start_time.tv_usec = 0; + mac->dad_dup_detect_time = 0; + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + + /* warn-only action return */ + if (!zvrf->dad_freeze) + return CMD_SUCCESS; + + /* Local: Notify Peer VTEPs, Remote: Install the entry */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* Inform to BGP */ + if (zvni_mac_send_add_to_client(zvni->vni, + &mac->macaddr, + mac->flags, + mac->loc_seq)) + return CMD_SUCCESS; + + /* Process all neighbors associated with this MAC. */ + zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zvni_process_neigh_on_remote_mac_add(zvni, mac); + + /* Install the entry. */ + zvni_mac_install(zvni, mac); + } + + return CMD_SUCCESS; +} + +int zebra_vxlan_clear_dup_detect_vni_ip(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, struct ipaddr *ip) +{ + zebra_vni_t *zvni; + zebra_neigh_t *nbr; + zebra_mac_t *mac; + char buf[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + + if (!is_evpn_enabled()) + return CMD_SUCCESS; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return CMD_WARNING; + } + + nbr = zvni_neigh_lookup(zvni, ip); + if (!nbr) { + vty_out(vty, + "%% Requested host IP does not exist in VNI %u\n", + vni); + return CMD_WARNING; + } + + ipaddr2str(&nbr->ip, buf, sizeof(buf)); + + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + vty_out(vty, + "%% Requsted host IP %s is not duplicate detected\n", + buf); + return CMD_WARNING; + } + + mac = zvni_mac_lookup(zvni, &nbr->emac); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + vty_out(vty, + "%% Requested IP's associated MAC %s is still in duplicate state\n", + prefix_mac2str(&nbr->emac, buf2, sizeof(buf2))); + return CMD_WARNING_CONFIG_FAILED; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: clear neigh %s in dup state, flags 0x%x seq %u", + __PRETTY_FUNCTION__, buf, nbr->flags, + nbr->loc_seq); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + + if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zvni_neigh_send_add_to_client(zvni->vni, ip, + &nbr->emac, + nbr->flags, nbr->loc_seq); + } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zvni_neigh_install(zvni, nbr); + } + + return CMD_SUCCESS; +} + +static void zvni_clear_dup_mac_hash(struct hash_backet *backet, void *ctxt) +{ + struct mac_walk_ctx *wctx = ctxt; + zebra_mac_t *mac; + zebra_vni_t *zvni; + struct listnode *node = NULL; + zebra_neigh_t *nbr = NULL; + + mac = (zebra_mac_t *)backet->data; + if (!mac) + return; + + zvni = wctx->zvni; + + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + return; + + UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + mac->dad_count = 0; + mac->detect_start_time.tv_sec = 0; + mac->detect_start_time.tv_usec = 0; + mac->dad_dup_detect_time = 0; + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + + /* Remove all IPs as duplicate associcated with this MAC */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL) + && nbr->dad_count) + ZEBRA_NEIGH_SET_INACTIVE(nbr); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->dad_dup_detect_time = 0; + } + + /* Local: Notify Peer VTEPs, Remote: Install the entry */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* Inform to BGP */ + if (zvni_mac_send_add_to_client(zvni->vni, + &mac->macaddr, + mac->flags, mac->loc_seq)) + return; + + /* Process all neighbors associated with this MAC. */ + zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zvni_process_neigh_on_remote_mac_add(zvni, mac); + + /* Install the entry. */ + zvni_mac_install(zvni, mac); + } +} + +static void zvni_clear_dup_neigh_hash(struct hash_backet *backet, void *ctxt) +{ + struct neigh_walk_ctx *wctx = ctxt; + zebra_neigh_t *nbr; + zebra_vni_t *zvni; + char buf[INET6_ADDRSTRLEN]; + + nbr = (zebra_neigh_t *)backet->data; + if (!nbr) + return; + + zvni = wctx->zvni; + + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + return; + + if (IS_ZEBRA_DEBUG_VXLAN) { + ipaddr2str(&nbr->ip, buf, sizeof(buf)); + zlog_debug( + "%s: clear neigh %s dup state, flags 0x%x seq %u", + __PRETTY_FUNCTION__, buf, + nbr->flags, nbr->loc_seq); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, + &nbr->emac, + nbr->flags, nbr->loc_seq); + } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zvni_neigh_install(zvni, nbr); + } +} + +static void zvni_clear_dup_detect_hash_vni_all(struct hash_backet *backet, + void **args) +{ + struct vty *vty; + zebra_vni_t *zvni; + struct zebra_vrf *zvrf; + struct mac_walk_ctx m_wctx; + struct neigh_walk_ctx n_wctx; + + zvni = (zebra_vni_t *)backet->data; + if (!zvni) + return; + + vty = (struct vty *)args[0]; + zvrf = (struct zebra_vrf *)args[1]; + + if (hashcount(zvni->neigh_table)) { + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.vty = vty; + n_wctx.zvni = zvni; + n_wctx.zvrf = zvrf; + hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, + &n_wctx); + } + + if (num_valid_macs(zvni)) { + memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); + m_wctx.zvni = zvni; + m_wctx.vty = vty; + m_wctx.zvrf = zvrf; + hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + } + +} + +int zebra_vxlan_clear_dup_detect_vni_all(struct vty *vty, + struct zebra_vrf *zvrf) +{ + void *args[2]; + + if (!is_evpn_enabled()) + return CMD_SUCCESS; + + args[0] = vty; + args[1] = zvrf; + + hash_iterate(zvrf->vni_table, + (void (*)(struct hash_backet *, void *)) + zvni_clear_dup_detect_hash_vni_all, args); + + return CMD_SUCCESS; +} + +int zebra_vxlan_clear_dup_detect_vni(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni) +{ + zebra_vni_t *zvni; + struct mac_walk_ctx m_wctx; + struct neigh_walk_ctx n_wctx; + + if (!is_evpn_enabled()) + return CMD_SUCCESS; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return CMD_WARNING; + } + + if (hashcount(zvni->neigh_table)) { + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.vty = vty; + n_wctx.zvni = zvni; + n_wctx.zvrf = zvrf; + hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, + &n_wctx); + } + + if (num_valid_macs(zvni)) { + memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); + m_wctx.zvni = zvni; + m_wctx.vty = vty; + m_wctx.zvrf = zvrf; + hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + } + + return CMD_SUCCESS; } /* @@ -5373,11 +6852,34 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) json_object_int_add(json, "numVnis", num_vnis); json_object_int_add(json, "numL2Vnis", num_l2vnis); json_object_int_add(json, "numL3Vnis", num_l3vnis); + if (zvrf->dup_addr_detect) + json_object_boolean_true_add(json, + "isDuplicateAddrDetection"); + else + json_object_boolean_false_add(json, + "isDuplicateAddrDetection"); + json_object_int_add(json, "maxMoves", zvrf->dad_max_moves); + json_object_int_add(json, "detectionTime", zvrf->dad_time); + json_object_int_add(json, "detectionFreezeTime", + zvrf->dad_freeze_time); + } else { vty_out(vty, "L2 VNIs: %u\n", num_l2vnis); vty_out(vty, "L3 VNIs: %u\n", num_l3vnis); vty_out(vty, "Advertise gateway mac-ip: %s\n", zvrf->advertise_gw_macip ? "Yes" : "No"); + vty_out(vty, "Duplicate address detection: %s\n", + zvrf->dup_addr_detect ? "Enable" : "Disable"); + vty_out(vty, " Detection max-moves %u, time %d\n", + zvrf->dad_max_moves, zvrf->dad_time); + if (zvrf->dad_freeze) { + if (zvrf->dad_freeze_time) + vty_out(vty, " Detection freeze %u\n", + zvrf->dad_freeze_time); + else + vty_out(vty, " Detection freeze %s\n", + "permanent"); + } } if (uj) { @@ -5426,6 +6928,91 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, } } +void zebra_vxlan_dup_addr_detection(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + int time = 0; + uint32_t max_moves = 0; + uint32_t freeze_time = 0; + bool dup_addr_detect = false; + bool freeze = false; + + s = msg; + STREAM_GETL(s, dup_addr_detect); + STREAM_GETL(s, time); + STREAM_GETL(s, max_moves); + STREAM_GETL(s, freeze); + STREAM_GETL(s, freeze_time); + + /* DAD previous state was enabled, and new state is disable, + * clear all duplicate detected addresses. + */ + if (zvrf->dup_addr_detect && !dup_addr_detect) + zebra_vxlan_clear_dup_detect_vni_all(NULL, zvrf); + + zvrf->dup_addr_detect = dup_addr_detect; + zvrf->dad_time = time; + zvrf->dad_max_moves = max_moves; + zvrf->dad_freeze = freeze; + zvrf->dad_freeze_time = freeze_time; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "VRF %s duplicate detect %s max_moves %u timeout %u freeze %s freeze_time %u", + vrf_id_to_name(zvrf->vrf->vrf_id), + zvrf->dup_addr_detect ? "enable" : "disable", + zvrf->dad_max_moves, + zvrf->dad_time, + zvrf->dad_freeze ? "enable" : "disable", + zvrf->dad_freeze_time); + +stream_failure: + return; +} + +/* + * Display VNI hash table in detail(VTY command handler). + */ +void zebra_vxlan_print_vnis_detail(struct vty *vty, struct zebra_vrf *zvrf, + bool use_json) +{ + json_object *json = NULL; + struct zebra_ns *zns = NULL; + struct zvni_evpn_show zes; + + if (!is_evpn_enabled()) + return; + + zns = zebra_ns_lookup(NS_DEFAULT); + if (!zns) + return; + + + if (use_json) + json = json_object_new_object(); + + zes.vty = vty; + zes.json = json; + zes.zvrf = zvrf; + + /* Display all L2-VNIs */ + hash_iterate(zvrf->vni_table, (void (*)(struct hash_backet *, + void *))zvni_print_hash_detail, + &zes); + + /* Display all L3-VNIs */ + hash_iterate(zrouter.l3vni_table, + (void (*)(struct hash_backet *, + void *))zl3vni_print_hash_detail, + &zes); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + /* * Handle neighbor delete notification from the kernel (on a VLAN device * / L3 interface). This may result in either the neighbor getting deleted @@ -5495,8 +7082,8 @@ int zebra_vxlan_handle_kernel_neigh_del(struct interface *ifp, } /* Remove neighbor from BGP. */ - if (IS_ZEBRA_NEIGH_ACTIVE(n)) - zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac, 0); + zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac, + 0, n->state); /* Delete this neighbor entry. */ zvni_neigh_del(zvni, n); @@ -5871,10 +7458,13 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, { zebra_vni_t *zvni; zebra_mac_t *mac; + struct zebra_vrf *zvrf; char buf[ETHER_ADDR_STRLEN]; bool mac_sticky = false; bool inform_client = false; bool upd_neigh = false; + bool is_dup_detect = false; + struct in_addr vtep_ip = {.s_addr = 0}; /* We are interested in MACs only on ports or (port, VLAN) that * map to a VNI. @@ -5897,6 +7487,10 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, return -1; } + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return -1; + /* Check if we need to create or update or it is a NO-OP. */ mac = zvni_mac_lookup(zvni, macaddr); if (!mac) { @@ -5970,6 +7564,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) || CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { + bool do_dad = false; /* * MAC has either moved or was "internally" created due @@ -5989,9 +7584,14 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, } /* If an actual move, compute MAC's seq number */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { mac->loc_seq = MAX(mac->rem_seq + 1, mac->loc_seq); + vtep_ip = mac->fwd_info.r_vtep_ip; + /* Trigger DAD for remote MAC */ + do_dad = true; + } + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); @@ -6008,6 +7608,15 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, */ inform_client = true; upd_neigh = true; + + zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, vtep_ip, + do_dad, + &is_dup_detect, + true); + if (is_dup_detect) { + inform_client = false; + upd_neigh = false; + } } } @@ -6842,7 +8451,7 @@ int zebra_vxlan_if_add(struct interface *ifp) "Add L2-VNI %u VRF %s intf %s(%u) VLAN %u local IP %s master %u", vni, vlan_if ? vrf_id_to_name(vlan_if->vrf_id) - : "Default", + : VRF_DEFAULT_NAME, ifp->name, ifp->ifindex, vxl->access_vlan, inet_ntoa(vxl->vtep_ip), zif->brslave_info.bridge_ifindex); @@ -7311,14 +8920,14 @@ void zebra_vxlan_close_tables(struct zebra_vrf *zvrf) } /* init the l3vni table */ -void zebra_vxlan_ns_init(struct zebra_ns *zns) +void zebra_vxlan_init(void) { zrouter.l3vni_table = hash_create(l3vni_hash_keymake, l3vni_hash_cmp, "Zebra VRF L3 VNI table"); } /* free l3vni table */ -void zebra_vxlan_ns_disable(struct zebra_ns *zns) +void zebra_vxlan_disable(void) { hash_free(zrouter.l3vni_table); } @@ -7334,3 +8943,125 @@ ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) return zl3vni->svi_if->ifindex; } + +static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_neigh_t *nbr = NULL; + zebra_vni_t *zvni = NULL; + char buf1[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + + nbr = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + nbr = zvni_neigh_lookup(zvni, &nbr->ip); + if (!nbr) + return 0; + + zvni = zvni_lookup(nbr->zvni->vni); + if (!zvni) + return 0; + + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired", + __PRETTY_FUNCTION__, + prefix_mac2str(&nbr->emac, buf1, sizeof(buf1)), + ipaddr2str(&nbr->ip, buf2, sizeof(buf2)), + nbr->flags, + nbr->dad_count, zvni->vni); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + nbr->dad_ip_auto_recovery_timer = NULL; + + /* Send to BGP */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, &nbr->emac, + nbr->flags, nbr->loc_seq); + } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zvni_neigh_install(zvni, nbr); + } + + return 0; +} + +static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_mac_t *mac = NULL; + zebra_vni_t *zvni = NULL; + struct listnode *node = NULL; + zebra_neigh_t *nbr = NULL; + char buf[ETHER_ADDR_STRLEN]; + + mac = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + mac = zvni_mac_lookup(zvni, &mac->macaddr); + if (!mac) + return 0; + + zvni = zvni_lookup(mac->zvni->vni); + if (!zvni) + return 0; + + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired", + __PRETTY_FUNCTION__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, + mac->dad_count, + listcount(mac->neigh_list)); + + /* Remove all IPs as duplicate associcated with this MAC */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + ZEBRA_NEIGH_SET_INACTIVE(nbr); + else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) + zvni_neigh_install(zvni, nbr); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->dad_dup_detect_time = 0; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + mac->dad_count = 0; + mac->detect_start_time.tv_sec = 0; + mac->detect_start_time.tv_usec = 0; + mac->dad_dup_detect_time = 0; + mac->dad_mac_auto_recovery_timer = NULL; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* Inform to BGP */ + if (zvni_mac_send_add_to_client(zvni->vni, &mac->macaddr, + mac->flags, mac->loc_seq)) + return -1; + + /* Process all neighbors associated with this MAC. */ + zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zvni_process_neigh_on_remote_mac_add(zvni, mac); + + /* Install the entry. */ + zvni_mac_install(zvni, mac); + } + + return 0; +} |
