diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c index fc3ac28723..e5e80cd5c4 100644 --- a/bgpd/bgp_evpn.c +++ b/bgpd/bgp_evpn.c @@ -5802,6 +5802,21 @@ void bgp_evpn_init(struct bgp *bgp) bgp->vrf_export_rtl->del = evpn_xxport_delete_ecomm; bgp->l2vnis = list_new(); bgp->l2vnis->cmp = vni_list_cmp; + /* By default Duplicate Address Dection is enabled. + * Max-moves (N) 5, detection time (M) 180 + * default action is warning-only + * freeze action permanently freezes address, + * and freeze time (auto-recovery) is disabled. + */ + if (bgp->evpn_info) { + bgp->evpn_info->dup_addr_detect = true; + bgp->evpn_info->dad_time = EVPN_DAD_DEFAULT_TIME; + bgp->evpn_info->dad_max_moves = EVPN_DAD_DEFAULT_MAX_MOVES; + bgp->evpn_info->dad_freeze = false; + bgp->evpn_info->dad_freeze_time = 0; + /* Initialize zebra vxlan */ + bgp_zebra_dup_addr_detection(bgp); + } /* Default BUM handling is to do head-end replication. */ bgp->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL; diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h index f0017f3533..b2f16fc284 100644 --- a/bgpd/bgp_evpn_private.h +++ b/bgpd/bgp_evpn_private.h @@ -161,6 +161,24 @@ struct vrf_irt_node { #define RT_TYPE_EXPORT 2 #define RT_TYPE_BOTH 3 +#define EVPN_DAD_DEFAULT_TIME 180 /* secs */ +#define EVPN_DAD_DEFAULT_MAX_MOVES 5 /* default from RFC 7432 */ +#define EVPN_DAD_DEFAULT_AUTO_RECOVERY_TIME 1800 /* secs */ + +struct bgp_evpn_info { + /* enable disable dup detect */ + bool dup_addr_detect; + + /* Detection time(M) */ + int dad_time; + /* Detection max moves(N) */ + uint32_t dad_max_moves; + /* Permanent freeze */ + bool dad_freeze; + /* Recovery time */ + uint32_t dad_freeze_time; +}; + static inline int is_vrf_rd_configured(struct bgp *bgp_vrf) { return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD)); diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c index 1e062528b2..e915574ed0 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -2999,6 +2999,130 @@ DEFUN (no_bgp_evpn_default_originate, return CMD_SUCCESS; } +DEFPY (dup_addr_detection, + dup_addr_detection_cmd, + "dup-addr-detection [max-moves (2-1000)$max_moves_val time (2-1800)$time_val]", + "Duplicate address detection\n" + "Max allowed moves before address detected as duplicate\n" + "Num of max allowed moves (2-1000) default 5\n" + "Duplicate address detection time\n" + "Time in seconds (2-1800) default 180\n") +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); + + if (!bgp_vrf) + return CMD_WARNING; + + bgp_vrf->evpn_info->dup_addr_detect = true; + + if (time_val) + bgp_vrf->evpn_info->dad_time = time_val; + if (max_moves_val) + bgp_vrf->evpn_info->dad_max_moves = max_moves_val; + + bgp_zebra_dup_addr_detection(bgp_vrf); + + return CMD_SUCCESS; +} + +DEFPY (dup_addr_detection_auto_recovery, + dup_addr_detection_auto_recovery_cmd, + "dup-addr-detection freeze ", + "Duplicate address detection\n" + "Duplicate address detection freeze\n" + "Duplicate address detection permanent freeze\n" + "Duplicate address detection freeze time (30-3600)\n") +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); + uint32_t freeze_time = freeze_time_val; + + if (!bgp_vrf) + return CMD_WARNING; + + bgp_vrf->evpn_info->dup_addr_detect = true; + bgp_vrf->evpn_info->dad_freeze = true; + bgp_vrf->evpn_info->dad_freeze_time = freeze_time; + + bgp_zebra_dup_addr_detection(bgp_vrf); + + return CMD_SUCCESS; +} + +DEFPY (no_dup_addr_detection, + no_dup_addr_detection_cmd, + "no dup-addr-detection [max-moves (2-1000)$max_moves_val time (2-1800)$time_val | freeze ]", + NO_STR + "Duplicate address detection\n" + "Max allowed moves before address detected as duplicate\n" + "Num of max allowed moves (2-1000) default 5\n" + "Duplicate address detection time\n" + "Time in seconds (2-1800) default 180\n" + "Duplicate address detection freeze\n" + "Duplicate address detection permanent freeze\n" + "Duplicate address detection freeze time (30-3600)\n") +{ + struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); + uint32_t max_moves = (uint32_t)max_moves_val; + uint32_t freeze_time = (uint32_t)freeze_time_val; + + if (!bgp_vrf) + return CMD_WARNING; + + if (argc == 2) { + if (!bgp_vrf->evpn_info->dup_addr_detect) + return CMD_SUCCESS; + /* Reset all parameters to default. */ + bgp_vrf->evpn_info->dup_addr_detect = false; + bgp_vrf->evpn_info->dad_time = EVPN_DAD_DEFAULT_TIME; + bgp_vrf->evpn_info->dad_max_moves = EVPN_DAD_DEFAULT_MAX_MOVES; + bgp_vrf->evpn_info->dad_freeze = false; + bgp_vrf->evpn_info->dad_freeze_time = 0; + } else { + if (max_moves) { + if (bgp_vrf->evpn_info->dad_max_moves != max_moves) { + vty_out(vty, + "%% Value does not match with config\n"); + return CMD_SUCCESS; + } + bgp_vrf->evpn_info->dad_max_moves = + EVPN_DAD_DEFAULT_MAX_MOVES; + } + + if (time_val) { + if (bgp_vrf->evpn_info->dad_time != time_val) { + vty_out(vty, + "%% Value does not match with config\n"); + return CMD_SUCCESS; + } + bgp_vrf->evpn_info->dad_time = EVPN_DAD_DEFAULT_TIME; + } + + if (freeze_time) { + if (bgp_vrf->evpn_info->dad_freeze_time + != freeze_time) { + vty_out(vty, + "%% Value does not match with config\n"); + return CMD_SUCCESS; + } + bgp_vrf->evpn_info->dad_freeze_time = 0; + bgp_vrf->evpn_info->dad_freeze = false; + } + + if (permanent_val) { + if (bgp_vrf->evpn_info->dad_freeze_time) { + vty_out(vty, + "%% Value does not match with config\n"); + return CMD_SUCCESS; + } + bgp_vrf->evpn_info->dad_freeze = false; + } + } + + bgp_zebra_dup_addr_detection(bgp_vrf); + + return CMD_SUCCESS; +} + DEFUN_HIDDEN (bgp_evpn_advertise_vni_subnet, bgp_evpn_advertise_vni_subnet_cmd, "advertise-subnet", @@ -4919,6 +5043,26 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi, if (bgp->advertise_gw_macip) vty_out(vty, " advertise-default-gw\n"); + if (!bgp->evpn_info->dup_addr_detect) + vty_out(vty, " no dup-addr-detection\n"); + + if (bgp->evpn_info->dad_max_moves != + EVPN_DAD_DEFAULT_MAX_MOVES || + bgp->evpn_info->dad_time != EVPN_DAD_DEFAULT_TIME) + vty_out(vty, " dup-addr-detection max-moves %u time %u\n", + bgp->evpn_info->dad_max_moves, + bgp->evpn_info->dad_time); + + if (bgp->evpn_info->dad_freeze) { + if (bgp->evpn_info->dad_freeze_time) + vty_out(vty, + " dup-addr-detection freeze %u\n", + bgp->evpn_info->dad_freeze_time); + else + vty_out(vty, + " dup-addr-detection freeze permanent\n"); + } + if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_DISABLED) vty_out(vty, " flooding disable\n"); @@ -5013,6 +5157,9 @@ void bgp_ethernetvpn_init(void) install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_default_originate_cmd); install_element(BGP_EVPN_NODE, &no_bgp_evpn_default_originate_cmd); + install_element(BGP_EVPN_NODE, &dup_addr_detection_cmd); + install_element(BGP_EVPN_NODE, &dup_addr_detection_auto_recovery_cmd); + install_element(BGP_EVPN_NODE, &no_dup_addr_detection_cmd); install_element(BGP_EVPN_NODE, &bgp_evpn_flood_control_cmd); /* test commands */ diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index f9f9e693dc..1e0abaa29e 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -58,6 +58,7 @@ #include "bgpd/bgp_mplsvpn.h" #include "bgpd/bgp_labelpool.h" #include "bgpd/bgp_pbr.h" +#include "bgpd/bgp_evpn_private.h" /* All information about zebra. */ struct zclient *zclient = NULL; @@ -1999,6 +2000,42 @@ int bgp_zebra_advertise_all_vni(struct bgp *bgp, int advertise) return zclient_send_message(zclient); } +int bgp_zebra_dup_addr_detection(struct bgp *bgp) +{ + struct stream *s; + + /* Check socket. */ + if (!zclient || zclient->sock < 0) + return 0; + + /* Don't try to register if Zebra doesn't know of this instance. */ + if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) + return 0; + + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("dup addr detect %s max_moves %u time %u freeze %s freeze_time %u", + bgp->evpn_info->dup_addr_detect ? + "enable" : "disable", + bgp->evpn_info->dad_max_moves, + bgp->evpn_info->dad_time, + bgp->evpn_info->dad_freeze ? + "enable" : "disable", + bgp->evpn_info->dad_freeze_time); + + s = zclient->obuf; + stream_reset(s); + zclient_create_header(s, ZEBRA_DUPLICATE_ADDR_DETECTION, + bgp->vrf_id); + stream_putl(s, bgp->evpn_info->dup_addr_detect); + stream_putl(s, bgp->evpn_info->dad_time); + stream_putl(s, bgp->evpn_info->dad_max_moves); + stream_putl(s, bgp->evpn_info->dad_freeze); + stream_putl(s, bgp->evpn_info->dad_freeze_time); + stream_putw_at(s, 0, stream_get_endp(s)); + + return zclient_send_message(zclient); +} + static int rule_notify_owner(int command, struct zclient *zclient, zebra_size_t length, vrf_id_t vrf_id) { diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h index 2ea1fc777c..e7b7d683af 100644 --- a/bgpd/bgp_zebra.h +++ b/bgpd/bgp_zebra.h @@ -73,6 +73,7 @@ extern int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise, vni_t vni); extern int bgp_zebra_advertise_gw_macip(struct bgp *, int, vni_t); extern int bgp_zebra_advertise_all_vni(struct bgp *, int); +extern int bgp_zebra_dup_addr_detection(struct bgp *bgp); extern int bgp_zebra_vxlan_flood_control(struct bgp *bgp, enum vxlan_flood_control flood_ctrl); diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 48a3b1bb09..94cb285a03 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -86,8 +86,10 @@ #include "bgpd/bgp_labelpool.h" #include "bgpd/bgp_pbr.h" #include "bgpd/bgp_addpath.h" +#include "bgpd/bgp_evpn_private.h" DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)"); +DEFINE_MTYPE_STATIC(BGPD, BGP_EVPN_INFO, "BGP EVPN instance information"); DEFINE_QOBJ_TYPE(bgp_master) DEFINE_QOBJ_TYPE(bgp) DEFINE_QOBJ_TYPE(peer) @@ -2982,6 +2984,9 @@ static struct bgp *bgp_create(as_t *as, const char *name, /* assign a unique rd id for auto derivation of vrf's RD */ bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id); + bgp->evpn_info = XCALLOC(MTYPE_BGP_EVPN_INFO, + sizeof(struct bgp_evpn_info)); + bgp_evpn_init(bgp); bgp_pbr_init(bgp); return bgp; @@ -3373,6 +3378,7 @@ void bgp_free(struct bgp *bgp) bgp_evpn_cleanup(bgp); bgp_pbr_cleanup(bgp); + XFREE(MTYPE_BGP_EVPN_INFO, bgp->evpn_info); for (afi = AFI_IP; afi < AFI_MAX; afi++) { vpn_policy_direction_t dir; diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h index 725fe1ac9b..62096d651a 100644 --- a/bgpd/bgpd.h +++ b/bgpd/bgpd.h @@ -482,6 +482,8 @@ struct bgp { /* EVPN enable - advertise local VNIs and their MACs etc. */ int advertise_all_vni; + struct bgp_evpn_info *evpn_info; + /* EVPN - use RFC 8365 to auto-derive RT */ int advertise_autort_rfc8365; diff --git a/lib/log.c b/lib/log.c index 5cc303daf0..12a1d7fbe0 100644 --- a/lib/log.c +++ b/lib/log.c @@ -1034,6 +1034,7 @@ static const struct zebra_desc_table command_types[] = { DESC_ENTRY(ZEBRA_IP_PREFIX_ROUTE_DEL), DESC_ENTRY(ZEBRA_REMOTE_MACIP_ADD), DESC_ENTRY(ZEBRA_REMOTE_MACIP_DEL), + DESC_ENTRY(ZEBRA_DUPLICATE_ADDR_DETECTION), DESC_ENTRY(ZEBRA_PW_ADD), DESC_ENTRY(ZEBRA_PW_DELETE), DESC_ENTRY(ZEBRA_PW_SET), diff --git a/lib/monotime.h b/lib/monotime.h index 8e50c1874a..00b9400462 100644 --- a/lib/monotime.h +++ b/lib/monotime.h @@ -80,4 +80,15 @@ static inline int64_t monotime_until(const struct timeval *ref, return (int64_t)tv.tv_sec * 1000000LL + tv.tv_usec; } +static inline char *time_to_string(time_t ts) +{ + struct timeval tv; + time_t tbuf; + + monotime(&tv); + tbuf = time(NULL) - (tv.tv_sec - ts); + + return ctime(&tbuf); +} + #endif /* _FRR_MONOTIME_H */ diff --git a/lib/zclient.h b/lib/zclient.h index 07fe512a3c..adb48b252a 100644 --- a/lib/zclient.h +++ b/lib/zclient.h @@ -135,6 +135,7 @@ typedef enum { ZEBRA_IP_PREFIX_ROUTE_DEL, ZEBRA_REMOTE_MACIP_ADD, ZEBRA_REMOTE_MACIP_DEL, + ZEBRA_DUPLICATE_ADDR_DETECTION, ZEBRA_PW_ADD, ZEBRA_PW_DELETE, ZEBRA_PW_SET, diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index b93911bee7..9b84a6e58a 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2443,6 +2443,7 @@ void (*zserv_handlers[])(ZAPI_HANDLER_ARGS) = { [ZEBRA_REMOTE_VTEP_DEL] = zebra_vxlan_remote_vtep_del, [ZEBRA_REMOTE_MACIP_ADD] = zebra_vxlan_remote_macip_add, [ZEBRA_REMOTE_MACIP_DEL] = zebra_vxlan_remote_macip_del, + [ZEBRA_DUPLICATE_ADDR_DETECTION] = zebra_vxlan_dup_addr_detection, [ZEBRA_INTERFACE_SET_MASTER] = zread_interface_set_master, [ZEBRA_PW_ADD] = zread_pseudowire, [ZEBRA_PW_DELETE] = zread_pseudowire, 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_vrf.h b/zebra/zebra_vrf.h index ef02ca63e5..c28025403b 100644 --- a/zebra/zebra_vrf.h +++ b/zebra/zebra_vrf.h @@ -125,6 +125,13 @@ struct zebra_vrf { /* l3-vni info */ vni_t l3vni; + bool dup_addr_detect; + + int dad_time; + uint32_t dad_max_moves; + bool dad_freeze; + uint32_t dad_freeze_time; + /* * Flooding mechanism for BUM packets for VxLAN-EVPN. */ diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 263cb3d22c..536fdabcbc 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -1970,7 +1970,7 @@ DEFUN (show_evpn_mac_vni_all, bool uj = use_json(argc, argv); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_macs_all_vni(vty, zvrf, uj); + zebra_vxlan_print_macs_all_vni(vty, zvrf, false, uj); return CMD_SUCCESS; } @@ -2059,6 +2059,90 @@ DEFUN (show_evpn_mac_vni_vtep, return CMD_SUCCESS; } +DEFPY (show_evpn_mac_vni_all_dad, + show_evpn_mac_vni_all_dad_cmd, + "show evpn mac vni all duplicate [json]", + SHOW_STR + "EVPN\n" + "MAC addresses\n" + "VxLAN Network Identifier\n" + "All VNIs\n" + "Duplicate address list\n" + JSON_STR) +{ + struct zebra_vrf *zvrf; + bool uj = use_json(argc, argv); + + zvrf = vrf_info_lookup(VRF_DEFAULT); + zebra_vxlan_print_macs_all_vni(vty, zvrf, true, uj); + return CMD_SUCCESS; +} + + +DEFPY (show_evpn_mac_vni_dad, + show_evpn_mac_vni_dad_cmd, + "show evpn mac vni " CMD_VNI_RANGE " duplicate" "[json]", + SHOW_STR + "EVPN\n" + "MAC addresses\n" + "VxLAN Network Identifier\n" + "VNI number\n" + "Duplicate address list\n" + JSON_STR) +{ + struct zebra_vrf *zvrf; + vni_t vni; + bool uj = use_json(argc, argv); + + vni = strtoul(argv[4]->arg, NULL, 10); + zvrf = vrf_info_lookup(VRF_DEFAULT); + + zebra_vxlan_print_macs_vni_dad(vty, zvrf, vni, uj); + + return CMD_SUCCESS; +} + +DEFPY (show_evpn_neigh_vni_dad, + show_evpn_neigh_vni_dad_cmd, + "show evpn arp-cache vni " CMD_VNI_RANGE "duplicate" "[json]", + SHOW_STR + "EVPN\n" + "ARP and ND cache\n" + "VxLAN Network Identifier\n" + "VNI number\n" + "Duplicate address list\n" + JSON_STR) +{ + struct zebra_vrf *zvrf; + vni_t vni; + bool uj = use_json(argc, argv); + + vni = strtoul(argv[4]->arg, NULL, 10); + zvrf = vrf_info_lookup(VRF_DEFAULT); + zebra_vxlan_print_neigh_vni_dad(vty, zvrf, vni, uj); + return CMD_SUCCESS; +} + +DEFPY (show_evpn_neigh_vni_all_dad, + show_evpn_neigh_vni_all_dad_cmd, + "show evpn arp-cache vni all duplicate [json]", + SHOW_STR + "EVPN\n" + "ARP and ND cache\n" + "VxLAN Network Identifier\n" + "All VNIs\n" + "Duplicate address list\n" + JSON_STR) +{ + struct zebra_vrf *zvrf; + bool uj = use_json(argc, argv); + + zvrf = vrf_info_lookup(VRF_DEFAULT); + zebra_vxlan_print_neigh_all_vni(vty, zvrf, true, uj); + return CMD_SUCCESS; +} + + DEFUN (show_evpn_neigh_vni, show_evpn_neigh_vni_cmd, "show evpn arp-cache vni " CMD_VNI_RANGE "[json]", @@ -2093,7 +2177,7 @@ DEFUN (show_evpn_neigh_vni_all, bool uj = use_json(argc, argv); zvrf = vrf_info_lookup(VRF_DEFAULT); - zebra_vxlan_print_neigh_all_vni(vty, zvrf, uj); + zebra_vxlan_print_neigh_all_vni(vty, zvrf, false, uj); return CMD_SUCCESS; } @@ -2193,6 +2277,55 @@ DEFUN (show_pbr_iptable, return CMD_SUCCESS; } +DEFPY (clear_evpn_dup_addr, + clear_evpn_dup_addr_cmd, + "clear evpn dup-addr vni ]>", + CLEAR_STR + "EVPN\n" + "Duplicate address \n" + "VxLAN Network Identifier\n" + "VNI number\n" + "All VNIs\n" + "MAC\n" + "MAC address (e.g., 00:e0:ec:20:12:62)\n" + "IP\n" + "IPv4 address\n" + "IPv6 address\n") +{ + struct zebra_vrf *zvrf; + vni_t vni = 0; + struct ipaddr host_ip = {.ipa_type = IPADDR_NONE }; + struct ethaddr mac_addr; + + zvrf = vrf_info_lookup(VRF_DEFAULT); + if (vni_val) { + vni = strtoul(vni_val, NULL, 10); + + if (mac_val) { + prefix_str2mac(mac_val, &mac_addr); + zebra_vxlan_clear_dup_detect_vni_mac(vty, zvrf, vni, + &mac_addr); + } else if (ip) { + if (sockunion_family(ip) == AF_INET) { + host_ip.ipa_type = IPADDR_V4; + host_ip.ipaddr_v4.s_addr = sockunion2ip(ip); + } else { + host_ip.ipa_type = IPADDR_V6; + memcpy(&host_ip.ipaddr_v6, &ip->sin6.sin6_addr, + sizeof(struct in6_addr)); + } + zebra_vxlan_clear_dup_detect_vni_ip(vty, zvrf, vni, + &host_ip); + } else + zebra_vxlan_clear_dup_detect_vni(vty, zvrf, vni); + + } else { + zebra_vxlan_clear_dup_detect_vni_all(vty, zvrf); + } + + return CMD_SUCCESS; +} + /* Static ip route configuration write function. */ static int zebra_ip_config(struct vty *vty) { @@ -2755,10 +2888,15 @@ void zebra_vty_init(void) install_element(VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_mac_cmd); install_element(VIEW_NODE, &show_evpn_mac_vni_vtep_cmd); + install_element(VIEW_NODE, &show_evpn_mac_vni_dad_cmd); + install_element(VIEW_NODE, &show_evpn_mac_vni_all_dad_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_all_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd); install_element(VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd); + install_element(VIEW_NODE, &show_evpn_neigh_vni_dad_cmd); + install_element(VIEW_NODE, &show_evpn_neigh_vni_all_dad_cmd); + install_element(ENABLE_NODE, &clear_evpn_dup_addr_cmd); install_element(VIEW_NODE, &show_pbr_ipset_cmd); install_element(VIEW_NODE, &show_pbr_iptable_cmd); 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, @@ -5100,6 +5751,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). */ @@ -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; +} diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h index 1c394e9eff..bf6e4290dc 100644 --- a/zebra/zebra_vxlan.h +++ b/zebra/zebra_vxlan.h @@ -70,6 +70,7 @@ extern void zebra_vxlan_flood_control(ZAPI_HANDLER_ARGS); extern void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS); extern void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS); extern void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS); +extern void zebra_vxlan_dup_addr_detection(ZAPI_HANDLER_ARGS); extern int is_l3vni_for_prefix_routes_only(vni_t vni); extern ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id); @@ -87,6 +88,7 @@ extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json); extern void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, + bool print_dup, bool use_json); extern void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, @@ -100,10 +102,14 @@ extern void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct in_addr vtep_ip, bool use_json); +extern void zebra_vxlan_print_macs_vni_dad(struct vty *vty, + struct zebra_vrf *zvrf, vni_t vni, + bool use_json); extern void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json); extern void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf, + bool print_dup, bool use_json); extern void zebra_vxlan_print_specific_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf, @@ -113,6 +119,9 @@ extern void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, struct in_addr vtep_ip, bool use_json); +extern void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, + struct zebra_vrf *zvrf, vni_t vni, + bool use_json); extern void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni, bool use_json); extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, @@ -172,5 +181,17 @@ extern void zebra_vxlan_evpn_vrf_route_add(vrf_id_t vrf_id, extern void zebra_vxlan_evpn_vrf_route_del(vrf_id_t vrf_id, struct ipaddr *vtep_ip, struct prefix *host_prefix); +extern void zebra_vxlan_clear_dup_detect_vni_mac(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, + struct ethaddr *macaddr); +extern void zebra_vxlan_clear_dup_detect_vni_ip(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, struct ipaddr *ip); +extern void zebra_vxlan_clear_dup_detect_vni_all(struct vty *vty, + struct zebra_vrf *zvrf); +extern void zebra_vxlan_clear_dup_detect_vni(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni); #endif /* _ZEBRA_VXLAN_H */ diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h index 44163eb331..3be7dc012a 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; }; /* @@ -292,6 +305,7 @@ struct mac_walk_ctx { struct vty *vty; /* Used by VTY handlers */ uint32_t count; /* Used by VTY handlers */ struct json_object *json; /* Used for JSON Output */ + bool print_dup; /* Used to print dup addr list */ }; struct rmac_walk_ctx { @@ -330,12 +344,15 @@ struct zebra_neigh_t_ { /* Underlying interface. */ ifindex_t ifindex; + zebra_vni_t *zvni; + uint32_t flags; #define ZEBRA_NEIGH_LOCAL 0x01 #define ZEBRA_NEIGH_REMOTE 0x02 #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 +371,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; }; /*