diff options
52 files changed, 2380 insertions, 152 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 65a46bb965..87ebb9c285 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -3495,8 +3495,8 @@ void bgp_packet_mpunreach_prefix(struct stream *s, struct prefix *p, afi_t afi, num_labels = 1; } - return bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label, num_labels, - addpath_encode, addpath_tx_id, attr); + bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label, num_labels, + addpath_encode, addpath_tx_id, attr); } void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt) 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 aa5eabeade..e915574ed0 100644 --- a/bgpd/bgp_evpn_vty.c +++ b/bgpd/bgp_evpn_vty.c @@ -1696,10 +1696,10 @@ static void evpn_unconfigure_import_rt(struct bgp *bgp, struct bgpevpn *vpn, /* Delete all import RTs */ if (ecomdel == NULL) { - for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) + for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) { ecommunity_free(&ecom); - - list_delete_all_node(vpn->import_rtl); + list_delete_node(vpn->import_rtl, node); + } } /* Delete a specific import RT */ @@ -1764,10 +1764,10 @@ static void evpn_unconfigure_export_rt(struct bgp *bgp, struct bgpevpn *vpn, /* Delete all export RTs */ if (ecomdel == NULL) { /* Reset to default and process all routes. */ - for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) + for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) { ecommunity_free(&ecom); - - list_delete_all_node(vpn->export_rtl); + list_delete_node(vpn->export_rtl, node); + } } /* Delete a specific export RT */ @@ -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 <permanent |(30-3600)$freeze_time_val>", + "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 <permanent$permanent_val | (30-3600)$freeze_time_val>]", + 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_fsm.c b/bgpd/bgp_fsm.c index 1f0cfd6e25..b70c8bbd63 100644 --- a/bgpd/bgp_fsm.c +++ b/bgpd/bgp_fsm.c @@ -935,9 +935,29 @@ static void bgp_update_delay_process_status_change(struct peer *peer) read/write and timer thread. */ void bgp_fsm_change_status(struct peer *peer, int status) { + struct bgp *bgp; + uint32_t peer_count; bgp_dump_state(peer, peer->status, status); + bgp = peer->bgp; + peer_count = bgp->established_peers; + + if (status == Established) + bgp->established_peers++; + else if ((peer->status == Established) && (status != Established)) + bgp->established_peers--; + + if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) + zlog_debug("%s : vrf %u, established_peers %u", __func__, + bgp->vrf_id, bgp->established_peers); + /* Set to router ID to the value provided by RIB if there are no peers + * in the established state and peer count did not change + */ + if ((peer_count != bgp->established_peers) && + (bgp->established_peers == 0)) + bgp_router_id_zebra_bump(bgp->vrf_id, NULL); + /* Transition into Clearing or Deleted must /always/ clear all routes.. * (and must do so before actually changing into Deleted.. */ diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index 0dce96f432..c5d12a5706 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -866,6 +866,8 @@ void bgp_nht_register_enhe_capability_interfaces(struct peer *peer) if (p.family != AF_INET6) return; rn = bgp_node_lookup(bgp->nexthop_cache_table[AFI_IP6], &p); + if (!rn) + return; bnc = bgp_nexthop_get_node_info(rn); if (!bnc) diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c index edd8317c31..4e050df3e5 100644 --- a/bgpd/bgp_pbr.c +++ b/bgpd/bgp_pbr.c @@ -1508,9 +1508,10 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive( struct list *orig_list; struct bgp_pbr_val_mask **target_val; - if (type_entry == 0) - return bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, - bpf); + if (type_entry == 0) { + bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, bpf); + return; + } next_type_entry = bgp_pbr_next_type_entry(type_entry); if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) { orig_list = bpof->tcpflags; @@ -1530,8 +1531,9 @@ static void bgp_pbr_policyroute_remove_from_zebra_recursive( bgp_pbr_icmp_action(bgp, path, bpf, bpof, false, NULL, NULL); return; } else { - return bgp_pbr_policyroute_remove_from_zebra_recursive( + bgp_pbr_policyroute_remove_from_zebra_recursive( bgp, path, bpf, bpof, next_type_entry); + return; } for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) { *target_val = valmask; @@ -1544,9 +1546,10 @@ static void bgp_pbr_policyroute_remove_from_zebra( struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_filter *bpf, struct bgp_pbr_or_filter *bpof) { - if (!bpof) - return bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, - bpf); + if (!bpof) { + bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, bpf); + return; + } if (bpof->tcpflags) bgp_pbr_policyroute_remove_from_zebra_recursive( bgp, path, bpf, bpof, FLOWSPEC_TCP_FLAGS); @@ -1903,9 +1906,10 @@ static void bgp_pbr_policyroute_add_to_zebra_recursive( struct list *orig_list; struct bgp_pbr_val_mask **target_val; - if (type_entry == 0) - return bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, - rate); + if (type_entry == 0) { + bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, rate); + return; + } next_type_entry = bgp_pbr_next_type_entry(type_entry); if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) { orig_list = bpof->tcpflags; @@ -1925,8 +1929,9 @@ static void bgp_pbr_policyroute_add_to_zebra_recursive( bgp_pbr_icmp_action(bgp, path, bpf, bpof, true, nh, rate); return; } else { - return bgp_pbr_policyroute_add_to_zebra_recursive( + bgp_pbr_policyroute_add_to_zebra_recursive( bgp, path, bpf, bpof, nh, rate, next_type_entry); + return; } for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) { *target_val = valmask; @@ -1941,9 +1946,10 @@ static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp, struct bgp_pbr_or_filter *bpof, struct nexthop *nh, float *rate) { - if (!bpof) - return bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, - rate); + if (!bpof) { + bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, rate); + return; + } if (bpof->tcpflags) bgp_pbr_policyroute_add_to_zebra_recursive( bgp, path, bpf, bpof, nh, rate, FLOWSPEC_TCP_FLAGS); @@ -2112,9 +2118,10 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path, bpf.protocol = proto; bpf.src_port = srcp; bpf.dst_port = dstp; - if (!add) - return bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, - &bpof); + if (!add) { + bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, &bpof); + return; + } /* no action for add = true */ for (i = 0; i < api->action_num; i++) { switch (api->actions[i].action) { diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c index 85b6414e48..69a0b78378 100644 --- a/bgpd/bgp_vty.c +++ b/bgpd/bgp_vty.c @@ -602,7 +602,7 @@ static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi, bgp->update_delay_over = 0; if (!found) - vty_out(vty, "%%BGP: No %s peer configured", + vty_out(vty, "%%BGP: No %s peer configured\n", afi_safi_print(afi, safi)); return CMD_SUCCESS; @@ -972,6 +972,7 @@ DEFUN_NOSH (router_bgp, int idx_asn = 2; int idx_view_vrf = 3; int idx_vrf = 4; + int is_new_bgp = 0; int ret; as_t as; struct bgp *bgp; @@ -1011,6 +1012,9 @@ DEFUN_NOSH (router_bgp, inst_type = BGP_INSTANCE_TYPE_VIEW; } + if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) + is_new_bgp = (bgp_lookup(as, name) == NULL); + ret = bgp_get(&bgp, &as, name, inst_type); switch (ret) { case BGP_ERR_MULTIPLE_INSTANCE_NOT_SET: @@ -1034,7 +1038,7 @@ DEFUN_NOSH (router_bgp, * any pending VRF-VPN leaking that was configured via * earlier "router bgp X vrf FOO" blocks. */ - if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) + if (is_new_bgp && inst_type == BGP_INSTANCE_TYPE_DEFAULT) vpn_leak_postchange_all(); /* Pending: handle when user tries to change a view to vrf n vv. diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index 62f977eee1..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; @@ -1217,9 +1218,10 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p, if (bgp_debug_zebra(p)) prefix2str(p, buf_prefix, sizeof(buf_prefix)); - if (safi == SAFI_FLOWSPEC) - return bgp_pbr_update_entry(bgp, &rn->p, - info, afi, safi, true); + if (safi == SAFI_FLOWSPEC) { + bgp_pbr_update_entry(bgp, &rn->p, info, afi, safi, true); + return; + } /* * vrf leaking support (will have only one nexthop) @@ -1505,8 +1507,8 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_path_info *info, if (safi == SAFI_FLOWSPEC) { peer = info->peer; - return bgp_pbr_update_entry(peer->bgp, p, - info, AFI_IP, safi, false); + bgp_pbr_update_entry(peer->bgp, p, info, AFI_IP, safi, false); + return; } memset(&api, 0, sizeof(api)); @@ -1998,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) { @@ -2501,19 +2539,19 @@ static void bgp_zebra_process_local_ip_prefix(int cmd, struct zclient *zclient, if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) { if (p.family == AF_INET) - return bgp_evpn_advertise_type5_route( - bgp_vrf, &p, NULL, AFI_IP, SAFI_UNICAST); + bgp_evpn_advertise_type5_route(bgp_vrf, &p, NULL, + AFI_IP, SAFI_UNICAST); else - return bgp_evpn_advertise_type5_route( - bgp_vrf, &p, NULL, AFI_IP6, SAFI_UNICAST); + bgp_evpn_advertise_type5_route(bgp_vrf, &p, NULL, + AFI_IP6, SAFI_UNICAST); } else { if (p.family == AF_INET) - return bgp_evpn_withdraw_type5_route( - bgp_vrf, &p, AFI_IP, SAFI_UNICAST); + bgp_evpn_withdraw_type5_route(bgp_vrf, &p, AFI_IP, + SAFI_UNICAST); else - return bgp_evpn_withdraw_type5_route( - bgp_vrf, &p, AFI_IP6, SAFI_UNICAST); + bgp_evpn_withdraw_type5_route(bgp_vrf, &p, AFI_IP6, + SAFI_UNICAST); } } 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 a078c4f587..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) @@ -274,6 +276,10 @@ void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id) { struct listnode *node, *nnode; struct bgp *bgp; + struct in_addr *addr = NULL; + + if (router_id != NULL) + addr = (struct in_addr *)&(router_id->u.prefix4); if (vrf_id == VRF_DEFAULT) { /* Router-id change for default VRF has to also update all @@ -282,17 +288,43 @@ void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id) if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) continue; - bgp->router_id_zebra = router_id->u.prefix4; - if (!bgp->router_id_static.s_addr) - bgp_router_id_set(bgp, &router_id->u.prefix4); + if (addr) + bgp->router_id_zebra = *addr; + else + addr = &bgp->router_id_zebra; + + if (!bgp->router_id_static.s_addr) { + /* Router ID is updated if there are no active + * peer sessions + */ + if (bgp->established_peers == 0) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("RID change : vrf %u, RTR ID %s", + bgp->vrf_id, inet_ntoa(*addr)); + bgp_router_id_set(bgp, addr); + } + } } } else { bgp = bgp_lookup_by_vrf_id(vrf_id); if (bgp) { - bgp->router_id_zebra = router_id->u.prefix4; + if (addr) + bgp->router_id_zebra = *addr; + else + addr = &bgp->router_id_zebra; + + if (!bgp->router_id_static.s_addr) { + /* Router ID is updated if there are no active + * peer sessions + */ + if (bgp->established_peers == 0) { + if (BGP_DEBUG(zebra, ZEBRA)) + zlog_debug("RID change : vrf %u, RTR ID %s", + bgp->vrf_id, inet_ntoa(*addr)); + bgp_router_id_set(bgp, addr); + } + } - if (!bgp->router_id_static.s_addr) - bgp_router_id_set(bgp, &router_id->u.prefix4); } } } @@ -2952,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; @@ -3343,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 70193104b4..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; @@ -542,6 +544,9 @@ struct bgp { /* local esi hash table */ struct hash *esihash; + /* Count of peers in established state */ + uint32_t established_peers; + QOBJ_FIELDS }; DECLARE_QOBJ_TYPE(bgp) @@ -1004,7 +1009,7 @@ struct peer { struct thread *t_process_packet; /* Thread flags. */ - _Atomic uint16_t thread_flags; + _Atomic uint32_t thread_flags; #define PEER_THREAD_WRITES_ON (1 << 0) #define PEER_THREAD_READS_ON (1 << 1) #define PEER_THREAD_KEEPALIVES_ON (1 << 2) @@ -1886,5 +1891,4 @@ extern void bgp_update_redist_vrf_bitmaps(struct bgp *, vrf_id_t); /* For benefit of rfapi */ extern struct peer *peer_new(struct bgp *bgp); - #endif /* _QUAGGA_BGPD_H */ diff --git a/configure.ac b/configure.ac index 13f6fdab72..c925509faf 100755 --- a/configure.ac +++ b/configure.ac @@ -334,6 +334,12 @@ _LT_CONFIG_LIBTOOL([ ]) if test "$enable_static_bin" = "yes"; then AC_LDFLAGS="-static" + if test "$enable_static" != "yes"; then + AC_MSG_ERROR([The --enable-static-bin option must be combined with --enable-static.]) + fi +fi +if test "$enable_shared" != "yes"; then + AC_MSG_ERROR([FRR cannot be built with --disable-shared. If you want statically linked daemons, use --enable-shared --enable-static --enable-static-bin]) fi AC_SUBST([AC_LDFLAGS]) AM_CONDITIONAL([STATIC_BIN], [test "x$enable_static_bin" = "xyes"]) diff --git a/doc/developer/index.rst b/doc/developer/index.rst index 42192db637..9838e1098c 100644 --- a/doc/developer/index.rst +++ b/doc/developer/index.rst @@ -12,3 +12,4 @@ FRRouting Developer's Guide bgpd ospf zebra + vtysh diff --git a/doc/developer/vtysh.rst b/doc/developer/vtysh.rst new file mode 100644 index 0000000000..4a52eb0544 --- /dev/null +++ b/doc/developer/vtysh.rst @@ -0,0 +1,201 @@ +.. _vtysh: + +***** +VTYSH +***** + +.. seealso:: :ref:`command-line-interface` + +.. _vtysh-architecture: + +Architecture +============ + +VTYSH is a shell for FRR daemons. It amalgamates all the CLI commands defined +in each of the daemons and presents them to the user in a single shell, which +saves the user from having to telnet to each of the daemons and use their +individual shells. The amalgamation is achieved by +:ref:`extracting <vtysh-command-extraction>` commands from daemons and +injecting them into VTYSH at build time. + +At runtime, VTYSH maintains an instance of a CLI mode tree just like each +daemon. However, the mode tree in VTYSH contains (almost) all commands from +every daemon in the same tree, whereas individual daemons have trees that only +contain commands relevant to themselves. VTYSH also uses the library CLI +facilities to maintain the user's current position in the tree (the current +node). Note that this position must be synchronized with all daemons; if a +daemon receives a command that causes it to change its current node, VTYSH must +also change its node. Since the extraction script does not understand the +handler code of commands, but only their definitions, this and other behaviors +must be manually programmed into VTYSH for every case where the internal state +of VTYSH must change in response to a command. Details on how this is done are +discussed in the :ref:`vtysh-special-defuns` section. + +VTYSH also handles writing and applying the integrated configuration file, +:file:`/etc/frr/frr.conf`. Since it has knowledge of the entire command space +of FRR, it can intelligently distribute configuration commands only to the +daemons that understand them. Similarly, when writing the configuration file it +takes care of combining multiple instances of configuration blocks and +simplifying the output. This is discussed in :ref:`vtysh-configuration`. + +.. _vtysh-command-extraction: + +Command Extraction +------------------ + +When VTYSH is a built, a Perl script named :file:`extract.pl` searches the FRR +codebase looking for ``DEFUN``'s. It extracts these ``DEFUN``'s, transforms +them into ``DEFSH``'s and appends them to ``vtysh_cmd.c``. Each ``DEFSH`` +contains the name of the command plus ``_vtysh``, as well as a flag that +indicates which daemons the command was found in. When the command is executed +in VTYSH, this flag is inspected to determine which daemons to send the command +to. This way, commands are only sent to the daemons that know about them, +avoiding spurious errors from daemons that don't have the command defined. + +The extraction script contains lots of hardcoded knowledge about what sources +to look at and what flags to use for certain commands. + +.. _vtysh-special-defuns: + +Special DEFUNs +-------------- + +In addition to the vanilla ``DEFUN`` macro for defining CLI commands, there are +several VTYSH-specific ``DEFUN`` variants that each serve different purposes. + +``DEFSH`` + Used almost exclusively by generated VTYSH code. This macro defines a + ``cmd_element`` with no handler function; the command, when executed, is + simply forwarded to the daemons indicated in the daemon flag. + +``DEFUN_NOSH`` + Used by daemons. Has the same expansion as a ``DEFUN``, but ``extract.pl`` + will skip these definitions when extracting commands. This is typically used + when VTYSH must take some special action upon receiving the command, and the + programmer therefore needs to write VTYSH's copy of the command manually + instead of using the generated version. + +``DEFUNSH`` + The same as ``DEFUN``, but with an argument that allows specifying the + ``->daemon`` field of the generated ``cmd_element``. This is used by VTYSH + to determine which daemons to send the command to. + +``DEFUNSH_ATTR`` + A version of ``DEFUNSH`` that allows setting the ``->attr`` field of the + generated ``cmd_element``. Not used in practice. + +.. _vtysh-configuration: + +Configuration Management +------------------------ + +When integrated configuration is used, VTYSH manages writing, reading and +applying the FRR configuration file. VTYSH can be made to read and apply an +integrated configuration to all running daemons by launching it with ``-f +<file>``. It sends the appropriate configuration lines to the relevant daemons +in the same way that commands entered by the user on VTYSH's shell prompt are +processed. + +Configuration writing is more complicated. VTYSH makes a best-effort attempt to +combine and simplify the configuration as much as possible. A working example +is best to explain this behavior. + +Example +^^^^^^^ + +Suppose we have just *staticd* and *zebra* running on the system, and use VTYSH +to apply the following configuration snippet: + +.. code-block:: frr + + ! + vrf blue + ip protocol static route-map ExampleRoutemap + ip route 192.168.0.0/24 192.168.0.1 + exit-vrf + ! + +Note that *staticd* defines static route commands and *zebra* defines ``ip +protocol`` commands. Therefore if we ask only *zebra* for its configuration, we +get the following:: + + (config)# do sh running-config zebra + Building configuration... + + ... + ! + vrf blue + ip protocol static route-map ExampleRoutemap + exit-vrf + ! + ... + +Note that the static route doesn't show up there. Similarly, if we ask +*staticd* for its configuration, we get:: + + (config)# do sh running-config staticd + + ... + ! + vrf blue + ip route 192.168.0.0/24 192.168.0.1 + exit-vrf + ! + ... + +But when we display the configuration with VTYSH, we see:: + + ubuntu-bionic(config)# do sh running-config + + ... + ! + vrf blue + ip protocol static route-map ExampleRoutemap + ip route 192.168.0.0/24 192.168.0.1 + exit-vrf + ! + ... + +This is because VTYSH asks each daemon for its currently running configuration, +and combines equivalent blocks together. In the above example, it combined the +``vrf blue`` blocks from both *zebra* and *staticd* together into one. This is +done in :file:`vtysh_config.c`. + +Protocol +======== + +VTYSH communicates with FRR daemons by way of domain socket. Each daemon +creates its own socket, typically in :file:`/var/run/frr/<daemon>.vty`. The +protocol is very simple. In the VTYSH to daemon direction, messages are simply +NULL-terminated strings, whose content are CLI commands. Here is a typical +message from VTYSH to a daemon: + +:: + + Request + + 00000000: 646f 2077 7269 7465 2074 6572 6d69 6e61 do write termina + 00000010: 6c0a 00 l.. + + +The response format has some more data in it. First is a NULL-terminated string +containing the plaintext response, which is just the output of the command that +was sent in the request. This is displayed to the user. The plaintext response +is followed by 3 null marker bytes, followed by a 1-byte status code that +indicates whether the command was successful or not. + +:: + + Response + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Plaintext Response | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Marker (0x00) | Status Code | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + +The first ``0x00`` byte in the marker also serves to terminate the plaintext +response. diff --git a/doc/developer/workflow.rst b/doc/developer/workflow.rst index ee7592fd6a..543dfdd3b9 100644 --- a/doc/developer/workflow.rst +++ b/doc/developer/workflow.rst @@ -91,17 +91,63 @@ documentation and packaging systems will be updated to reflect the next possible release name to allow for easy distinguishing. After one month the development branch will be renamed to -``stable/MAJOR.MINOR``. This process is not held up unless a crash or security -issue has been found and needs to be addressed. Issues being fixed will not -cause a delay. +``stable/MAJOR.MINOR``. The branch is a stable branch. This process is not +held up unless a crash or security issue has been found and needs to +be addressed. Issues being fixed will not cause a delay. Bugfix releases are made as needed at 1 month intervals until the next -``MAJOR.MINOR`` relese branch is pulled. Depending on the severity of the bugs, +``MAJOR.MINOR`` release branch is pulled. Depending on the severity of the bugs, bugfix releases may occur sooner. Bugfixes are applied to the two most recent releases. Security fixes are -backported to all releases less than or equal to one year old. Security fixes -may also be backported to older releases depending on severity. +backported to all releases less than or equal to at least one year old. Security +fixes may also be backported to older releases depending on severity. + +Long term support branches ( LTS ) +----------------------------------------- + +This kind of branch is not yet officially supported, and need experimentation +before being effective. + +Previous definition of releases prevents long term support of previous releases. +For instance, bug and security fixes are not applied if the stable branch is too +old. + +Because the FRR users have a need to backport bug and security fixes after the +stable branch becomes too old, there is a need to provide support on a long term +basis on that stable branch. If that support is applied on that stable branch, +then that branch is a long term support branch. + +Having a LTS branch requires extra-work and requires one person to be in charge +of that maintenance branch for a certain amount of time. The amount of time will +be by default set to 4 months, and can be increased. 4 months stands for the time +between two releases, this time can be applied to the decision to continue with a +LTS release or not. In all cases, that time period will be well-defined and +published. Also, a self nomination from a person that proposes to handle the LTS +branch is required. The work can be shared by multiple people. In all cases, there +must be at least one person that is in charge of the maintenance branch. The person +on people responsible for a maintenance branch must be a FRR maintainer. Note that +they may choose to abandon support for the maintenance branch at any time. If +noone takes over the responsibility of the LTS branch, then the support will be +discontinued. + +The LTS branch duties are the following ones: + +- organise meetings on a (bi-)weekly or monthly basis, the handling of issues + and pull requested relative to that branch. When time permits, this may be done + during the regularly scheduled FRR meeting. + +- ensure the stability of the branch, by using and eventually adapting the + checking the CI tools of FRR ( indeed, maintaining may lead to create + maintenance branches for topotests or for CI). + +It will not be possible to backport feature requests to LTS branches. Actually, it +is a false good idea to use LTS for that need. Introducing feature requests may +break the paradigm where all more recent releases should also include the feature +request. This would require the LTS maintainer to ensure that all more recent +releases have support for this feature request. Moreover, introducing features +requests may result in breaking the stability of the branch. LTS branches are first +done to bring long term support for stability. Changelog --------- diff --git a/isisd/fabricd.c b/isisd/fabricd.c index 76c8087f2d..2b3116b1ff 100644 --- a/isisd/fabricd.c +++ b/isisd/fabricd.c @@ -585,17 +585,19 @@ void fabricd_lsp_flood(struct isis_lsp *lsp) while (!skiplist_next(f->neighbors, NULL, (void **)&n, &cursor)) { n->present = true; - struct isis_lsp *lsp = lsp_for_vertex(f->spftree, n->vertex); - if (!lsp || !lsp->tlvs || !lsp->tlvs->spine_leaf) - continue; - - if (!lsp->tlvs->spine_leaf->has_tier - || lsp->tlvs->spine_leaf->tier != 0) + struct isis_lsp *node_lsp = lsp_for_vertex(f->spftree, + n->vertex); + if (!node_lsp + || !node_lsp->tlvs + || !node_lsp->tlvs->spine_leaf + || !node_lsp->tlvs->spine_leaf->has_tier + || node_lsp->tlvs->spine_leaf->tier != 0) { continue; + } if (isis->debugs & DEBUG_FABRICD_FLOODING) { zlog_debug("Moving %s to DNR because it's T0", - rawlspid_print(lsp->hdr.lsp_id)); + rawlspid_print(node_lsp->hdr.lsp_id)); } move_to_dnr(lsp, n); diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 38239d5919..9a57d0d0ac 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -253,7 +253,8 @@ int lsp_compare(char *areatag, struct isis_lsp *lsp, uint32_t seqno, if (seqno > lsp->hdr.seqno || (seqno == lsp->hdr.seqno && ((lsp->hdr.rem_lifetime != 0 && rem_lifetime == 0) - || lsp->hdr.checksum != checksum))) { + || (lsp->hdr.checksum != checksum + && lsp->hdr.rem_lifetime)))) { if (isis->debugs & DEBUG_SNP_PACKETS) { zlog_debug( "ISIS-Snp (%s): Compare LSP %s seq 0x%08" PRIx32 diff --git a/isisd/isis_mt.c b/isisd/isis_mt.c index 7493b90d33..f7d4c7170f 100644 --- a/isisd/isis_mt.c +++ b/isisd/isis_mt.c @@ -398,7 +398,7 @@ bool tlvs_to_adj_mt_set(struct isis_tlvs *tlvs, bool v4_usable, bool v6_usable, && !tlvs->mt_router_info_empty) { /* Other end does not have MT enabled */ if (mt_settings[i]->mtid == ISIS_MT_IPV4_UNICAST - && v4_usable) + && (v4_usable || v6_usable)) adj_mt_set(adj, intersect_count++, ISIS_MT_IPV4_UNICAST); } else { diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 99041b0e61..7df152f1fa 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -747,7 +747,7 @@ static int process_lsp(uint8_t pdu_type, struct isis_circuit *circuit, stream_get_endp(circuit->rcv_stream)); } - struct isis_lsp_hdr hdr = {}; + struct isis_lsp_hdr hdr = {0}; hdr.pdu_len = stream_getw(circuit->rcv_stream); hdr.rem_lifetime = stream_getw(circuit->rcv_stream); @@ -1011,19 +1011,26 @@ dontcheckadj: * is * "greater" than that held by S, ... */ - if (hdr.seqno > lsp->hdr.seqno) { + if (comp == LSP_NEWER) { /* 7.3.16.1 */ lsp_inc_seqno(lsp, hdr.seqno); - if (isis->debugs & DEBUG_UPDATE_PACKETS) + if (isis->debugs & DEBUG_UPDATE_PACKETS) { zlog_debug( "ISIS-Upd (%s): (2) re-originating LSP %s new seq 0x%08" PRIx32, circuit->area->area_tag, rawlspid_print(hdr.lsp_id), lsp->hdr.seqno); + } + lsp_flood(lsp, NULL); + } else if (comp == LSP_EQUAL) { + isis_tx_queue_del(circuit->tx_queue, lsp); + if (circuit->circ_type != CIRCUIT_T_BROADCAST) + ISIS_SET_FLAG(lsp->SSNflags, circuit); + } else { + isis_tx_queue_add(circuit->tx_queue, lsp, + TX_LSP_NORMAL); + ISIS_CLEAR_FLAG(lsp->SSNflags, circuit); } - /* If the received LSP is older or equal, - * resend the LSP which will act as ACK */ - lsp_flood(lsp, NULL); } else { /* 7.3.15.1 e) - This lsp originated on another system */ @@ -1119,8 +1126,8 @@ static int process_snp(uint8_t pdu_type, struct isis_circuit *circuit, stream_get(rem_sys_id, circuit->rcv_stream, ISIS_SYS_ID_LEN); stream_forward_getp(circuit->rcv_stream, 1); /* Circuit ID - unused */ - uint8_t start_lsp_id[ISIS_SYS_ID_LEN + 2] = {}; - uint8_t stop_lsp_id[ISIS_SYS_ID_LEN + 2] = {}; + uint8_t start_lsp_id[ISIS_SYS_ID_LEN + 2] = {0}; + uint8_t stop_lsp_id[ISIS_SYS_ID_LEN + 2] = {0}; if (is_csnp) { stream_get(start_lsp_id, circuit->rcv_stream, diff --git a/isisd/isis_route.c b/isisd/isis_route.c index f9b4a2b146..1439a4229a 100644 --- a/isisd/isis_route.c +++ b/isisd/isis_route.c @@ -492,7 +492,7 @@ static void _isis_route_verify_table(struct isis_area *area, void isis_route_verify_table(struct isis_area *area, struct route_table *table) { - return _isis_route_verify_table(area, table, NULL); + _isis_route_verify_table(area, table, NULL); } /* Function to validate route tables for L1L2 areas. In this case we can't use diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index fce3a0a113..5e73baa841 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -171,8 +171,7 @@ static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s, struct sbuf *log, void *dest, int indent) { struct isis_subtlvs *subtlvs = dest; - struct isis_prefix_sid sid = { - }; + struct isis_prefix_sid sid = {0}; sbuf_push(log, indent, "Unpacking SR Prefix-SID...\n"); @@ -2052,7 +2051,7 @@ static int unpack_tlv_purge_originator(enum isis_tlv_context context, void *dest, int indent) { struct isis_tlvs *tlvs = dest; - struct isis_purge_originator poi = {}; + struct isis_purge_originator poi = {0}; sbuf_push(log, indent, "Unpacking Purge Originator Identification TLV...\n"); if (tlv_len < 7) { @@ -3131,7 +3130,7 @@ static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { [ISIS_TLV_IPV6_REACH] = &tlv_ipv6_reach_ops, [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops, }, - [ISIS_CONTEXT_SUBTLV_NE_REACH] = {}, + [ISIS_CONTEXT_SUBTLV_NE_REACH] = {0}, [ISIS_CONTEXT_SUBTLV_IP_REACH] = { [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops, }, @@ -3397,7 +3396,7 @@ static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs, ipv6_supported = true; } - struct nlpids reduced = {}; + struct nlpids reduced = {0}; if (ipv4_supported && ipv6_supported) { reduced.count = 2; @@ -3408,7 +3407,7 @@ static void tlvs_protocols_supported_to_adj(struct isis_tlvs *tlvs, reduced.nlpids[0] = NLPID_IP; } else if (ipv6_supported) { reduced.count = 1; - reduced.nlpids[1] = NLPID_IPV6; + reduced.nlpids[0] = NLPID_IPV6; } else { reduced.count = 0; } diff --git a/lib/agg_table.h b/lib/agg_table.h index a703969fdb..dc2ff03b67 100644 --- a/lib/agg_table.h +++ b/lib/agg_table.h @@ -58,7 +58,7 @@ static inline struct agg_node *agg_lock_node(struct agg_node *node) static inline void agg_unlock_node(struct agg_node *node) { - return route_unlock_node(agg_node_to_rnode(node)); + route_unlock_node(agg_node_to_rnode(node)); } static inline void agg_set_table_info(struct agg_table *atable, void *data) diff --git a/lib/command_py.c b/lib/command_py.c index 58b7982665..ca0c8be79d 100644 --- a/lib/command_py.c +++ b/lib/command_py.c @@ -92,7 +92,7 @@ static PyMemberDef members_graph_node[] = { member(deprecated, T_BOOL), member(hidden, T_BOOL), member(text, T_STRING), member(desc, T_STRING), member(min, T_LONGLONG), member(max, T_LONGLONG), - member(varname, T_STRING), {}, + member(varname, T_STRING), {0}, }; #undef member @@ -137,7 +137,7 @@ static PyObject *graph_node_join(PyObject *self, PyObject *args) static PyMethodDef methods_graph_node[] = { {"next", graph_node_next, METH_NOARGS, "outbound graph edge list"}, {"join", graph_node_join, METH_NOARGS, "outbound join node"}, - {}}; + {0}}; static void graph_node_wrap_free(void *arg) { @@ -228,7 +228,7 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph, } static PyMemberDef members_graph[] = { member(definition, T_STRING), - {}, + {0}, }; #undef member @@ -242,7 +242,7 @@ static PyObject *graph_first(PyObject *self, PyObject *args) static PyMethodDef methods_graph[] = { {"first", graph_first, METH_NOARGS, "first graph node"}, - {}}; + {0}}; static PyObject *graph_parse(PyTypeObject *type, PyObject *args, PyObject *kwds); diff --git a/lib/distribute.c b/lib/distribute.c index 0f1d666aeb..9697916332 100644 --- a/lib/distribute.c +++ b/lib/distribute.c @@ -299,16 +299,15 @@ DEFUN (ipv6_distribute_list, ifname = argv[argc - 1]->arg; /* Get interface name corresponding distribute list. */ - distfn(ifname, type, argv[1 + prefix]->arg); + distfn(ifname, type, argv[2 + prefix]->arg); return CMD_SUCCESS; } DEFUN (no_distribute_list, no_distribute_list_cmd, - "no [ipv6] distribute-list [prefix] WORD <in|out> [WORD]", + "no distribute-list [prefix] WORD <in|out> [WORD]", NO_STR - "IPv6\n" "Filter networks in routing updates\n" "Specify a prefix\n" "Access-list name\n" @@ -316,20 +315,53 @@ DEFUN (no_distribute_list, "Filter outgoing routing updates\n" "Interface name\n") { - int ipv6 = strmatch(argv[1]->text, "ipv6"); - int prefix = (argv[2 + ipv6]->type == WORD_TKN) ? 1 : 0; + int prefix = (argv[2]->type == WORD_TKN) ? 1 : 0; - int idx_alname = 2 + ipv6 + prefix; + int idx_alname = 2 + prefix; int idx_disttype = idx_alname + 1; + enum distribute_type type = + argv[idx_disttype]->arg[0] == 'i' ? + DISTRIBUTE_V4_IN : DISTRIBUTE_V4_OUT; - /* Check of distribute list type. */ - enum distribute_type distin = - (ipv6) ? DISTRIBUTE_V6_IN : DISTRIBUTE_V4_IN; - enum distribute_type distout = - (ipv6) ? DISTRIBUTE_V6_OUT : DISTRIBUTE_V4_OUT; + /* Set appropriate function call */ + int (*distfn)(const char *, enum distribute_type, + const char *) = + prefix ? &distribute_list_prefix_unset : &distribute_list_unset; + + /* if interface is present, get name */ + const char *ifname = NULL; + if (argv[argc - 1]->type == VARIABLE_TKN) + ifname = argv[argc - 1]->arg; + /* Get interface name corresponding distribute list. */ + int ret = distfn(ifname, type, argv[2 + prefix]->arg); + + if (!ret) { + vty_out(vty, "distribute list doesn't exist\n"); + return CMD_WARNING_CONFIG_FAILED; + } + return CMD_SUCCESS; +} + +DEFUN (no_ipv6_distribute_list, + no_ipv6_distribute_list_cmd, + "no ipv6 distribute-list [prefix] WORD <in|out> [WORD]", + NO_STR + "IPv6\n" + "Filter networks in routing updates\n" + "Specify a prefix\n" + "Access-list name\n" + "Filter incoming routing updates\n" + "Filter outgoing routing updates\n" + "Interface name\n") +{ + int prefix = (argv[3]->type == WORD_TKN) ? 1 : 0; + + int idx_alname = 3 + prefix; + int idx_disttype = idx_alname + 1; enum distribute_type type = - argv[idx_disttype]->arg[0] == 'i' ? distin : distout; + argv[idx_disttype]->arg[0] == 'i' ? + DISTRIBUTE_V6_IN : DISTRIBUTE_V6_OUT; /* Set appropriate function call */ int (*distfn)(const char *, enum distribute_type, const char *) = @@ -337,10 +369,11 @@ DEFUN (no_distribute_list, /* if interface is present, get name */ const char *ifname = NULL; + if (argv[argc - 1]->type == VARIABLE_TKN) ifname = argv[argc - 1]->arg; /* Get interface name corresponding distribute list. */ - int ret = distfn(ifname, type, argv[2 + prefix]->arg); + int ret = distfn(ifname, type, argv[3 + prefix]->arg); if (!ret) { vty_out(vty, "distribute list doesn't exist\n"); @@ -535,6 +568,7 @@ void distribute_list_init(int node) /* install v6 */ if (node == RIPNG_NODE) { install_element(RIPNG_NODE, &ipv6_distribute_list_cmd); + install_element(RIPNG_NODE, &no_ipv6_distribute_list_cmd); } /* TODO: install v4 syntax command for v6 only protocols. */ @@ -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/openbsd-tree.h b/lib/openbsd-tree.h index 859f751678..1383ef6de0 100644 --- a/lib/openbsd-tree.h +++ b/lib/openbsd-tree.h @@ -475,25 +475,25 @@ int _rb_check(const struct rb_type *, void *, unsigned long); __attribute__((__unused__)) static inline void _name##_RB_SET_LEFT( \ struct _type *elm, struct _type *left) \ { \ - return _rb_set_left(_name##_RB_TYPE, elm, left); \ + _rb_set_left(_name##_RB_TYPE, elm, left); \ } \ \ __attribute__((__unused__)) static inline void _name##_RB_SET_RIGHT( \ struct _type *elm, struct _type *right) \ { \ - return _rb_set_right(_name##_RB_TYPE, elm, right); \ + _rb_set_right(_name##_RB_TYPE, elm, right); \ } \ \ __attribute__((__unused__)) static inline void _name##_RB_SET_PARENT( \ struct _type *elm, struct _type *parent) \ { \ - return _rb_set_parent(_name##_RB_TYPE, elm, parent); \ + _rb_set_parent(_name##_RB_TYPE, elm, parent); \ } \ \ __attribute__((__unused__)) static inline void _name##_RB_POISON( \ struct _type *elm, unsigned long poison) \ { \ - return _rb_poison(_name##_RB_TYPE, elm, poison); \ + _rb_poison(_name##_RB_TYPE, elm, poison); \ } \ \ __attribute__((__unused__)) static inline int _name##_RB_CHECK( \ diff --git a/lib/thread.h b/lib/thread.h index 70090cf784..4de9a65562 100644 --- a/lib/thread.h +++ b/lib/thread.h @@ -125,7 +125,7 @@ struct cpu_thread_history { _Atomic unsigned long total, max; } real; struct time_stats cpu; - _Atomic uint8_t types; + _Atomic uint32_t types; const char *funcname; }; @@ -2458,8 +2458,6 @@ static void vty_read_file(struct nb_config *config, FILE *confp) */ if (config == NULL && vty->candidate_config && frr_get_cli_mode() == FRR_CLI_TRANSACTIONAL) { - int ret; - ret = nb_candidate_commit(vty->candidate_config, NB_CLIENT_CLI, true, "Read configuration file", NULL); 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/ospfd/ospf_packet.c b/ospfd/ospf_packet.c index 3bb3b79a6a..27a94f2310 100644 --- a/ospfd/ospf_packet.c +++ b/ospfd/ospf_packet.c @@ -655,7 +655,7 @@ static int ospf_write(struct thread *thread) int pkt_count = 0; #ifdef GNU_LINUX - unsigned char cmsgbuf[64] = {}; + unsigned char cmsgbuf[64] = {0}; struct cmsghdr *cm = (struct cmsghdr *)cmsgbuf; struct in_pktinfo *pi; #endif diff --git a/pbrd/pbr_nht.c b/pbrd/pbr_nht.c index 6103bd7db5..db958e833f 100644 --- a/pbrd/pbr_nht.c +++ b/pbrd/pbr_nht.c @@ -232,9 +232,9 @@ void pbr_nhgroup_add_nexthop_cb(const struct nexthop_group_cmd *nhgc, const struct nexthop *nhop) { char debugstr[256]; - struct pbr_nexthop_group_cache pnhgc_find = {}; + struct pbr_nexthop_group_cache pnhgc_find = {0}; struct pbr_nexthop_group_cache *pnhgc; - struct pbr_nexthop_cache pnhc_find = {}; + struct pbr_nexthop_cache pnhc_find = {0}; struct pbr_nexthop_cache *pnhc; if (!pbr_nht_get_next_tableid(true)) { @@ -270,9 +270,9 @@ void pbr_nhgroup_del_nexthop_cb(const struct nexthop_group_cmd *nhgc, const struct nexthop *nhop) { char debugstr[256]; - struct pbr_nexthop_group_cache pnhgc_find = {}; + struct pbr_nexthop_group_cache pnhgc_find = {0}; struct pbr_nexthop_group_cache *pnhgc; - struct pbr_nexthop_cache pnhc_find = {}; + struct pbr_nexthop_cache pnhc_find = {0}; struct pbr_nexthop_cache *pnhc; enum nexthop_types_t nh_afi = nhop->type; diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 129d569c87..8f6a9ece53 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -1012,10 +1012,21 @@ int pim_ifchannel_local_membership_add(struct interface *ifp, /* PIM enabled on interface? */ pim_ifp = ifp->info; - if (!pim_ifp) + if (!pim_ifp) { + if (PIM_DEBUG_EVENTS) + zlog_debug("%s:%s Expected pim interface setup for %s", + __PRETTY_FUNCTION__, + pim_str_sg_dump(sg), ifp->name); return 0; - if (!PIM_IF_TEST_PIM(pim_ifp->options)) + } + + if (!PIM_IF_TEST_PIM(pim_ifp->options)) { + if (PIM_DEBUG_EVENTS) + zlog_debug("%s:%s PIM is not configured on this interface %s", + __PRETTY_FUNCTION__, + pim_str_sg_dump(sg), ifp->name); return 0; + } pim = pim_ifp->pim; @@ -1033,6 +1044,10 @@ int pim_ifchannel_local_membership_add(struct interface *ifp, ch = pim_ifchannel_add(ifp, sg, 0, PIM_UPSTREAM_FLAG_MASK_SRC_IGMP); if (!ch) { + if (PIM_DEBUG_EVENTS) + zlog_debug("%s:%s Unable to add ifchannel", + __PRETTY_FUNCTION__, + pim_str_sg_dump(sg)); return 0; } diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 3dfc36a0c2..10ea17cf1f 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -1048,6 +1048,10 @@ void igmp_source_forward_start(struct pim_instance *pim, __PRETTY_FUNCTION__, pim_str_sg_dump(&sg), group->group_igmp_sock->interface->name); + + pim_channel_del_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP); return; } /* @@ -1059,6 +1063,10 @@ void igmp_source_forward_start(struct pim_instance *pim, if (PIM_DEBUG_MROUTE) zlog_warn("%s: Failure to add local membership for %s", __PRETTY_FUNCTION__, pim_str_sg_dump(&sg)); + + pim_channel_del_oif(source->source_channel_oil, + group->group_igmp_sock->interface, + PIM_OIF_FLAG_PROTO_IGMP); return; } diff --git a/python/clidef.py b/python/clidef.py index a140ce3d54..b6eb490b67 100644 --- a/python/clidef.py +++ b/python/clidef.py @@ -67,19 +67,19 @@ class PrefixBase(RenderHandler): deref = '&' class Prefix4Handler(PrefixBase): argtype = 'const struct prefix_ipv4 *' - decl = Template('struct prefix_ipv4 $varname = { };') + decl = Template('struct prefix_ipv4 $varname = {0};') code = Template('_fail = !str2prefix_ipv4(argv[_i]->arg, &$varname);') class Prefix6Handler(PrefixBase): argtype = 'const struct prefix_ipv6 *' - decl = Template('struct prefix_ipv6 $varname = { };') + decl = Template('struct prefix_ipv6 $varname = {0};') code = Template('_fail = !str2prefix_ipv6(argv[_i]->arg, &$varname);') class PrefixEthHandler(PrefixBase): argtype = 'struct prefix_eth *' - decl = Template('struct prefix_eth $varname = { };') + decl = Template('struct prefix_eth $varname = {0};') code = Template('_fail = !str2prefix_eth(argv[_i]->arg, &$varname);') class PrefixGenHandler(PrefixBase): argtype = 'const struct prefix *' - decl = Template('struct prefix $varname = { };') + decl = Template('struct prefix $varname = {0};') code = Template('_fail = !str2prefix(argv[_i]->arg, &$varname);') # same for IP addresses. result is union sockunion. @@ -96,7 +96,7 @@ class IP4Handler(IPBase): code = Template('_fail = !inet_aton(argv[_i]->arg, &$varname);') class IP6Handler(IPBase): argtype = 'struct in6_addr' - decl = Template('struct in6_addr $varname = {};') + decl = Template('struct in6_addr $varname = {0};') code = Template('_fail = !inet_pton(AF_INET6, argv[_i]->arg, &$varname);') class IPGenHandler(IPBase): argtype = 'const union sockunion *' diff --git a/staticd/static_routes.c b/staticd/static_routes.c index cd0330ed82..a5f0f74b00 100644 --- a/staticd/static_routes.c +++ b/staticd/static_routes.c @@ -497,6 +497,66 @@ void static_cleanup_vrf_ids(struct static_vrf *disable_svrf) } } +/* + * This function enables static routes when an interface it relies + * on in a different vrf is coming up. + * + * stable -> The stable we are looking at. + * ifp -> interface coming up + * afi -> the afi in question + * safi -> the safi in question + */ +static void static_fixup_intf_nh(struct route_table *stable, + struct interface *ifp, + afi_t afi, safi_t safi) +{ + struct route_node *rn; + struct static_route *si; + + for (rn = route_top(stable); rn; rn = route_next(rn)) { + for (si = rn->info; si; si = si->next) { + if (si->nh_vrf_id != ifp->vrf_id) + continue; + + if (si->ifindex != ifp->ifindex) + continue; + + static_install_route(rn, si, safi); + } + } +} + +/* + * This function enables static routes that rely on an interface in + * a different vrf when that interface comes up. + */ +void static_install_intf_nh(struct interface *ifp) +{ + struct route_table *stable; + struct vrf *vrf; + afi_t afi; + safi_t safi; + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + struct static_vrf *svrf = vrf->info; + + /* Not needed if same vrf since happens naturally */ + if (vrf->vrf_id == ifp->vrf_id) + continue; + + /* Install any static routes configured for this interface. */ + for (afi = AFI_IP; afi < AFI_MAX; afi++) { + for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) { + stable = svrf->stable[afi][safi]; + if (!stable) + continue; + + static_fixup_intf_nh(stable, ifp, afi, safi); + } + } + } +} + /* called from if_{add,delete}_update, i.e. when ifindex becomes [in]valid */ void static_ifindex_update(struct interface *ifp, bool up) { diff --git a/staticd/static_routes.h b/staticd/static_routes.h index 916ddcd7eb..6036bfe396 100644 --- a/staticd/static_routes.h +++ b/staticd/static_routes.h @@ -113,5 +113,7 @@ extern int static_delete_route(afi_t afi, safi_t safi, uint8_t type, extern void static_cleanup_vrf_ids(struct static_vrf *disable_svrf); +extern void static_install_intf_nh(struct interface *ifp); + extern void static_ifindex_update(struct interface *ifp, bool up); #endif diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index fd4201e562..1e23f597b0 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -122,11 +122,17 @@ static int interface_state_up(int command, struct zclient *zclient, ifp = zebra_interface_if_lookup(zclient->ibuf); - if (ifp && if_is_vrf(ifp)) { - struct static_vrf *svrf = static_vrf_lookup_by_id(vrf_id); + if (ifp) { + if (if_is_vrf(ifp)) { + struct static_vrf *svrf = + static_vrf_lookup_by_id(vrf_id); - static_fixup_vrf_ids(svrf); - static_config_install_delayed_routes(svrf); + static_fixup_vrf_ids(svrf); + static_config_install_delayed_routes(svrf); + } + + /* Install any static reliant on this interface coming up */ + static_install_intf_nh(ifp); } return 0; diff --git a/tools/checkpatch.pl b/tools/checkpatch.pl index 22354c1e88..547a4b2137 100755 --- a/tools/checkpatch.pl +++ b/tools/checkpatch.pl @@ -6367,6 +6367,13 @@ sub process { ERROR("NONSTANDARD_INTEGRAL_TYPES", "Please, no nonstandard integer types in new code.\n" . $herecurr) } + +# check for usage of non-32 bit atomics + if ($line =~ /_Atomic [u]?int(?!32)[0-9]+_t/) { + WARN("NON_32BIT_ATOMIC", + "Please, only use 32 bit atomics.\n" . $herecurr); + } + } # If we have no input at all, then there is nothing to report on diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index cd78551cb4..f57a4d9ddf 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -577,7 +577,7 @@ static int vtysh_execute_func(const char *line, int pager) && (cmd->daemon == vtysh_client[i].flag)) { for (vc = &vtysh_client[i]; vc; vc = vc->next) - if (vc->fd < 0) + if (vc->fd == VTYSH_WAS_ACTIVE) vtysh_reconnect(vc); } if (vtysh_client[i].fd < 0 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_dplane.c b/zebra/zebra_dplane.c index 61eba92c98..3e61418b64 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -135,8 +135,8 @@ struct zebra_dplane_provider { dplane_provider_fini_fp dp_fini; - _Atomic uint64_t dp_in_counter; - _Atomic uint64_t dp_error_counter; + _Atomic uint32_t dp_in_counter; + _Atomic uint32_t dp_error_counter; /* Embedded list linkage */ TAILQ_ENTRY(zebra_dplane_provider) dp_q_providers; @@ -171,10 +171,10 @@ static struct zebra_dplane_globals { /* Limit number of pending, unprocessed updates */ _Atomic uint32_t dg_max_queued_updates; - _Atomic uint64_t dg_routes_in; + _Atomic uint32_t dg_routes_in; _Atomic uint32_t dg_routes_queued; _Atomic uint32_t dg_routes_queued_max; - _Atomic uint64_t dg_route_errors; + _Atomic uint32_t dg_route_errors; /* Event-delivery context 'master' for the dplane */ struct thread_master *dg_master; 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 @@ -695,6 +695,33 @@ static struct log_ref ferr_zebra_err[] = { "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 <all$vni_all |" CMD_VNI_RANGE"$vni_val [mac M:A:C$mac_val | ip <A.B.C.D|X:X::X:X>]>", + 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, @@ -5101,6 +5752,70 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf, } /* + * Display Duplicate detected Neighbors for a VNI + * (VTY command handler). + */ +void zebra_vxlan_print_neigh_vni_dad(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, + bool use_json) +{ + zebra_vni_t *zvni; + uint32_t num_neigh; + struct neigh_walk_ctx wctx; + json_object *json = NULL; + + if (!is_evpn_enabled()) + return; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + num_neigh = hashcount(zvni->neigh_table); + if (!num_neigh) + return; + + num_neigh = num_dup_detected_neighs(zvni); + if (!num_neigh) + return; + + if (use_json) + json = json_object_new_object(); + + /* Since we have IPv6 addresses to deal with which can vary widely in + * size, we try to be a bit more elegant in display by first computing + * the maximum width. + */ + memset(&wctx, 0, sizeof(struct neigh_walk_ctx)); + wctx.zvni = zvni; + wctx.vty = vty; + wctx.addr_width = 15; + wctx.json = json; + hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx); + + if (!use_json) { + vty_out(vty, + "Number of ARPs (local and remote) known for this VNI: %u\n", + num_neigh); + vty_out(vty, "%*s %-6s %-8s %-17s %-21s\n", + -wctx.addr_width, "IP", "Type", + "State", "MAC", "Remote VTEP"); + } else + json_object_int_add(json, "numArpNd", num_neigh); + + hash_iterate(zvni->neigh_table, zvni_print_dad_neigh_hash, &wctx); + + if (use_json) { + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } +} + +/* * Display MACs for a VNI (VTY command handler). */ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, @@ -5159,7 +5874,7 @@ void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf, * Display MACs for all VNIs (VTY command handler). */ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, - bool use_json) + bool print_dup, bool use_json) { struct mac_walk_ctx wctx; json_object *json = NULL; @@ -5175,6 +5890,7 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf, memset(&wctx, 0, sizeof(struct mac_walk_ctx)); wctx.vty = vty; wctx.json = json; + wctx.print_dup = print_dup; hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx); if (use_json) { @@ -5253,6 +5969,393 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf, zvni_print_mac(mac, vty, json); } +/* Print Duplicate MACs per VNI */ +void zebra_vxlan_print_macs_vni_dad(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, bool use_json) +{ + zebra_vni_t *zvni; + struct mac_walk_ctx wctx; + uint32_t num_macs; + json_object *json = NULL; + json_object *json_mac = NULL; + + if (!is_evpn_enabled()) + return; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + num_macs = num_valid_macs(zvni); + if (!num_macs) + return; + + num_macs = num_dup_detected_macs(zvni); + if (!num_macs) + return; + + if (use_json) { + json = json_object_new_object(); + json_mac = json_object_new_object(); + } + + memset(&wctx, 0, sizeof(struct mac_walk_ctx)); + wctx.zvni = zvni; + wctx.vty = vty; + wctx.json = json_mac; + + if (!use_json) { + vty_out(vty, + "Number of MACs (local and remote) known for this VNI: %u\n", + num_macs); + vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type", + "Intf/Remote VTEP", "VLAN"); + } else + json_object_int_add(json, "numMacs", num_macs); + + hash_iterate(zvni->mac_table, zvni_print_dad_mac_hash, &wctx); + + if (use_json) { + json_object_object_add(json, "macs", json_mac); + vty_out(vty, "%s\n", json_object_to_json_string_ext( + json, JSON_C_TO_STRING_PRETTY)); + json_object_free(json); + } + +} + +void zebra_vxlan_clear_dup_detect_vni_mac(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, struct ethaddr *macaddr) +{ + zebra_vni_t *zvni; + zebra_mac_t *mac; + struct listnode *node = NULL; + zebra_neigh_t *nbr = NULL; + + if (!is_evpn_enabled()) + return; + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + mac = zvni_mac_lookup(zvni, macaddr); + if (!mac) { + vty_out(vty, "%% Requested MAC does not exist in VNI %u\n", + vni); + return; + } + + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + vty_out(vty, "%% Requested MAC is not duplicate detected\n"); + return; + } + + /* Remove all IPs as duplicate associcated with this MAC */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + /* For local neigh mark inactive so MACIP update is generated + * to BGP. This is a scenario where MAC update received + * and detected as duplicate which marked neigh as duplicate. + * Later local neigh update did not get a chance to relay + * to BGP. Similarly remote macip update, neigh needs to be + * installed locally. + */ + if (nbr->dad_count) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + ZEBRA_NEIGH_SET_INACTIVE(nbr); + else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) + zvni_neigh_install(zvni, nbr); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->dad_dup_detect_time = 0; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + mac->dad_count = 0; + mac->detect_start_time.tv_sec = 0; + mac->detect_start_time.tv_usec = 0; + mac->dad_dup_detect_time = 0; + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + + /* Local: Notify Peer VTEPs, Remote: Install the entry */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* Inform to BGP */ + if (zvni_mac_send_add_to_client(zvni->vni, + &mac->macaddr, + mac->flags, + mac->loc_seq)) + return; + + /* Process all neighbors associated with this MAC. */ + zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zvni_process_neigh_on_remote_mac_add(zvni, mac); + + /* Install the entry. */ + zvni_mac_install(zvni, mac); + } + +} + +void zebra_vxlan_clear_dup_detect_vni_ip(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni, struct ipaddr *ip) +{ + zebra_vni_t *zvni; + zebra_neigh_t *nbr; + zebra_mac_t *mac; + char buf[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + + if (!is_evpn_enabled()) + return; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + nbr = zvni_neigh_lookup(zvni, ip); + if (!nbr) { + vty_out(vty, + "%% Requested host IP does not exist in VNI %u\n", + vni); + return; + } + + ipaddr2str(&nbr->ip, buf, sizeof(buf)); + + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) { + vty_out(vty, + "%% Requsted host IP %s is not duplicate detected\n", + buf); + return; + } + + mac = zvni_mac_lookup(zvni, &nbr->emac); + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) { + vty_out(vty, + "%% Requested IP's associated MAC %s is still in duplicate state\n", + prefix_mac2str(&nbr->emac, buf2, sizeof(buf2))); + return; + } + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: clear neigh %s in dup state, flags 0x%x seq %u", + __PRETTY_FUNCTION__, buf, nbr->flags, + nbr->loc_seq); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + + if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zvni_neigh_send_add_to_client(zvni->vni, ip, + &nbr->emac, + nbr->flags, nbr->loc_seq); + } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zvni_neigh_install(zvni, nbr); + } + +} + +static void zvni_clear_dup_mac_hash(struct hash_backet *backet, void *ctxt) +{ + struct mac_walk_ctx *wctx = ctxt; + zebra_mac_t *mac; + zebra_vni_t *zvni; + struct listnode *node = NULL; + zebra_neigh_t *nbr = NULL; + + mac = (zebra_mac_t *)backet->data; + if (!mac) + return; + + zvni = wctx->zvni; + + if (!CHECK_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE)) + return; + + UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + mac->dad_count = 0; + mac->detect_start_time.tv_sec = 0; + mac->detect_start_time.tv_usec = 0; + mac->dad_dup_detect_time = 0; + THREAD_OFF(mac->dad_mac_auto_recovery_timer); + + /* Remove all IPs as duplicate associcated with this MAC */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL) + && nbr->dad_count) + ZEBRA_NEIGH_SET_INACTIVE(nbr); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->dad_dup_detect_time = 0; + } + + /* Local: Notify Peer VTEPs, Remote: Install the entry */ + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* Inform to BGP */ + if (zvni_mac_send_add_to_client(zvni->vni, + &mac->macaddr, + mac->flags, mac->loc_seq)) + return; + + /* Process all neighbors associated with this MAC. */ + zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zvni_process_neigh_on_remote_mac_add(zvni, mac); + + /* Install the entry. */ + zvni_mac_install(zvni, mac); + } +} + +static void zvni_clear_dup_neigh_hash(struct hash_backet *backet, void *ctxt) +{ + struct neigh_walk_ctx *wctx = ctxt; + zebra_neigh_t *nbr; + zebra_vni_t *zvni; + char buf[INET6_ADDRSTRLEN]; + + nbr = (zebra_neigh_t *)backet->data; + if (!nbr) + return; + + zvni = wctx->zvni; + + if (!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE)) + return; + + if (IS_ZEBRA_DEBUG_VXLAN) { + ipaddr2str(&nbr->ip, buf, sizeof(buf)); + zlog_debug( + "%s: clear neigh %s dup state, flags 0x%x seq %u", + __PRETTY_FUNCTION__, buf, + nbr->flags, nbr->loc_seq); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + THREAD_OFF(nbr->dad_ip_auto_recovery_timer); + + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, + &nbr->emac, + nbr->flags, nbr->loc_seq); + } else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zvni_neigh_install(zvni, nbr); + } +} + +static void zvni_clear_dup_detect_hash_vni_all(struct hash_backet *backet, + void **args) +{ + struct vty *vty; + zebra_vni_t *zvni; + struct zebra_vrf *zvrf; + struct mac_walk_ctx m_wctx; + struct neigh_walk_ctx n_wctx; + + zvni = (zebra_vni_t *)backet->data; + if (!zvni) + return; + + vty = (struct vty *)args[0]; + zvrf = (struct zebra_vrf *)args[1]; + + if (hashcount(zvni->neigh_table)) { + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.vty = vty; + n_wctx.zvni = zvni; + n_wctx.zvrf = zvrf; + hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, + &n_wctx); + } + + if (num_valid_macs(zvni)) { + memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); + m_wctx.zvni = zvni; + m_wctx.vty = vty; + m_wctx.zvrf = zvrf; + hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + } + +} + +void zebra_vxlan_clear_dup_detect_vni_all(struct vty *vty, + struct zebra_vrf *zvrf) +{ + void *args[2]; + + if (!is_evpn_enabled()) + return; + + args[0] = vty; + args[1] = zvrf; + + hash_iterate(zvrf->vni_table, + (void (*)(struct hash_backet *, void *)) + zvni_clear_dup_detect_hash_vni_all, args); + +} + +void zebra_vxlan_clear_dup_detect_vni(struct vty *vty, + struct zebra_vrf *zvrf, + vni_t vni) +{ + zebra_vni_t *zvni; + struct mac_walk_ctx m_wctx; + struct neigh_walk_ctx n_wctx; + + if (!is_evpn_enabled()) + return; + + zvni = zvni_lookup(vni); + if (!zvni) { + vty_out(vty, "%% VNI %u does not exist\n", vni); + return; + } + + if (hashcount(zvni->neigh_table)) { + memset(&n_wctx, 0, sizeof(struct neigh_walk_ctx)); + n_wctx.vty = vty; + n_wctx.zvni = zvni; + n_wctx.zvrf = zvrf; + hash_iterate(zvni->neigh_table, zvni_clear_dup_neigh_hash, + &n_wctx); + } + + if (num_valid_macs(zvni)) { + memset(&m_wctx, 0, sizeof(struct mac_walk_ctx)); + m_wctx.zvni = zvni; + m_wctx.vty = vty; + m_wctx.zvrf = zvrf; + hash_iterate(zvni->mac_table, zvni_clear_dup_mac_hash, &m_wctx); + } + +} + /* * Display MACs for a VNI from specific VTEP (VTY command handler). */ @@ -5373,11 +6476,34 @@ void zebra_vxlan_print_evpn(struct vty *vty, bool uj) json_object_int_add(json, "numVnis", num_vnis); json_object_int_add(json, "numL2Vnis", num_l2vnis); json_object_int_add(json, "numL3Vnis", num_l3vnis); + if (zvrf->dup_addr_detect) + json_object_boolean_true_add(json, + "isDuplicateAddrDetection"); + else + json_object_boolean_false_add(json, + "isDuplicateAddrDetection"); + json_object_int_add(json, "maxMoves", zvrf->dad_max_moves); + json_object_int_add(json, "detectionTime", zvrf->dad_time); + json_object_int_add(json, "detectionFreezeTime", + zvrf->dad_freeze_time); + } else { vty_out(vty, "L2 VNIs: %u\n", num_l2vnis); vty_out(vty, "L3 VNIs: %u\n", num_l3vnis); vty_out(vty, "Advertise gateway mac-ip: %s\n", zvrf->advertise_gw_macip ? "Yes" : "No"); + vty_out(vty, "Duplicate address detection: %s\n", + zvrf->dup_addr_detect ? "Enable" : "Disable"); + vty_out(vty, " Detection max-moves %u, time %d\n", + zvrf->dad_max_moves, zvrf->dad_time); + if (zvrf->dad_freeze) { + if (zvrf->dad_freeze_time) + vty_out(vty, " Detection freeze %u\n", + zvrf->dad_freeze_time); + else + vty_out(vty, " Detection freeze %s\n", + "permanent"); + } } if (uj) { @@ -5426,6 +6552,48 @@ void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf, } } +void zebra_vxlan_dup_addr_detection(ZAPI_HANDLER_ARGS) +{ + struct stream *s; + int time = 0; + uint32_t max_moves = 0; + uint32_t freeze_time = 0; + bool dup_addr_detect = false; + bool freeze = false; + + s = msg; + STREAM_GETL(s, dup_addr_detect); + STREAM_GETL(s, time); + STREAM_GETL(s, max_moves); + STREAM_GETL(s, freeze); + STREAM_GETL(s, freeze_time); + + /* DAD previous state was enabled, and new state is disable, + * clear all duplicate detected addresses. + */ + if (zvrf->dup_addr_detect && !dup_addr_detect) + zebra_vxlan_clear_dup_detect_vni_all(NULL, zvrf); + + zvrf->dup_addr_detect = dup_addr_detect; + zvrf->dad_time = time; + zvrf->dad_max_moves = max_moves; + zvrf->dad_freeze = freeze; + zvrf->dad_freeze_time = freeze_time; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "%s: duplicate detect %s max_moves %u timeout %u freeze %s freeze_time %u", + __PRETTY_FUNCTION__, + zvrf->dup_addr_detect ? "enable" : "disable", + zvrf->dad_max_moves, + zvrf->dad_time, + zvrf->dad_freeze ? "enable" : "disable", + zvrf->dad_freeze_time); + +stream_failure: + return; +} + /* * Handle neighbor delete notification from the kernel (on a VLAN device * / L3 interface). This may result in either the neighbor getting deleted @@ -5871,10 +7039,12 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, { zebra_vni_t *zvni; zebra_mac_t *mac; + struct zebra_vrf *zvrf; char buf[ETHER_ADDR_STRLEN]; bool mac_sticky = false; bool inform_client = false; bool upd_neigh = false; + struct in_addr vtep_ip = {.s_addr = 0}; /* We are interested in MACs only on ports or (port, VLAN) that * map to a VNI. @@ -5897,6 +7067,10 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, return -1; } + zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id); + if (!zvrf) + return -1; + /* Check if we need to create or update or it is a NO-OP. */ mac = zvni_mac_lookup(zvni, macaddr); if (!mac) { @@ -5970,6 +7144,7 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE) || CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) { + bool do_dad = false; /* * MAC has either moved or was "internally" created due @@ -5989,9 +7164,14 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, } /* If an actual move, compute MAC's seq number */ - if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { mac->loc_seq = MAX(mac->rem_seq + 1, mac->loc_seq); + vtep_ip = mac->fwd_info.r_vtep_ip; + /* Trigger DAD for remote MAC */ + do_dad = true; + } + UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE); UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO); SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL); @@ -6008,6 +7188,11 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp, */ inform_client = true; upd_neigh = true; + + zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, vtep_ip, + do_dad, + &inform_client, + true); } } @@ -7334,3 +8519,125 @@ ifindex_t get_l3vni_svi_ifindex(vrf_id_t vrf_id) return zl3vni->svi_if->ifindex; } + +static int zebra_vxlan_dad_ip_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_neigh_t *nbr = NULL; + zebra_vni_t *zvni = NULL; + char buf1[INET6_ADDRSTRLEN]; + char buf2[ETHER_ADDR_STRLEN]; + + nbr = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + zvrf = vrf_info_lookup(nbr->zvni->vrf_id); + if (!zvrf) + return 0; + + zvni = zvni_lookup(nbr->zvni->vni); + if (!zvni) + return 0; + + nbr = zvni_neigh_lookup(zvni, &nbr->ip); + if (!nbr) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: duplicate addr MAC %s IP %s flags 0x%x learn count %u vni %u auto recovery expired", + __PRETTY_FUNCTION__, + prefix_mac2str(&nbr->emac, buf1, sizeof(buf1)), + ipaddr2str(&nbr->ip, buf2, sizeof(buf2)), + nbr->flags, + nbr->dad_count, zvni->vni); + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->detect_start_time.tv_usec = 0; + nbr->dad_dup_detect_time = 0; + nbr->dad_ip_auto_recovery_timer = NULL; + + /* Send to BGP */ + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) { + zvni_neigh_send_add_to_client(zvni->vni, &nbr->ip, &nbr->emac, + nbr->flags, nbr->loc_seq); + } else if (!!CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) { + zvni_neigh_install(zvni, nbr); + } + + return 0; +} + +static int zebra_vxlan_dad_mac_auto_recovery_exp(struct thread *t) +{ + struct zebra_vrf *zvrf = NULL; + zebra_mac_t *mac = NULL; + zebra_vni_t *zvni = NULL; + struct listnode *node = NULL; + zebra_neigh_t *nbr = NULL; + char buf[ETHER_ADDR_STRLEN]; + + mac = THREAD_ARG(t); + + /* since this is asynchronous we need sanity checks*/ + zvrf = vrf_info_lookup(mac->zvni->vrf_id); + if (!zvrf) + return 0; + + zvni = zvni_lookup(mac->zvni->vni); + if (!zvni) + return 0; + + mac = zvni_mac_lookup(zvni, &mac->macaddr); + if (!mac) + return 0; + + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug("%s: duplicate addr mac %s flags 0x%x learn count %u host count %u auto recovery expired", + __PRETTY_FUNCTION__, + prefix_mac2str(&mac->macaddr, buf, sizeof(buf)), + mac->flags, + mac->dad_count, + listcount(mac->neigh_list)); + + /* Remove all IPs as duplicate associcated with this MAC */ + for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, nbr)) { + if (nbr->dad_count) { + if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_LOCAL)) + ZEBRA_NEIGH_SET_INACTIVE(nbr); + else if (CHECK_FLAG(nbr->flags, ZEBRA_NEIGH_REMOTE)) + zvni_neigh_install(zvni, nbr); + } + + UNSET_FLAG(nbr->flags, ZEBRA_NEIGH_DUPLICATE); + nbr->dad_count = 0; + nbr->detect_start_time.tv_sec = 0; + nbr->dad_dup_detect_time = 0; + } + + UNSET_FLAG(mac->flags, ZEBRA_MAC_DUPLICATE); + mac->dad_count = 0; + mac->detect_start_time.tv_sec = 0; + mac->detect_start_time.tv_usec = 0; + mac->dad_dup_detect_time = 0; + mac->dad_mac_auto_recovery_timer = NULL; + + if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)) { + /* Inform to BGP */ + if (zvni_mac_send_add_to_client(zvni->vni, &mac->macaddr, + mac->flags, mac->loc_seq)) + return -1; + + /* Process all neighbors associated with this MAC. */ + zvni_process_neigh_on_local_mac_change(zvni, mac, 0); + + } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) { + zvni_process_neigh_on_remote_mac_add(zvni, mac); + + /* Install the entry. */ + zvni_mac_install(zvni, mac); + } + + return 0; +} 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; }; /* diff --git a/zebra/zserv.c b/zebra/zserv.c index 3c3bf4077b..b40e9e2af5 100644 --- a/zebra/zserv.c +++ b/zebra/zserv.c @@ -875,7 +875,7 @@ static void zebra_show_client_detail(struct vty *vty, struct zserv *client) char cbuf[ZEBRA_TIME_BUF], rbuf[ZEBRA_TIME_BUF]; char wbuf[ZEBRA_TIME_BUF], nhbuf[ZEBRA_TIME_BUF], mbuf[ZEBRA_TIME_BUF]; time_t connect_time, last_read_time, last_write_time; - uint16_t last_read_cmd, last_write_cmd; + uint32_t last_read_cmd, last_write_cmd; vty_out(vty, "Client: %s", zebra_route_string(client->proto)); if (client->instance) diff --git a/zebra/zserv.h b/zebra/zserv.h index f21ea17fe8..f7967f54f0 100644 --- a/zebra/zserv.h +++ b/zebra/zserv.h @@ -157,9 +157,9 @@ struct zserv { /* monotime of last message sent */ _Atomic uint32_t last_write_time; /* command code of last message read */ - _Atomic uint16_t last_read_cmd; + _Atomic uint32_t last_read_cmd; /* command code of last message written */ - _Atomic uint16_t last_write_cmd; + _Atomic uint32_t last_write_cmd; }; #define ZAPI_HANDLER_ARGS \ |
