diff options
Diffstat (limited to 'zebra/zebra_vxlan.c')
| -rw-r--r-- | zebra/zebra_vxlan.c | 1353 |
1 files changed, 1330 insertions, 23 deletions
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index d372d3e832..86f6961118 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, @@ -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,331 @@ 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 = false; + + 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++; + + zlog_debug("%s: MAC DAD %s dad_count %u ", + __PRETTY_FUNCTION__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + 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); + } + + /* Do not inform to client (BGPd), + * upd_neigh for neigh sequence change. + */ + if (zvrf->dad_freeze) + *is_dup_detect = false; + } +} + +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,6 +689,12 @@ 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(n->zvni->vrf_id); + if (!zvrf) + return; ipaddr2str(&n->ip, buf2, sizeof(buf2)); prefix_mac2str(&n->emac, buf1, sizeof(buf1)); @@ -348,9 +742,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 +817,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 +855,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++; } @@ -446,13 +883,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 +905,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,12 +928,28 @@ 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); +} + /* print a specific next hop for an l3vni */ static void zl3vni_print_nh(zebra_neigh_t *n, struct vty *vty, json_object *json) @@ -576,6 +1036,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(mac->zvni->vrf_id); vty = (struct vty *)ctxt; prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1)); @@ -621,6 +1085,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"); @@ -698,6 +1168,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 +1260,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 +1298,34 @@ 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 MACs for all VNI. */ @@ -833,6 +1352,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 +1370,24 @@ 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; - hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx); + 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) @@ -1512,8 +2046,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->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 +2067,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); } } } @@ -2059,12 +2599,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 +2644,10 @@ static int zvni_local_neigh_update(zebra_vni_t *zvni, } } + zvrf = vrf_info_lookup(zvni->vrf_id); + if (!zvrf) + return -1; + /* Check if the neighbor exists. */ n = zvni_neigh_lookup(zvni, ip); if (!n) { @@ -2117,7 +2665,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 +2681,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 +2691,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); @@ -2198,13 +2756,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 +2783,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 +2821,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 +2841,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, @@ -4167,17 +4750,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 +4821,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 +4886,19 @@ 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 along is not considered + * for dup. detection. + */ + if ((!CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) && + mac->dad_count) + 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 +4919,16 @@ static void process_remote_macip_add(vni_t vni, else UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); + zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, + mac->fwd_info.r_vtep_ip, + do_dad, &is_dup_detect, + false); + zvni_process_neigh_on_remote_mac_add(zvni, mac); /* Install the entry. */ - zvni_mac_install(zvni, mac); - + if (!is_dup_detect) + zvni_mac_install(zvni, mac); } /* Update seq number. */ @@ -4332,6 +4940,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. */ @@ -4406,6 +5017,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 +5050,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. */ @@ -4991,10 +5640,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 +5653,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, @@ -5101,6 +5752,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 +5874,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 +5890,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) { @@ -5253,6 +5969,393 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, zvni_print_mac(mac, vty, 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); + } + +} + +void 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; + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + mac = zvni_mac_lookup(zvni, macaddr); + if (!mac) { + vty_out(vty, "%% Requested MAC does not exist in VNI %u\n", + vni); + return; + } + + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + vty_out(vty, "%% Requested MAC is not duplicate detected\n"); + return; + } + + /* 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 (nbr->dad_count) { + 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); + + /* 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); + } + +} + +void 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; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + nbr = zvni_neigh_lookup(zvni, ip); + if (!nbr) { + vty_out(vty, + "%% Requested host IP does not exist in VNI %u\n", + vni); + return; + } + + 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; + } + + 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; + } + + 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); + } + +} + +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); + } + +} + +void zebra_vxlan_clear_dup_detect_vni_all(struct vty *vty, + struct zebra_vrf *zvrf) +{ + void *args[2]; + + if (!is_evpn_enabled()) + return; + + args[0] = vty; + args[1] = zvrf; + + hash_iterate(zvrf->vni_table, + (void (*)(struct hash_backet *, void *)) + zvni_clear_dup_detect_hash_vni_all, args); + +} + +void 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; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + 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); + } + +} + /* * Display MACs for a VNI from specific VTEP (VTY command handler). */ @@ -5373,11 +6476,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 +6552,48 @@ 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( + "%s: duplicate detect %s max_moves %u timeout %u freeze %s freeze_time %u", + __PRETTY_FUNCTION__, + 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; +} + /* * Handle neighbor delete notification from the kernel (on a VLAN device * / L3 interface). This may result in either the neighbor getting deleted @@ -5871,10 +7039,12 @@ 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; + 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 +7067,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 +7144,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 +7164,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 +7188,11 @@ 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, + &inform_client, + true); } } @@ -7334,3 +8519,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*/ + zvrf = vrf_info_lookup(nbr->zvni->vrf_id); + if (!zvrf) + return 0; + + zvni = zvni_lookup(nbr->zvni->vni); + if (!zvni) + return 0; + + nbr = zvni_neigh_lookup(zvni, &nbr->ip); + if (!nbr) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired", + __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*/ + zvrf = vrf_info_lookup(mac->zvni->vrf_id); + if (!zvrf) + return 0; + + zvni = zvni_lookup(mac->zvni->vni); + if (!zvni) + return 0; + + mac = zvni_mac_lookup(zvni, &mac->macaddr); + if (!mac) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired", + __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 (nbr->dad_count) { + 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; +} |
