From e22a946a89579a5d73bcaf5eb10eb1b830a2419e Mon Sep 17 00:00:00 2001 From: Chirag Shah Date: Fri, 2 Nov 2018 08:30:41 -0700 Subject: [PATCH] zebra: dup addr detect warn-only Duplicate address detection warning only action upon an address detected as duplicate. Signed-off-by: Chirag Shah --- zebra/zebra_errors.c | 27 ++ zebra/zebra_errors.h | 3 + zebra/zebra_vxlan.c | 524 ++++++++++++++++++++++++++++++++++-- zebra/zebra_vxlan_private.h | 23 ++ 4 files changed, 562 insertions(+), 15 deletions(-) diff --git a/zebra/zebra_errors.c b/zebra/zebra_errors.c index 17163e2182..32f6653832 100644 --- a/zebra/zebra_errors.c +++ b/zebra/zebra_errors.c @@ -694,6 +694,33 @@ static struct log_ref ferr_zebra_err[] = { .suggestion = "Do not use v6 sourcedest routes, or upgrade your kernel.", }, + { + .code = EC_ZEBRA_DUP_MAC_DETECTED, + .title = + "EVPN MAC is detected duplicate", + .description = + "Zebra has hit duplicate address detection threshold which means host MAC is moving.", + .suggestion = + "Check network topology to detect duplicate host MAC for correctness.", + }, + { + .code = EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + .title = + "EVPN IP is detected duplicate by MAC", + .description = + "Zebra has hit duplicate address detection threshold which means MAC-IP pair is moving.", + .suggestion = + "Check network topology to detect duplicate host MAC for correctness.", + }, + { + .code = EC_ZEBRA_DUP_IP_DETECTED, + .title = + "EVPN IP is detected duplicate", + .description = + "Zebra has hit duplicate address detection threshold which means host IP is moving.", + .suggestion = + "Check network topology to detect duplicate host IP for correctness.", + }, { .code = END_FERR, } diff --git a/zebra/zebra_errors.h b/zebra/zebra_errors.h index 43e37c6e5b..cf2d6a7cf5 100644 --- a/zebra/zebra_errors.h +++ b/zebra/zebra_errors.h @@ -117,6 +117,9 @@ enum zebra_log_refs { EC_ZEBRA_MAX_LABELS_PUSH, EC_ZEBRA_STICKY_MAC_ALREADY_LEARNT, EC_ZEBRA_UNSUPPORTED_V6_SRCDEST, + EC_ZEBRA_DUP_MAC_DETECTED, + EC_ZEBRA_DUP_IP_INHERIT_DETECTED, + EC_ZEBRA_DUP_IP_DETECTED, }; void zebra_error_init(void); diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c index 280b10b6e9..f305950013 100644 --- a/zebra/zebra_vxlan.c +++ b/zebra/zebra_vxlan.c @@ -178,6 +178,10 @@ 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); @@ -269,6 +273,49 @@ 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 inheirt duplicate flag + */ +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; +} + /* * Helper function to determine maximum width of neighbor IP address for * display - just because we're dealing with IPv6 addresses that can @@ -2059,12 +2106,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; + struct in_addr vtep_ip = {.s_addr = 0}; + struct timeval elapsed = {0, 0}; /* Check if the MAC exists. */ zmac = zvni_mac_lookup(zvni, macaddr); @@ -2100,6 +2151,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 +2172,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 +2188,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 +2198,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 +2263,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 +2290,98 @@ 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))); + } + + /* Duplicate Address Detection (DAD) is enabled. + * Based on Mobility event Scenario-B from the + * draft, IP/Neigh's MAC binding changed and + * neigh's previous state was remote, trigger DAD. + */ + if (zvrf->dup_addr_detect) { + /* Neigh could have inherit dup flag or IP DAD + * detected earlier + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s skip update to client, learn count %u recover time %u", + __PRETTY_FUNCTION__, + prefix_mac2str(macaddr, + buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), + n->dad_count, + zvrf->dad_freeze_time); + + /* In case of warn-only, inform client and update neigh + */ + if (zvrf->dad_freeze) + neigh_on_hold = true; + + goto send_notif; + } + /* MAC binding changed and previous state was remote */ + if (!(neigh_mac_change && neigh_was_remote)) + goto send_notif; + + /* First check if neigh is already marked duplicate via + * MAC dup detection, before firing M Seconds + * duplicate detection. + * RFC-7432: A PE/VTEP that detects a MAC mobility + * event via local learning starts an M-second timer. + * + * Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + */ + monotime_since(&n->detect_start_time, &elapsed); + if (n->dad_count == 0 || elapsed.tv_sec > zvrf->dad_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: duplicate addr MAC %s detection time passed, reset learn count %u", + __PRETTY_FUNCTION__, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + n->dad_count); + n->dad_count = 0; + /* Start dup. detection time */ + monotime(&n->detect_start_time); + } + + n->dad_count++; + + if (n->dad_count >= zvrf->dad_max_moves) { + flog_warn(EC_ZEBRA_DUP_IP_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during local update, last VTEP %s", + zvni->vni, + prefix_mac2str(&n->emac, buf, sizeof(buf)), + ipaddr2str(ip, buf2, sizeof(buf2)), + inet_ntoa(vtep_ip)); + + /* Mark Duplicate */ + SET_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE); + + /* Capture Duplicate detection time */ + n->dad_dup_detect_time = monotime(NULL); + + if (zvrf->dad_freeze) + neigh_on_hold = true; + } + } + +send_notif: /* 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 +2392,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 +2412,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 +4321,22 @@ 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; + struct listnode *node = NULL; + struct timeval elapsed = {0, 0}; /* Locate VNI hash entry - expected to exist. */ zvni = zvni_lookup(vni); @@ -4235,6 +4394,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 +4459,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 +4492,99 @@ static void process_remote_macip_add(vni_t vni, else UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE_DEF_GW); + if (zvrf->dup_addr_detect && do_dad) { + /* MAC is detected as duplicate, hold on to + * install as remote entry. + */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s skip installing, learn count %u recover time %u", + __PRETTY_FUNCTION__, + prefix_mac2str(macaddr, + buf, sizeof(buf)), + mac->dad_count, + zvrf->dad_freeze_time); + /* Do not install MAC but process neigh + * due to the remote MAC add. + */ + goto process_neigh; + } + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + */ + monotime_since(&mac->detect_start_time, &elapsed); + if (elapsed.tv_sec > zvrf->dad_time) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: duplicate addr MAC %s flags 0%x detection time passed, reset learn count %u", + __PRETTY_FUNCTION__, + prefix_mac2str(macaddr, buf, + sizeof(buf)), + mac->flags, mac->dad_count); + /* Reset learn count but do not start detection + * during remote learn. + * Next local learn event start time wil be + * resetted. + */ + mac->dad_count = 0; + } else { + /* Increment detection count while in probe + * window + */ + 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 remote update, from VTEP %s", + zvni->vni, + prefix_mac2str(&mac->macaddr, + buf, sizeof(buf)), + inet_ntoa(mac->fwd_info.r_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, n)) { + /* Ony Mark IPs which are Remote */ + if (!CHECK_FLAG(n->flags, + ZEBRA_NEIGH_REMOTE)) + continue; + + SET_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE); + + /* Capture Duplicate detection time */ + n->dad_dup_detect_time = monotime(NULL); + + 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))); + } + + if (zvrf->dad_freeze) + is_dup_detect = true; + } + + } +process_neigh: 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 +4596,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 +4673,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 +4706,96 @@ 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))); + } + + if (zvrf->dup_addr_detect) { + /* IP is detected as duplicate or inherit dup + * state, hold on to install as remote entry + * only if freeze is enabled. + */ + if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s IP %s skip installing, learn count %u recover time %u", + __PRETTY_FUNCTION__, + prefix_mac2str(macaddr, + buf, sizeof(buf)), + ipaddr2str(ipaddr, buf1, + sizeof(buf1)), + n->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. + */ + goto install_neigh; + } + + if (!do_dad) + goto install_neigh; + + /* 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(&n->detect_start_time, &elapsed); + if (elapsed.tv_sec > zvrf->dad_time) { + 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(macaddr, buf, + sizeof(buf)), + ipaddr2str(ipaddr, buf1, + sizeof(buf1)), + n->flags, + n->dad_count); + /* Reset learn count but do not start detection + * during remote learn event. + */ + n->dad_count = 0; + } else { + /* Increment detection count while in probe + * window + */ + n->dad_count++; + } + + if (n->dad_count >= zvrf->dad_max_moves) { + flog_warn(EC_ZEBRA_DUP_IP_DETECTED, + "VNI %u: MAC %s IP %s detected as duplicate during remote update, from VTEP %s", + zvni->vni, + prefix_mac2str(&mac->macaddr, + buf, sizeof(buf)), + ipaddr2str(ipaddr, buf1, + sizeof(buf1)), + inet_ntoa(n->r_vtep_ip)); + + SET_FLAG(n->flags, ZEBRA_NEIGH_DUPLICATE); + + /* Capture Duplicate detection time */ + n->dad_dup_detect_time = monotime(NULL); + + if (zvrf->dad_freeze) + is_dup_detect = true; + } + } +install_neigh: /* Install the entry. */ - zvni_neigh_install(zvni, n); + if (!is_dup_detect) + zvni_neigh_install(zvni, n); } /* Update seq number. */ @@ -5907,10 +6281,16 @@ 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; + zebra_neigh_t *n = NULL; + struct listnode *node = NULL; + struct in_addr vtep_ip = {.s_addr = 0}; + struct timeval elapsed = {0, 0}; + char buf2[INET6_ADDRSTRLEN]; /* We are interested in MACs only on ports or (port, VLAN) that * map to a VNI. @@ -5933,6 +6313,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) { @@ -6006,6 +6390,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 @@ -6025,9 +6410,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); @@ -6044,9 +6434,113 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, */ inform_client = true; upd_neigh = true; + + if (zvrf->dup_addr_detect && do_dad) { + /* MAC is detected as duplicate, hold on + * advertising to BGP. + */ + if (CHECK_FLAG(mac->flags, + ZEBRA_MAC_DUPLICATE)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate addr MAC %s skip update to client, learn count %u recover time %u", + __PRETTY_FUNCTION__, + prefix_mac2str(macaddr, + buf, sizeof(buf)), + 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) + inform_client = false; + + goto send_notif; + } + + /* Check if detection time (M-secs) expired. + * Reset learn count and detection start time. + */ + monotime_since(&mac->detect_start_time, + &elapsed); + if (mac->dad_count == 0 || + elapsed.tv_sec >= zvrf->dad_time) { + + 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( + macaddr, buf, + sizeof(buf)), + mac->flags, + mac->dad_count); + + mac->dad_count = 0; + /* Capture start dup. detection time */ + monotime(&mac->detect_start_time); + } + + /* Increment move count */ + 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 local update, last VTEP %s", + zvni->vni, + prefix_mac2str(&mac->macaddr, + buf, sizeof(buf)), + 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, n)) { + + /* Ony Mark IPs which are Local + */ + if (!CHECK_FLAG(n->flags, + ZEBRA_NEIGH_LOCAL)) + continue; + + SET_FLAG(n->flags, + ZEBRA_NEIGH_DUPLICATE); + + n->dad_dup_detect_time = + monotime(NULL); + + 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(&mac->macaddr, + buf, sizeof(buf)), + ipaddr2str(&n->ip, buf2, + sizeof(buf2))); + } + + /* Do not inform to client (BGPd), + * upd_neigh for neigh sequence change. + */ + if (zvrf->dad_freeze) + inform_client = false; + } + } } } +send_notif: + /* Inform BGP if required. */ if (inform_client) { if (zvni_mac_send_add_to_client(zvni->vni, macaddr, diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 44163eb331..be48a70ead 100644 --- a/zebra/zebra_vxlan_private.h +++ b/zebra/zebra_vxlan_private.h @@ -249,6 +249,10 @@ struct zebra_mac_t_ { #define ZEBRA_MAC_DEF_GW 0x20 /* remote VTEP advertised MAC as default GW */ #define ZEBRA_MAC_REMOTE_DEF_GW 0x40 +#define ZEBRA_MAC_DUPLICATE 0x80 + + /* back pointer to zvni */ + zebra_vni_t *zvni; /* Local or remote info. */ union { @@ -269,6 +273,15 @@ struct zebra_mac_t_ { /* list of hosts pointing to this remote RMAC */ struct host_rb_tree_entry host_rb; + + /* Duplicate mac detection */ + uint32_t dad_count; + + struct thread *dad_mac_auto_recovery_timer; + + struct timeval detect_start_time; + + time_t dad_dup_detect_time; }; /* @@ -336,6 +349,7 @@ struct zebra_neigh_t_ { #define ZEBRA_NEIGH_REMOTE_NH 0x04 /* neigh entry for remote vtep */ #define ZEBRA_NEIGH_DEF_GW 0x08 #define ZEBRA_NEIGH_ROUTER_FLAG 0x10 +#define ZEBRA_NEIGH_DUPLICATE 0x20 enum zebra_neigh_state state; @@ -354,6 +368,15 @@ struct zebra_neigh_t_ { /* list of hosts pointing to this remote NH entry */ struct host_rb_tree_entry host_rb; + + /* Duplicate ip detection */ + uint32_t dad_count; + + struct thread *dad_ip_auto_recovery_timer; + + struct timeval detect_start_time; + + time_t dad_dup_detect_time; }; /* -- 2.39.5