/* Update and advertise the type-3 route (only one) followed by the
* locally learnt type-2 routes (MACIP) - for this VNI.
+ *
+ * RT-3 only if doing head-end replication
*/
- build_evpn_type3_prefix(&p, vpn->originator_ip);
- ret = update_evpn_route(bgp, vpn, &p, 0, 0);
- if (ret)
- return ret;
+ if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL) {
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ ret = update_evpn_route(bgp, vpn, &p, 0, 0);
+ if (ret)
+ return ret;
+ }
return update_all_type2_routes(bgp, vpn);
}
/* Locate type-3 route for VNI in the per-VNI table and use its
* attributes to create and advertise the type-3 route for this VNI
* in the global table.
+ *
+ * RT-3 only if doing head-end replication
*/
- build_evpn_type3_prefix(&p, vpn->originator_ip);
- rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p);
- if (!rn) /* unexpected */
- return 0;
- for (pi = rn->info; pi; pi = pi->next)
- if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP
- && pi->sub_type == BGP_ROUTE_STATIC)
- break;
- if (!pi) /* unexpected */
- return 0;
- attr = pi->attr;
+ if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL) {
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ rn = bgp_node_lookup(vpn->route_table, (struct prefix *)&p);
+ if (!rn) /* unexpected */
+ return 0;
+ for (pi = rn->info; pi; pi = pi->next)
+ if (pi->peer == bgp->peer_self &&
+ pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_STATIC)
+ break;
+ if (!pi) /* unexpected */
+ return 0;
+ attr = pi->attr;
- global_rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
- (struct prefix *)&p, &vpn->prd);
- update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr, 1, &pi, 0,
- mac_mobility_seqnum(attr));
+ global_rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)&p, &vpn->prd);
+ update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr,
+ 1, &pi, 0, mac_mobility_seqnum(attr));
- /* Schedule for processing and unlock node. */
- bgp_process(bgp, global_rn, afi, safi);
- bgp_unlock_node(global_rn);
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, global_rn, afi, safi);
+ bgp_unlock_node(global_rn);
+ }
/* Now, walk this VNI's route table and use the route and its attribute
* to create and schedule route in global table.
delete_withdraw_vni_routes(bgp, vpn);
}
+/*
+ * Create RT-3 for a VNI and schedule for processing and advertisement.
+ * This is invoked upon flooding mode changing to head-end replication.
+ */
+static void create_advertise_type3(struct hash_backet *backet, void *data)
+{
+ struct bgpevpn *vpn = backet->data;
+ struct bgp *bgp = data;
+ struct prefix_evpn p;
+
+ if (!vpn || !is_vni_live(vpn))
+ return;
+
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ if (update_evpn_route(bgp, vpn, &p, 0, 0))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "Type3 route creation failure for VNI %u", vpn->vni);
+}
+
+/*
+ * Delete RT-3 for a VNI and schedule for processing and withdrawal.
+ * This is invoked upon flooding mode changing to drop BUM packets.
+ */
+static void delete_withdraw_type3(struct hash_backet *backet, void *data)
+{
+ struct bgpevpn *vpn = backet->data;
+ struct bgp *bgp = data;
+ struct prefix_evpn p;
+
+ if (!vpn || !is_vni_live(vpn))
+ return;
+
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ delete_evpn_route(bgp, vpn, &p);
+}
+
/*
* Process received EVPN type-2 route (advertise or withdraw).
*/
/* filter routes as nexthop database has changed */
bgp_filter_evpn_routes_upon_martian_nh_change(bgp);
- /* Create EVPN type-3 route and schedule for processing. */
- build_evpn_type3_prefix(&p, vpn->originator_ip);
- if (update_evpn_route(bgp, vpn, &p, 0, 0)) {
- flog_err(EC_BGP_EVPN_ROUTE_CREATE,
- "%u: Type3 route creation failure for VNI %u",
- bgp->vrf_id, vni);
- return -1;
+ /*
+ * Create EVPN type-3 route and schedule for processing.
+ *
+ * RT-3 only if doing head-end replication
+ */
+ if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL) {
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ if (update_evpn_route(bgp, vpn, &p, 0, 0)) {
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: Type3 route creation failure for VNI %u",
+ bgp->vrf_id, vni);
+ return -1;
+ }
}
/* If we have learnt and retained remote routes (VTEPs, MACs) for this
return 0;
}
+/*
+ * Handle change in setting for BUM handling. The supported values
+ * are head-end replication and dropping all BUM packets. Any change
+ * should be registered with zebra. Also, if doing head-end replication,
+ * need to advertise local VNIs as EVPN RT-3 wheras, if BUM packets are
+ * to be dropped, the RT-3s must be withdrawn.
+ */
+void bgp_evpn_flood_control_change(struct bgp *bgp)
+{
+ zlog_info("L2VPN EVPN BUM handling is %s",
+ bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL ?
+ "Flooding" : "Flooding Disabled");
+
+ bgp_zebra_vxlan_flood_control(bgp, bgp->vxlan_flood_ctrl);
+ if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL)
+ hash_iterate(bgp->vnihash, create_advertise_type3, bgp);
+ else if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_DISABLED)
+ hash_iterate(bgp->vnihash, delete_withdraw_type3, bgp);
+}
+
/*
* Cleanup EVPN information on disable - Need to delete and withdraw
* EVPN routes from peers.
bgp->vrf_export_rtl->del = evpn_xxport_delete_ecomm;
bgp->l2vnis = list_new();
bgp->l2vnis->cmp = (int (*)(void *, void *))vni_hash_cmp;
+
+ /* Default BUM handling is to do head-end replication. */
+ bgp->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL;
}
void bgp_evpn_vrf_delete(struct bgp *bgp_vrf)
struct ipaddr *originator_ip);
extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi,
struct ipaddr *originator_ip);
+extern void bgp_evpn_flood_control_change(struct bgp *bgp);
extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp);
extern void bgp_evpn_cleanup(struct bgp *bgp);
extern void bgp_evpn_init(struct bgp *bgp);
}
}
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_evpn_vty_clippy.c"
+#endif
+
+DEFPY(bgp_evpn_flood_control,
+ bgp_evpn_flood_control_cmd,
+ "[no$no] flooding <disable$disable|head-end-replication$her>",
+ NO_STR
+ "Specify handling for BUM packets\n"
+ "Do not flood any BUM packets\n"
+ "Flood BUM packets using head-end replication\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ enum vxlan_flood_control flood_ctrl;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (disable && !no)
+ flood_ctrl = VXLAN_FLOOD_DISABLED;
+ else if (her || no)
+ flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL;
+ else
+ return CMD_WARNING;
+
+ if (bgp->vxlan_flood_ctrl == flood_ctrl)
+ return CMD_SUCCESS;
+
+ bgp->vxlan_flood_ctrl = flood_ctrl;
+ bgp_evpn_flood_control_change(bgp);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (bgp_evpn_advertise_default_gw_vni,
bgp_evpn_advertise_default_gw_vni_cmd,
"advertise-default-gw",
json_object_string_add(json, "advertiseAllVnis",
is_evpn_enabled() ? "Enabled"
: "Disabled");
+ json_object_string_add(
+ json, "flooding",
+ bgp_def->vxlan_flood_ctrl
+ == VXLAN_FLOOD_HEAD_END_REPL
+ ? "Head-end replication"
+ : "Disabled");
json_object_int_add(json, "numVnis", num_vnis);
json_object_int_add(json, "numL2Vnis", num_l2vnis);
json_object_int_add(json, "numL3Vnis", num_l3vnis);
: "Disabled");
vty_out(vty, "Advertise All VNI flag: %s\n",
is_evpn_enabled() ? "Enabled" : "Disabled");
+ vty_out(vty, "BUM flooding: %s\n",
+ bgp_def->vxlan_flood_ctrl
+ == VXLAN_FLOOD_HEAD_END_REPL
+ ? "Head-end replication"
+ : "Disabled");
vty_out(vty, "Number of L2 VNIs: %u\n", num_l2vnis);
vty_out(vty, "Number of L3 VNIs: %u\n", num_l3vnis);
}
if (bgp->advertise_gw_macip)
vty_out(vty, " advertise-default-gw\n");
+ if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_DISABLED)
+ vty_out(vty, " flooding disable\n");
+
if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
BGP_L2VPN_EVPN_ADVERTISE_IPV4_UNICAST)) {
if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
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, &bgp_evpn_flood_control_cmd);
/* test commands */
install_element(BGP_EVPN_NODE, &test_adv_evpn_type4_route_cmd);
return zclient_send_message(zclient);
}
+int bgp_zebra_vxlan_flood_control(struct bgp *bgp,
+ enum vxlan_flood_control flood_ctrl)
+{
+ 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;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_VXLAN_FLOOD_CONTROL, bgp->vrf_id);
+ stream_putc(s, flood_ctrl);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
int bgp_zebra_advertise_all_vni(struct bgp *bgp, int advertise)
{
struct stream *s;
zclient_create_header(s, ZEBRA_ADVERTISE_ALL_VNI, bgp->vrf_id);
stream_putc(s, advertise);
+ /* Also inform current BUM handling setting. This is really
+ * relevant only when 'advertise' is set.
+ */
+ stream_putc(s, bgp->vxlan_flood_ctrl);
stream_putw_at(s, 0, stream_get_endp(s));
return zclient_send_message(zclient);
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_vxlan_flood_control(struct bgp *bgp,
+ enum vxlan_flood_control flood_ctrl);
extern int bgp_zebra_num_connects(void);
/* EVPN - use RFC 8365 to auto-derive RT */
int advertise_autort_rfc8365;
+ /*
+ * Flooding mechanism for BUM packets for VxLAN-EVPN.
+ */
+ enum vxlan_flood_control vxlan_flood_ctrl;
+
/* Hash table of Import RTs to EVIs */
struct hash *import_rt_hash;
bgpd_bgpd_rpki_la_LDFLAGS = -avoid-version -module -shared -export-dynamic
bgpd_bgpd_rpki_la_LIBADD = $(RTRLIB_LIBS)
+bgpd/bgp_evpn_vty_clippy.c: $(CLIPPY_DEPS)
+bgpd/bgp_evpn_vty.$(OBJEXT): bgpd/bgp_evpn_vty_clippy.c
bgpd/bgp_vty_clippy.c: $(CLIPPY_DEPS)
bgpd/bgp_vty.$(OBJEXT): bgpd/bgp_vty_clippy.c
bgpd/bgp_route_clippy.c: $(CLIPPY_DEPS)
DESC_ENTRY(ZEBRA_IPSET_DESTROY),
DESC_ENTRY(ZEBRA_IPSET_ENTRY_ADD),
DESC_ENTRY(ZEBRA_IPSET_ENTRY_DELETE),
+ DESC_ENTRY(ZEBRA_VXLAN_FLOOD_CONTROL),
};
#undef DESC_ENTRY
typedef uint32_t vni_t;
#define VNI_MAX 16777215 /* (2^24 - 1) */
+/* Flooding mechanisms for BUM packets. */
+/* Currently supported mechanisms are head-end (ingress) replication
+ * (which is the default) and no flooding. Future options could be
+ * using PIM-SM, PIM-Bidir etc.
+ */
+enum vxlan_flood_control {
+ VXLAN_FLOOD_HEAD_END_REPL = 0,
+ VXLAN_FLOOD_DISABLED,
+};
#endif /* __VXLAN_H__ */
ZEBRA_IPTABLE_ADD,
ZEBRA_IPTABLE_DELETE,
ZEBRA_IPTABLE_NOTIFY_OWNER,
+ ZEBRA_VXLAN_FLOOD_CONTROL,
} zebra_message_types_t;
struct redist_proto {
[ZEBRA_IPSET_ENTRY_DELETE] = zread_ipset_entry,
[ZEBRA_IPTABLE_ADD] = zread_iptable,
[ZEBRA_IPTABLE_DELETE] = zread_iptable,
+ [ZEBRA_VXLAN_FLOOD_CONTROL] = zebra_vxlan_flood_control,
};
#if defined(HANDLE_ZAPI_FUZZING)
#if !defined(__ZEBRA_VRF_H__)
#define __ZEBRA_VRF_H__
+#include "vxlan.h"
+
#include <zebra/zebra_ns.h>
#include <zebra/zebra_pw.h>
#include <lib/vxlan.h>
/* l3-vni info */
vni_t l3vni;
+ /*
+ * Flooding mechanism for BUM packets for VxLAN-EVPN.
+ */
+ enum vxlan_flood_control vxlan_flood_ctrl;
+
/* Route Installs */
uint64_t installs;
uint64_t removals;
*/
static int zvni_vtep_install(zebra_vni_t *zvni, struct in_addr *vtep_ip)
{
- return kernel_add_vtep(zvni->vni, zvni->vxlan_if, vtep_ip);
+ if (is_vxlan_flooding_head_end())
+ return kernel_add_vtep(zvni->vni, zvni->vxlan_if, vtep_ip);
+ return 0;
}
/*
return kernel_del_vtep(zvni->vni, zvni->vxlan_if, vtep_ip);
}
+/*
+ * Install or uninstall flood entries in the kernel corresponding to
+ * remote VTEPs. This is invoked upon change to BUM handling.
+ */
+static void zvni_handle_flooding_remote_vteps(struct hash_backet *backet,
+ void *zvrf)
+{
+ zebra_vni_t *zvni;
+ zebra_vtep_t *zvtep;
+
+ zvni = (zebra_vni_t *)backet->data;
+ if (!zvni)
+ return;
+
+ for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) {
+ if (is_vxlan_flooding_head_end())
+ zvni_vtep_install(zvni, &zvtep->vtep_ip);
+ else
+ zvni_vtep_uninstall(zvni, &zvtep->vtep_ip);
+ }
+}
+
/*
* Cleanup VNI/VTEP and update kernel
*/
return 0;
}
+/*
+ * Handle message from client to specify the flooding mechanism for
+ * BUM packets. The default is to do head-end (ingress) replication
+ * and the other supported option is to disable it. This applies to
+ * all BUM traffic and disabling it applies to both the transmit and
+ * receive direction.
+ */
+void zebra_vxlan_flood_control(ZAPI_HANDLER_ARGS)
+{
+ struct stream *s;
+ enum vxlan_flood_control flood_ctrl;
+
+ if (zvrf_id(zvrf) != VRF_DEFAULT) {
+ zlog_err("EVPN flood control for non-default VRF %u",
+ zvrf_id(zvrf));
+ return;
+ }
+
+ s = msg;
+ STREAM_GETC(s, flood_ctrl);
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("EVPN flood control %u, currently %u",
+ flood_ctrl, zvrf->vxlan_flood_ctrl);
+
+ if (zvrf->vxlan_flood_ctrl == flood_ctrl)
+ return;
+
+ zvrf->vxlan_flood_ctrl = flood_ctrl;
+
+ /* Install or uninstall flood entries corresponding to
+ * remote VTEPs.
+ */
+ hash_iterate(zvrf->vni_table, zvni_handle_flooding_remote_vteps,
+ zvrf);
+
+stream_failure:
+ return;
+}
+
/*
* Handle message from client to enable/disable advertisement of g/w macip
* routes
* When enabled, the VNI hash table will be built and MAC FDB table read;
* when disabled, the entries should be deleted and remote VTEPs and MACs
* uninstalled from the kernel.
+ * This also informs the setting for BUM handling at the time this change
+ * occurs; it is relevant only when specifying "learn".
*/
void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS)
{
struct stream *s = NULL;
int advertise = 0;
struct zebra_ns *zns = NULL;
+ enum vxlan_flood_control flood_ctrl;
if (zvrf_id(zvrf) != VRF_DEFAULT) {
zlog_debug("EVPN VNI Adv for non-default VRF %u",
s = msg;
STREAM_GETC(s, advertise);
+ STREAM_GETC(s, flood_ctrl);
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("EVPN VNI Adv %s, currently %s",
+ zlog_debug("EVPN VNI Adv %s, currently %s, flood control %u",
advertise ? "enabled" : "disabled",
- is_evpn_enabled() ? "enabled" : "disabled");
+ is_evpn_enabled() ? "enabled" : "disabled",
+ flood_ctrl);
if (zvrf->advertise_all_vni == advertise)
return;
zvrf->advertise_all_vni = advertise;
if (is_evpn_enabled()) {
+ /* Note BUM handling */
+ zvrf->vxlan_flood_ctrl = flood_ctrl;
+
/* Build VNI hash table and inform BGP. */
zvni_build_hash_table();
return zvrf ? zvrf->advertise_all_vni : 0;
}
+static inline int
+is_vxlan_flooding_head_end()
+{
+ struct zebra_vrf *zvrf = zebra_vrf_lookup_by_id(VRF_DEFAULT);
+
+ if (!zvrf)
+ return 0;
+ return (zvrf->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL);
+}
/* VxLAN interface change flags of interest. */
#define ZEBRA_VXLIF_LOCAL_IP_CHANGE 0x1
extern void zebra_vxlan_remote_macip_del(ZAPI_HANDLER_ARGS);
extern void zebra_vxlan_remote_vtep_add(ZAPI_HANDLER_ARGS);
extern void zebra_vxlan_remote_vtep_del(ZAPI_HANDLER_ARGS);
+extern void zebra_vxlan_flood_control(ZAPI_HANDLER_ARGS);
extern void zebra_vxlan_advertise_subnet(ZAPI_HANDLER_ARGS);
extern void zebra_vxlan_advertise_gw_macip(ZAPI_HANDLER_ARGS);
extern void zebra_vxlan_advertise_all_vni(ZAPI_HANDLER_ARGS);