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)
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;
#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));
/* 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 */
/* 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 */
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",
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");
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 */
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..
*/
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)
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;
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;
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);
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;
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;
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);
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) {
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;
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;
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:
* 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.
#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;
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)
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));
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)
{
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);
}
}
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);
#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)
{
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
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);
}
}
}
/* 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;
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;
/* 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;
/* local esi hash table */
struct hash *esihash;
+ /* Count of peers in established state */
+ uint32_t established_peers;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp)
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)
/* For benefit of rfapi */
extern struct peer *peer_new(struct bgp *bgp);
-
#endif /* _QUAGGA_BGPD_H */
])
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"])
--- /dev/null
+.. _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.
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
---------
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);
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
&& !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 {
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);
* 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 */
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,
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
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");
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) {
[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,
},
ipv6_supported = true;
}
- struct nlpids reduced = {};
+ struct nlpids reduced = {0};
if (ipv4_supported && ipv6_supported) {
reduced.count = 2;
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;
}
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)
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
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)
{
}
static PyMemberDef members_graph[] = {
member(definition, T_STRING),
- {},
+ {0},
};
#undef member
static PyMethodDef methods_graph[] = {
{"first", graph_first, METH_NOARGS, "first graph node"},
- {}};
+ {0}};
static PyObject *graph_parse(PyTypeObject *type, PyObject *args,
PyObject *kwds);
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"
"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 *) =
/* 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");
/* 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. */
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),
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 */
__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( \
lib/lua.c \
# end
+nodist_lib_libfrr_la_SOURCES = \
+ yang/frr-interface.yang.c \
+ yang/frr-route-types.yang.c \
+ yang/frr-module-translator.yang.c \
+ # end
+
vtysh_scan += \
$(top_srcdir)/lib/distribute.c \
$(top_srcdir)/lib/filter.c \
_Atomic unsigned long total, max;
} real;
struct time_stats cpu;
- _Atomic uint8_t types;
+ _Atomic uint32_t types;
const char *funcname;
};
*/
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);
/* libyang container. */
struct ly_ctx *ly_native_ctx;
+static struct yang_module_embed *embeds, **embedupd = &embeds;
+
+void yang_module_embed(struct yang_module_embed *embed)
+{
+ embed->next = NULL;
+ *embedupd = embed;
+ embedupd = &embed->next;
+}
+
+static const char *yang_module_imp_clb(const char *mod_name,
+ const char *mod_rev,
+ const char *submod_name,
+ const char *submod_rev,
+ void *user_data,
+ LYS_INFORMAT *format,
+ void (**free_module_data)
+ (void *, void*))
+{
+ struct yang_module_embed *e;
+
+ if (submod_name || submod_rev)
+ return NULL;
+
+ for (e = embeds; e; e = e->next) {
+ if (strcmp(e->mod_name, mod_name))
+ continue;
+ if (mod_rev && strcmp(e->mod_rev, mod_rev))
+ continue;
+
+ *format = e->format;
+ return e->data;
+ }
+
+ flog_warn(EC_LIB_YANG_MODULE_LOAD,
+ "YANG model \"%s@%s\" not embedded, trying external file",
+ mod_name, mod_rev ? mod_rev : "*");
+ return NULL;
+}
+
/* Generate the yang_modules tree. */
static inline int yang_module_compare(const struct yang_module *a,
const struct yang_module *b)
flog_err(EC_LIB_LIBYANG, "%s: ly_ctx_new() failed", __func__);
exit(1);
}
+ ly_ctx_set_module_imp_clb(ly_native_ctx, yang_module_imp_clb, NULL);
ly_ctx_set_searchdir(ly_native_ctx, YANG_MODELS_PATH);
ly_ctx_set_priv_dup_clb(ly_native_ctx, ly_dup_cb);
/* Maximum string length of an YANG value. */
#define YANG_VALUE_MAXLEN 1024
+struct yang_module_embed {
+ struct yang_module_embed *next;
+ const char *mod_name, *mod_rev;
+ const char *data;
+ LYS_INFORMAT format;
+};
+
struct yang_module {
RB_ENTRY(yang_module) entry;
const char *name;
*/
extern struct yang_module *yang_module_find(const char *module_name);
+/*
+ * Register a YANG module embedded in the binary file. Should be called
+ * from a constructor function.
+ *
+ * embed
+ * YANG module embedding structure to register. (static global provided
+ * by caller.)
+ */
+extern void yang_module_embed(struct yang_module_embed *embed);
+
/*
* Iterate over all libyang schema nodes from the given YANG module.
*
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,
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
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)) {
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;
/* 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;
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;
}
__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;
}
/*
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;
}
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.
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 *'
ripd_ripd_SOURCES = \
ripd/rip_main.c \
# end
+nodist_ripd_ripd_SOURCES = \
+ yang/frr-ripd.yang.c \
+ # end
ripd_ripd_snmp_la_SOURCES = ripd/rip_snmp.c
ripd_ripd_snmp_la_CFLAGS = $(WERROR) $(SNMP_CFLAGS) -std=gnu99
}
}
+/*
+ * 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)
{
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
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;
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
&& (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
--- /dev/null
+*.yang.c
+*.yin
--- /dev/null
+#!/usr/bin/python3
+#
+# YANG module to C wrapper
+# written 2018 by David Lamparter, placed in Public Domain.
+
+import sys, string, re
+
+inname = sys.argv[1]
+outname = sys.argv[2]
+
+# these are regexes to avoid a compile-time/host dependency on yang-tools
+# or python-yang. Cross-compiling FRR is already somewhat involved, no need
+# to make it even harder.
+
+re_name = re.compile(r'\bmodule\s+([^\s]+)\s+\{')
+re_rev = re.compile(r'\brevision\s+([\d-]+)\s+\{')
+
+
+template = '''/* autogenerated by embedmodel.py. DO NOT EDIT */
+
+#include <zebra.h>
+#include "yang.h"
+
+static const char model[] =
+\t"%s";
+
+static struct yang_module_embed embed = {
+\t.mod_name = "%s",
+\t.mod_rev = "%s",
+\t.data = model,
+\t.format = %s,
+};
+
+static void embed_register(void) __attribute__((_CONSTRUCTOR(2000)));
+static void embed_register(void)
+{
+\tyang_module_embed(&embed);
+}
+'''
+
+passchars = set(string.printable) - set('\\\'"%\r\n\t\x0b\x0c')
+def escapech(char):
+ if char in passchars:
+ return char
+ if char == '\n':
+ return '\\n'
+ if char == '\t':
+ return '\\t'
+ if char in '"\\\'':
+ return '\\' + char
+ return '\\x%02x' % (ord(char))
+def escape(line):
+ return ''.join([escapech(i) for i in line])
+
+with open(inname, 'r') as fd:
+ data = fd.read()
+
+# XML support isn't actively used currently, but it's here in case the need
+# arises. It does avoid the regex'ing.
+if '<?xml' in data:
+ from xml.etree import ElementTree
+ xml = ElementTree.fromstring(data)
+ name = xml.get('name')
+ rev = xml.find('{urn:ietf:params:xml:ns:yang:yin:1}revision').get('date')
+ fmt = 'LYS_YIN'
+else:
+ name = re_name.search(data).group(1)
+ rev = re_rev.search(data).group(1)
+ fmt = 'LYS_YANG'
+
+if name is None or rev is None:
+ raise ValueError('cannot determine YANG module name and revision')
+
+lines = [escape(row) for row in data.split('\n')]
+text = '\\n"\n\t"'.join(lines)
+
+with open(outname, 'w') as fd:
+ fd.write(template % (text, escape(name), escape(rev), fmt))
+SUFFIXES += .yang .yang.c .yin .yin.c
+EXTRA_DIST += yang/embedmodel.py
+
+.yang.yang.c:
+ $(AM_V_GEN)$(PYTHON) $(top_srcdir)/yang/embedmodel.py $^ $@
+.yin.yin.c:
+ $(AM_V_GEN)$(PYTHON) $(top_srcdir)/yang/embedmodel.py $^ $@
+
+# use .yang.c files like this:
+#
+# ripd_ripd_SOURCES = \
+# ...
+# nodist_ripd_ripd_SOURCES = \
+# yang/frr-ripd.yang.c \
+# # end
+#
+# Note that putting the .yang.c file into a static library.a will NOT work
+# because the entire file is "optimized out" since it does not contain any
+# global symbols :(. Just put it in the daemon. Dynamic libraries.so work
+# without problems, as seen in libfrr.
+
dist_yangmodels_DATA += yang/frr-module-translator.yang
dist_yangmodels_DATA += yang/frr-interface.yang
dist_yangmodels_DATA += yang/frr-route-types.yang
[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,
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;
/* 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;
.suggestion =
"Do not use v6 sourcedest routes, or upgrade your kernel.",
},
+ {
+ .code = EC_ZEBRA_DUP_MAC_DETECTED,
+ .title =
+ "EVPN MAC is detected duplicate",
+ .description =
+ "Zebra has hit duplicate address detection threshold which means host MAC is moving.",
+ .suggestion =
+ "Check network topology to detect duplicate host MAC for correctness.",
+ },
+ {
+ .code = EC_ZEBRA_DUP_IP_INHERIT_DETECTED,
+ .title =
+ "EVPN IP is detected duplicate by MAC",
+ .description =
+ "Zebra has hit duplicate address detection threshold which means MAC-IP pair is moving.",
+ .suggestion =
+ "Check network topology to detect duplicate host MAC for correctness.",
+ },
+ {
+ .code = EC_ZEBRA_DUP_IP_DETECTED,
+ .title =
+ "EVPN IP is detected duplicate",
+ .description =
+ "Zebra has hit duplicate address detection threshold which means host IP is moving.",
+ .suggestion =
+ "Check network topology to detect duplicate host IP for correctness.",
+ },
{
.code = END_FERR,
}
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);
/* 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.
*/
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;
}
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]",
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;
}
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)
{
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);
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,
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,
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;
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
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));
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");
+
+
}
}
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)) {
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++;
}
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",
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);
-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)
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));
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");
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))
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);
}
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.
*/
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();
} 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)
{
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)),
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);
}
}
}
{
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);
}
}
+ zvrf = vrf_info_lookup(zvni->vrf_id);
+ if (!zvrf)
+ return -1;
+
/* Check if the neighbor exists. */
n = zvni_neigh_lookup(zvni, ip);
if (!n) {
/* 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;
}
if (!mac_different) {
+ bool is_neigh_freezed = false;
+
/* Only the router flag has changed. */
if (is_router)
SET_FLAG(n->flags,
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);
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;
}
}
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.
*/
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.
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,
{
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);
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.
}
}
+ /* 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);
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. */
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.
*/
}
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;
+
}
}
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. */
* 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;
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,
}
/*
- * Display MACs for a VNI (VTY command handler).
+ * Display Duplicate detected Neighbors for a VNI
+ * (VTY command handler).
*/
-void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni, bool use_json)
+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_macs;
- struct mac_walk_ctx wctx;
+ uint32_t num_neigh;
+ struct neigh_walk_ctx wctx;
json_object *json = NULL;
- json_object *json_mac = NULL;
if (!is_evpn_enabled())
return;
+
zvni = zvni_lookup(vni);
if (!zvni) {
- if (use_json)
- vty_out(vty, "{}\n");
- else
- vty_out(vty, "%% VNI %u does not exist\n", vni);
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- num_macs = num_valid_macs(zvni);
- if (!num_macs)
+
+ num_neigh = hashcount(zvni->neigh_table);
+ if (!num_neigh)
return;
- if (use_json) {
+ num_neigh = num_dup_detected_neighs(zvni);
+ if (!num_neigh)
+ 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;
+ /* 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,
+ vni_t vni, bool use_json)
+{
+ zebra_vni_t *zvni;
+ uint32_t num_macs;
+ struct mac_walk_ctx wctx;
+ json_object *json = NULL;
+ json_object *json_mac = NULL;
+
+ if (!is_evpn_enabled())
+ return;
+ zvni = zvni_lookup(vni);
+ if (!zvni) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+ num_macs = num_valid_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;
* 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;
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) {
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).
*/
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) {
}
}
+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
{
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.
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) {
} 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
}
/* 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);
*/
inform_client = true;
upd_neigh = true;
+
+ zebra_vxlan_dup_addr_detect_for_mac(zvrf, mac, vtep_ip,
+ do_dad,
+ &inform_client,
+ true);
}
}
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;
+}
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);
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,
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,
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,
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 */
#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 {
/* 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;
};
/*
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 {
/* 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;
/* 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;
};
/*
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)
/* 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 \