mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-04-29 04:25:43 +00:00
*: support for evpn type-4 route
Signed-off-by: Mitesh Kanjariya <mitesh@cumulusnetworks.com>
This commit is contained in:
parent
68542a6da6
commit
50f74cf131
@ -801,6 +801,20 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
|
||||
len = sprintf(
|
||||
str_buf + str_pnt,
|
||||
"FS:marking %u", *(pnt+5));
|
||||
} else if (*pnt
|
||||
== ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
|
||||
struct ethaddr mac;
|
||||
pnt++;
|
||||
memcpy(&mac, pnt, ETH_ALEN);
|
||||
len = sprintf(
|
||||
str_buf + str_pnt,
|
||||
"ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
(uint8_t)mac.octet[0],
|
||||
(uint8_t)mac.octet[1],
|
||||
(uint8_t)mac.octet[2],
|
||||
(uint8_t)mac.octet[3],
|
||||
(uint8_t)mac.octet[4],
|
||||
(uint8_t)mac.octet[5]);
|
||||
} else
|
||||
unk_ecom = 1;
|
||||
} else {
|
||||
|
1145
bgpd/bgp_evpn.c
1145
bgpd/bgp_evpn.c
File diff suppressed because it is too large
Load Diff
@ -141,6 +141,10 @@ extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni);
|
||||
extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
|
||||
struct in_addr originator_ip,
|
||||
vrf_id_t tenant_vrf_id);
|
||||
extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi,
|
||||
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_cleanup_on_disable(struct bgp *bgp);
|
||||
extern void bgp_evpn_cleanup(struct bgp *bgp);
|
||||
extern void bgp_evpn_init(struct bgp *bgp);
|
||||
|
@ -31,9 +31,7 @@
|
||||
#define RT_ADDRSTRLEN 28
|
||||
|
||||
/* EVPN prefix lengths. This reprsent the sizeof struct prefix_evpn */
|
||||
#define EVPN_TYPE_2_ROUTE_PREFIXLEN 224
|
||||
#define EVPN_TYPE_3_ROUTE_PREFIXLEN 224
|
||||
#define EVPN_TYPE_5_ROUTE_PREFIXLEN 224
|
||||
#define EVPN_ROUTE_PREFIXLEN 224
|
||||
|
||||
/* EVPN route types. */
|
||||
typedef enum {
|
||||
@ -98,6 +96,38 @@ struct bgpevpn {
|
||||
|
||||
DECLARE_QOBJ_TYPE(bgpevpn)
|
||||
|
||||
struct evpnes {
|
||||
|
||||
/* Ethernet Segment Identifier */
|
||||
esi_t esi;
|
||||
|
||||
/* es flags */
|
||||
uint16_t flags;
|
||||
#define EVPNES_LOCAL 0x01
|
||||
#define EVPNES_REMOTE 0x02
|
||||
|
||||
/* Id for deriving the RD
|
||||
* automatically for this ESI */
|
||||
uint16_t rd_id;
|
||||
|
||||
/* RD for this VNI. */
|
||||
struct prefix_rd prd;
|
||||
|
||||
/* originator ip address */
|
||||
struct ipaddr originator_ip;
|
||||
|
||||
/* list of VTEPs in the same site */
|
||||
struct list *vtep_list;
|
||||
|
||||
/* Route table for EVPN routes for
|
||||
* this ESI. - type4 routes */
|
||||
struct bgp_table *route_table;
|
||||
|
||||
QOBJ_FIELDS
|
||||
};
|
||||
|
||||
DECLARE_QOBJ_TYPE(evpnes)
|
||||
|
||||
/* Mapping of Import RT to VNIs.
|
||||
* The Import RTs of all VNIs are maintained in a hash table with each
|
||||
* RT linking to all VNIs that will import routes matching this RT.
|
||||
@ -238,6 +268,15 @@ static inline int is_vni_param_configured(struct bgpevpn *vpn)
|
||||
|| is_export_rt_configured(vpn));
|
||||
}
|
||||
|
||||
static inline void encode_es_rt_extcomm(struct ecommunity_val *eval,
|
||||
struct ethaddr *mac)
|
||||
{
|
||||
memset(eval, 0, sizeof(struct ecommunity_val));
|
||||
eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
|
||||
eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT;
|
||||
memcpy(&eval->val[2], mac, ETH_ALEN);
|
||||
}
|
||||
|
||||
static inline void encode_rmac_extcomm(struct ecommunity_val *eval,
|
||||
struct ethaddr *rmac)
|
||||
{
|
||||
@ -326,7 +365,7 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
|
||||
{
|
||||
memset(p, 0, sizeof(struct prefix_evpn));
|
||||
p->family = AF_EVPN;
|
||||
p->prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN;
|
||||
p->prefixlen = EVPN_ROUTE_PREFIXLEN;
|
||||
p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
|
||||
memcpy(&p->prefix.macip_addr.mac.octet, mac->octet, ETH_ALEN);
|
||||
p->prefix.macip_addr.ip.ipa_type = IPADDR_NONE;
|
||||
@ -352,7 +391,7 @@ static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp,
|
||||
|
||||
memset(evp, 0, sizeof(struct prefix_evpn));
|
||||
evp->family = AF_EVPN;
|
||||
evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN;
|
||||
evp->prefixlen = EVPN_ROUTE_PREFIXLEN;
|
||||
evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
|
||||
evp->prefix.prefix_addr.ip_prefix_length = ip_prefix->prefixlen;
|
||||
evp->prefix.prefix_addr.ip.ipa_type = ip.ipa_type;
|
||||
@ -364,12 +403,26 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
|
||||
{
|
||||
memset(p, 0, sizeof(struct prefix_evpn));
|
||||
p->family = AF_EVPN;
|
||||
p->prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN;
|
||||
p->prefixlen = EVPN_ROUTE_PREFIXLEN;
|
||||
p->prefix.route_type = BGP_EVPN_IMET_ROUTE;
|
||||
p->prefix.imet_addr.ip.ipa_type = IPADDR_V4;
|
||||
p->prefix.imet_addr.ip.ipaddr_v4 = originator_ip;
|
||||
}
|
||||
|
||||
static inline void build_evpn_type4_prefix(struct prefix_evpn *p,
|
||||
esi_t *esi,
|
||||
struct in_addr originator_ip)
|
||||
{
|
||||
memset(p, 0, sizeof(struct prefix_evpn));
|
||||
p->family = AF_EVPN;
|
||||
p->prefixlen = EVPN_ROUTE_PREFIXLEN;
|
||||
p->prefix.route_type = BGP_EVPN_ES_ROUTE;
|
||||
p->prefix.es_addr.ip_prefix_length = IPV4_MAX_BITLEN;
|
||||
p->prefix.es_addr.ip.ipa_type = IPADDR_V4;
|
||||
p->prefix.es_addr.ip.ipaddr_v4 = originator_ip;
|
||||
memcpy(&p->prefix.es_addr.esi, esi, sizeof(esi_t));
|
||||
}
|
||||
|
||||
static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi,
|
||||
safi_t safi)
|
||||
{
|
||||
@ -384,6 +437,20 @@ static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void es_get_system_mac(esi_t *esi,
|
||||
struct ethaddr *mac)
|
||||
{
|
||||
/* for type-1 and type-3 ESIs,
|
||||
the system mac starts at val[1]
|
||||
*/
|
||||
memcpy(mac, &esi->val[1], ETH_ALEN);
|
||||
}
|
||||
|
||||
static inline int is_es_local(struct evpnes *es)
|
||||
{
|
||||
return CHECK_FLAG(es->flags, EVPNES_LOCAL) ? 1 : 0;
|
||||
}
|
||||
|
||||
extern void evpn_rt_delete_auto(struct bgp *, vni_t, struct list *);
|
||||
extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
|
||||
struct ecommunity *ecomadd);
|
||||
@ -417,4 +484,8 @@ extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
|
||||
struct in_addr originator_ip,
|
||||
vrf_id_t tenant_vrf_id);
|
||||
extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
|
||||
extern struct evpnes *bgp_evpn_lookup_es(struct bgp *bgp, esi_t *esi);
|
||||
extern struct evpnes *bgp_evpn_es_new(struct bgp *bgp, esi_t *esi,
|
||||
struct ipaddr *originator_ip);
|
||||
extern void bgp_evpn_es_free(struct bgp *bgp, struct evpnes *es);
|
||||
#endif /* _BGP_EVPN_PRIVATE_H */
|
||||
|
@ -328,6 +328,7 @@ static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp,
|
||||
vty_out(vty,
|
||||
"EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
|
||||
vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
|
||||
vty_out(vty, "EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]\n");
|
||||
vty_out(vty, "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
|
||||
vty_out(vty, "%s", ri_header);
|
||||
}
|
||||
@ -409,6 +410,45 @@ static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf,
|
||||
json_object_object_add(json, "exportRts", json_export_rtl);
|
||||
}
|
||||
|
||||
static void display_es(struct vty *vty, struct evpnes *es, json_object *json)
|
||||
{
|
||||
struct in_addr *vtep;
|
||||
char buf[ESI_STR_LEN];
|
||||
char buf1[RD_ADDRSTRLEN];
|
||||
char buf2[INET6_ADDRSTRLEN];
|
||||
struct listnode *node = NULL;
|
||||
json_object *json_vteps = NULL;
|
||||
|
||||
if (json) {
|
||||
json_vteps = json_object_new_array();
|
||||
json_object_string_add(json, "esi",
|
||||
esi_to_str(&es->esi, buf, sizeof(buf)));
|
||||
json_object_string_add(json, "rd",
|
||||
prefix_rd2str(&es->prd, buf1,
|
||||
sizeof(buf1)));
|
||||
json_object_string_add(json,"originatorIp",
|
||||
ipaddr2str(&es->originator_ip, buf2, sizeof(buf2)));
|
||||
if (es->vtep_list) {
|
||||
for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep))
|
||||
json_object_array_add(json_vteps,
|
||||
json_object_new_string(inet_ntoa(*vtep)));
|
||||
}
|
||||
json_object_object_add(json, "vteps", json_vteps);
|
||||
} else {
|
||||
vty_out(vty, "ESI: %s\n",
|
||||
esi_to_str(&es->esi, buf, sizeof(buf)));
|
||||
vty_out(vty, " RD: %s\n", prefix_rd2str(&es->prd, buf1,
|
||||
sizeof(buf1)));
|
||||
vty_out(vty, " Originator-IP: %s\n",
|
||||
ipaddr2str(&es->originator_ip, buf2, sizeof(buf2)));
|
||||
if (es->vtep_list) {
|
||||
vty_out(vty, " VTEP List:\n");
|
||||
for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep))
|
||||
vty_out(vty," %s\n", inet_ntoa(*vtep));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
|
||||
{
|
||||
char buf1[RD_ADDRSTRLEN];
|
||||
@ -487,6 +527,88 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
|
||||
json_object_object_add(json, "exportRts", json_export_rtl);
|
||||
}
|
||||
|
||||
static void show_esi_routes(struct bgp *bgp,
|
||||
struct evpnes *es,
|
||||
struct vty *vty,
|
||||
json_object *json)
|
||||
{
|
||||
int header = 1;
|
||||
struct bgp_node *rn;
|
||||
struct bgp_info *ri;
|
||||
uint32_t prefix_cnt, path_cnt;
|
||||
uint64_t tbl_ver;
|
||||
|
||||
prefix_cnt = path_cnt = 0;
|
||||
|
||||
tbl_ver = es->route_table->version;
|
||||
for (rn = bgp_table_top(es->route_table); rn;
|
||||
rn = bgp_route_next(rn)) {
|
||||
int add_prefix_to_json = 0;
|
||||
char prefix_str[BUFSIZ];
|
||||
json_object *json_paths = NULL;
|
||||
json_object *json_prefix = NULL;
|
||||
|
||||
bgp_evpn_route2str((struct prefix_evpn *)&rn->p, prefix_str,
|
||||
sizeof(prefix_str));
|
||||
|
||||
if (json)
|
||||
json_prefix = json_object_new_object();
|
||||
|
||||
if (rn->info) {
|
||||
/* Overall header/legend displayed once. */
|
||||
if (header) {
|
||||
bgp_evpn_show_route_header(vty, bgp,
|
||||
tbl_ver, json);
|
||||
header = 0;
|
||||
}
|
||||
|
||||
prefix_cnt++;
|
||||
}
|
||||
|
||||
if (json)
|
||||
json_paths = json_object_new_array();
|
||||
|
||||
/* For EVPN, the prefix is displayed for each path (to fit in
|
||||
* with code that already exists).
|
||||
*/
|
||||
for (ri = rn->info; ri; ri = ri->next) {
|
||||
json_object *json_path = NULL;
|
||||
|
||||
if (json)
|
||||
json_path = json_object_new_array();
|
||||
|
||||
route_vty_out(vty, &rn->p, ri, 0, SAFI_EVPN, json_path);
|
||||
|
||||
if (json)
|
||||
json_object_array_add(json_paths, json_path);
|
||||
|
||||
path_cnt++;
|
||||
add_prefix_to_json = 1;
|
||||
}
|
||||
|
||||
if (json && add_prefix_to_json) {
|
||||
json_object_string_add(json_prefix, "prefix",
|
||||
prefix_str);
|
||||
json_object_int_add(json_prefix, "prefixLen",
|
||||
rn->p.prefixlen);
|
||||
json_object_object_add(json_prefix, "paths",
|
||||
json_paths);
|
||||
json_object_object_add(json, prefix_str, json_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
if (json) {
|
||||
json_object_int_add(json, "numPrefix", prefix_cnt);
|
||||
json_object_int_add(json, "numPaths", path_cnt);
|
||||
} else {
|
||||
if (prefix_cnt == 0)
|
||||
vty_out(vty, "No EVPN prefixes exist for this ESI");
|
||||
else
|
||||
vty_out(vty, "\nDisplayed %u prefixes (%u paths)\n",
|
||||
prefix_cnt, path_cnt);
|
||||
}
|
||||
}
|
||||
|
||||
static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
|
||||
struct vty *vty, struct in_addr vtep_ip,
|
||||
json_object *json)
|
||||
@ -575,7 +697,7 @@ static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
|
||||
vty_out(vty, "No EVPN prefixes %sexist for this VNI",
|
||||
type ? "(of requested type) " : "");
|
||||
else
|
||||
vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s",
|
||||
vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n",
|
||||
prefix_cnt, path_cnt,
|
||||
type ? " (of requested type)" : "");
|
||||
}
|
||||
@ -707,6 +829,47 @@ static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
|
||||
}
|
||||
}
|
||||
|
||||
static void show_es_entry(struct hash_backet *backet, void *args[])
|
||||
{
|
||||
char buf[ESI_STR_LEN];
|
||||
char buf1[RD_ADDRSTRLEN];
|
||||
char buf2[INET6_ADDRSTRLEN];
|
||||
struct in_addr *vtep = NULL;
|
||||
struct vty *vty = args[0];
|
||||
json_object *json = args[1];
|
||||
json_object *json_vteps = NULL;
|
||||
struct listnode *node = NULL;
|
||||
struct evpnes *es = (struct evpnes *)backet->data;
|
||||
|
||||
if (json) {
|
||||
json_vteps = json_object_new_array();
|
||||
json_object_string_add(json, "esi",
|
||||
esi_to_str(&es->esi, buf, sizeof(buf)));
|
||||
json_object_string_add(json, "type",
|
||||
is_es_local(es) ? "Local" : "Remote");
|
||||
json_object_string_add(json, "rd",
|
||||
prefix_rd2str(&es->prd, buf1,
|
||||
sizeof(buf1)));
|
||||
json_object_string_add(json,"originatorIp",
|
||||
ipaddr2str(&es->originator_ip, buf2, sizeof(buf2)));
|
||||
if (es->vtep_list) {
|
||||
for (ALL_LIST_ELEMENTS_RO(es->vtep_list, node, vtep))
|
||||
json_object_array_add(json_vteps,
|
||||
json_object_new_string(
|
||||
inet_ntoa(*vtep)));
|
||||
}
|
||||
json_object_object_add(json, "vteps", json_vteps);
|
||||
} else {
|
||||
vty_out(vty, "%-30s %-6s %-21s %-15s %-6d\n",
|
||||
esi_to_str(&es->esi, buf, sizeof(buf)),
|
||||
is_es_local(es) ? "Local" : "Remote",
|
||||
prefix_rd2str(&es->prd, buf1, sizeof(buf1)),
|
||||
ipaddr2str(&es->originator_ip, buf2,
|
||||
sizeof(buf2)),
|
||||
es->vtep_list ? listcount(es->vtep_list) : 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void show_vni_entry(struct hash_backet *backet, void *args[])
|
||||
{
|
||||
struct vty *vty;
|
||||
@ -1969,6 +2132,23 @@ static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
|
||||
}
|
||||
}
|
||||
|
||||
/* Disaplay EVPN routes for a ESI - VTY handler */
|
||||
static void evpn_show_routes_esi(struct vty *vty, struct bgp *bgp,
|
||||
esi_t *esi, json_object *json)
|
||||
{
|
||||
struct evpnes *es = NULL;
|
||||
|
||||
/* locate the ES */
|
||||
es = bgp_evpn_lookup_es(bgp, esi);
|
||||
if (!es) {
|
||||
if (!json)
|
||||
vty_out(vty, "ESI not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
show_esi_routes(bgp, es, vty, json);
|
||||
}
|
||||
|
||||
/*
|
||||
* Display EVPN routes for a VNI - vty handler.
|
||||
* If 'type' is non-zero, only routes matching that type are shown.
|
||||
@ -2318,6 +2498,42 @@ static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
|
||||
}
|
||||
}
|
||||
|
||||
/* Display specific ES */
|
||||
static void evpn_show_es(struct vty *vty, struct bgp *bgp, esi_t *esi,
|
||||
json_object *json)
|
||||
{
|
||||
struct evpnes *es = NULL;
|
||||
|
||||
es = bgp_evpn_lookup_es(bgp, esi);
|
||||
if (es) {
|
||||
display_es(vty, es, json);
|
||||
} else {
|
||||
if (json) {
|
||||
vty_out(vty, "{}\n");
|
||||
} else {
|
||||
vty_out(vty, "ESI not found\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Display all ESs */
|
||||
static void evpn_show_all_es(struct vty *vty, struct bgp *bgp, json_object *json)
|
||||
{
|
||||
void *args[2];
|
||||
|
||||
if (!json)
|
||||
vty_out(vty, "%-30s %-6s %-21s %-15s %-6s\n",
|
||||
"ESI", "Type", "RD", "Originator-IP", "#VTEPs");
|
||||
|
||||
/* print all ESs */
|
||||
args[0] = vty;
|
||||
args[1] = json;
|
||||
hash_iterate(bgp->esihash,
|
||||
(void (*)(struct hash_backet *, void *))show_es_entry,
|
||||
args);
|
||||
}
|
||||
|
||||
/*
|
||||
* Display specified VNI (vty handler)
|
||||
*/
|
||||
@ -3027,6 +3243,58 @@ DEFUN(show_bgp_l2vpn_evpn_vni,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* Disaply ES */
|
||||
DEFUN(show_bgp_l2vpn_evpn_es,
|
||||
show_bgp_l2vpn_evpn_es_cmd,
|
||||
"show bgp l2vpn evpn es [ESI] [json]",
|
||||
SHOW_STR
|
||||
BGP_STR
|
||||
L2VPN_HELP_STR
|
||||
EVPN_HELP_STR
|
||||
"ethernet-Segment\n"
|
||||
"Ethernet-Segment Identifier\n")
|
||||
{
|
||||
int idx = 0;
|
||||
uint8_t uj = 0;
|
||||
esi_t esi = {0};
|
||||
json_object *json = NULL;
|
||||
struct bgp *bgp = NULL;
|
||||
|
||||
uj = use_json(argc, argv);
|
||||
|
||||
bgp = bgp_get_default();
|
||||
if (!bgp)
|
||||
return CMD_WARNING;
|
||||
|
||||
if (!argv_find(argv, argc, "evpn", &idx))
|
||||
return CMD_WARNING;
|
||||
|
||||
if ((uj && argc == ((idx + 1) + 2)) ||
|
||||
(!uj && argc == (idx + 1) + 1)) {
|
||||
|
||||
/* show all ESs */
|
||||
evpn_show_all_es(vty, bgp, json);
|
||||
} else {
|
||||
|
||||
/* show a specific ES */
|
||||
|
||||
/* get the ESI - ESI-ID is at argv[5] */
|
||||
if (!str_to_esi(argv[idx + 2]->arg, &esi)) {
|
||||
vty_out(vty, "%% Malformed ESI\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
evpn_show_es(vty, bgp, &esi, json);
|
||||
}
|
||||
|
||||
if (uj) {
|
||||
vty_out(vty, "%s\n", json_object_to_json_string_ext(
|
||||
json, JSON_C_TO_STRING_PRETTY));
|
||||
json_object_free(json);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Display EVPN neighbor summary.
|
||||
*/
|
||||
@ -3056,7 +3324,7 @@ DEFUN(show_bgp_l2vpn_evpn_summary,
|
||||
*/
|
||||
DEFUN(show_bgp_l2vpn_evpn_route,
|
||||
show_bgp_l2vpn_evpn_route_cmd,
|
||||
"show bgp l2vpn evpn route [type <macip|multicast|prefix>] [json]",
|
||||
"show bgp l2vpn evpn route [type <macip|multicast|es|prefix>] [json]",
|
||||
SHOW_STR
|
||||
BGP_STR
|
||||
L2VPN_HELP_STR
|
||||
@ -3065,7 +3333,8 @@ DEFUN(show_bgp_l2vpn_evpn_route,
|
||||
"Specify Route type\n"
|
||||
"MAC-IP (Type-2) route\n"
|
||||
"Multicast (Type-3) route\n"
|
||||
"Prefix route\n"
|
||||
"Ethernet Segment (type-4) route \n"
|
||||
"Prefix (type-5 )route\n"
|
||||
JSON_STR)
|
||||
{
|
||||
struct bgp *bgp;
|
||||
@ -3090,6 +3359,8 @@ DEFUN(show_bgp_l2vpn_evpn_route,
|
||||
type = BGP_EVPN_MAC_IP_ROUTE;
|
||||
else if (strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0)
|
||||
type = BGP_EVPN_IMET_ROUTE;
|
||||
else if (strncmp(argv[type_idx + 1]->arg, "es", 2) == 0)
|
||||
type = BGP_EVPN_ES_ROUTE;
|
||||
else if (strncmp(argv[type_idx + 1]->arg, "pr", 2) == 0)
|
||||
type = BGP_EVPN_IP_PREFIX_ROUTE;
|
||||
else
|
||||
@ -3111,7 +3382,7 @@ DEFUN(show_bgp_l2vpn_evpn_route,
|
||||
*/
|
||||
DEFUN(show_bgp_l2vpn_evpn_route_rd,
|
||||
show_bgp_l2vpn_evpn_route_rd_cmd,
|
||||
"show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|multicast|prefix>] [json]",
|
||||
"show bgp l2vpn evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|multicast|es|prefix>] [json]",
|
||||
SHOW_STR
|
||||
BGP_STR
|
||||
L2VPN_HELP_STR
|
||||
@ -3122,6 +3393,7 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd,
|
||||
"Specify Route type\n"
|
||||
"MAC-IP (Type-2) route\n"
|
||||
"Multicast (Type-3) route\n"
|
||||
"Ethernet Segment route\n"
|
||||
"Prefix route\n"
|
||||
JSON_STR)
|
||||
{
|
||||
@ -3255,6 +3527,50 @@ DEFUN(show_bgp_l2vpn_evpn_route_rd_macip,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
/* Display per ESI routing table */
|
||||
DEFUN(show_bgp_l2vpn_evpn_route_esi,
|
||||
show_bgp_l2vpn_evpn_route_esi_cmd,
|
||||
"show bgp l2vpn evpn route esi ESI [json]",
|
||||
SHOW_STR
|
||||
BGP_STR
|
||||
L2VPN_HELP_STR
|
||||
EVPN_HELP_STR
|
||||
"EVPN route information\n"
|
||||
"Ethernet Segment Identifier\n"
|
||||
"ESI ID\n"
|
||||
JSON_STR)
|
||||
{
|
||||
int uj = 0;
|
||||
esi_t esi = {0};
|
||||
struct bgp *bgp = NULL;
|
||||
json_object *json = NULL;
|
||||
|
||||
bgp = bgp_get_default();
|
||||
if (!bgp)
|
||||
return CMD_WARNING;
|
||||
|
||||
uj = use_json(argc, argv);
|
||||
if (uj)
|
||||
json = json_object_new_object();
|
||||
|
||||
/* get the ESI - ESI-ID is at argv[6] */
|
||||
if (!str_to_esi(argv[6]->arg, &esi)) {
|
||||
vty_out(vty, "%% Malformed ESI\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
evpn_show_routes_esi(vty, bgp, &esi, json);
|
||||
|
||||
if (uj) {
|
||||
vty_out(vty, "%s\n", json_object_to_json_string_ext(
|
||||
json, JSON_C_TO_STRING_PRETTY));
|
||||
json_object_free(json);
|
||||
}
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display per-VNI EVPN routing table.
|
||||
*/
|
||||
@ -3583,6 +3899,78 @@ DEFUN(show_bgp_l2vpn_evpn_import_rt,
|
||||
}
|
||||
|
||||
#if defined(HAVE_CUMULUS)
|
||||
DEFUN(test_adv_evpn_type4_route,
|
||||
test_adv_evpn_type4_route_cmd,
|
||||
"advertise es ESI",
|
||||
"Advertise EVPN ES route\n"
|
||||
"Ethernet-segment\n"
|
||||
"Ethernet-Segment Identifier\n")
|
||||
{
|
||||
int ret = 0;
|
||||
esi_t esi;
|
||||
struct bgp *bgp;
|
||||
struct ipaddr vtep_ip;
|
||||
|
||||
bgp = bgp_get_default();
|
||||
if (!bgp) {
|
||||
vty_out(vty, "%%Default BGP instance not yet created\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if(!str_to_esi(argv[2]->arg, &esi)) {
|
||||
vty_out(vty, "%%Malformed ESI\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vtep_ip.ipa_type = IPADDR_V4;
|
||||
vtep_ip.ipaddr_v4 = bgp->router_id;
|
||||
|
||||
ret = bgp_evpn_local_es_add(bgp, &esi, &vtep_ip);
|
||||
if (ret == -1) {
|
||||
vty_out(vty, "%%Failed to EVPN advertise type-4 route\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(test_withdraw_evpn_type4_route,
|
||||
test_withdraw_evpn_type4_route_cmd,
|
||||
"withdraw es ESI",
|
||||
"Advertise EVPN ES route\n"
|
||||
"Ethernet-segment\n"
|
||||
"Ethernet-Segment Identifier\n")
|
||||
{
|
||||
int ret = 0;
|
||||
esi_t esi;
|
||||
struct bgp *bgp;
|
||||
struct ipaddr vtep_ip;
|
||||
|
||||
bgp = bgp_get_default();
|
||||
if (!bgp) {
|
||||
vty_out(vty, "%%Default BGP instance not yet created\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if (!bgp->peer_self) {
|
||||
vty_out(vty, "%%BGP instance doesnt have self peer\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
if(!str_to_esi(argv[2]->arg, &esi)) {
|
||||
vty_out(vty, "%%Malformed ESI\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
|
||||
vtep_ip.ipa_type = IPADDR_V4;
|
||||
vtep_ip.ipaddr_v4 = bgp->router_id;
|
||||
ret = bgp_evpn_local_es_del(bgp, &esi, &vtep_ip);
|
||||
if (ret == -1) {
|
||||
vty_out(vty, "%%Failed to withdraw EVPN type-4 route\n");
|
||||
return CMD_WARNING;
|
||||
}
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
ALIAS_HIDDEN(show_bgp_l2vpn_evpn_vni, show_bgp_evpn_vni_cmd,
|
||||
"show bgp evpn vni [(1-16777215)]", SHOW_STR BGP_STR EVPN_HELP_STR
|
||||
"Show VNI\n"
|
||||
@ -4543,12 +4931,18 @@ void bgp_ethernetvpn_init(void)
|
||||
install_element(BGP_EVPN_NODE, &bgp_evpn_default_originate_cmd);
|
||||
install_element(BGP_EVPN_NODE, &no_bgp_evpn_default_originate_cmd);
|
||||
|
||||
/* test commands */
|
||||
install_element(BGP_EVPN_NODE, &test_adv_evpn_type4_route_cmd);
|
||||
install_element(BGP_EVPN_NODE, &test_withdraw_evpn_type4_route_cmd);
|
||||
|
||||
/* "show bgp l2vpn evpn" commands. */
|
||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd);
|
||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);
|
||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd);
|
||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
|
||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
|
||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd);
|
||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_esi_cmd);
|
||||
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd);
|
||||
install_element(VIEW_NODE,
|
||||
&show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
|
||||
|
@ -116,6 +116,8 @@ DEFINE_MTYPE(BGPD, LCOMMUNITY_STR, "Large Community display string")
|
||||
DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value")
|
||||
|
||||
DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information")
|
||||
DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP Ip")
|
||||
DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI Information")
|
||||
DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT")
|
||||
DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT")
|
||||
DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP")
|
||||
|
@ -111,6 +111,9 @@ DECLARE_MTYPE(LCOMMUNITY)
|
||||
DECLARE_MTYPE(LCOMMUNITY_STR)
|
||||
DECLARE_MTYPE(LCOMMUNITY_VAL)
|
||||
|
||||
DECLARE_MTYPE(BGP_EVPN_ES)
|
||||
DECLARE_MTYPE(BGP_EVPN_ES_VTEP)
|
||||
|
||||
DECLARE_MTYPE(BGP_EVPN)
|
||||
DECLARE_MTYPE(BGP_EVPN_IMPORT_RT)
|
||||
DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT)
|
||||
|
@ -2107,12 +2107,10 @@ int bgp_zebra_has_route_changed(struct bgp_node *rn, struct bgp_info *selected)
|
||||
struct bgp_info *mpinfo;
|
||||
|
||||
/* If this is multipath, check all selected paths for any nexthop change
|
||||
* or
|
||||
* attribute change. Some attribute changes (e.g., community) aren't of
|
||||
* or attribute change. Some attribute changes (e.g., community) aren't of
|
||||
* relevance to the RIB, but we'll update zebra to ensure we handle the
|
||||
* case of BGP nexthop change. This is the behavior when the best path
|
||||
* has
|
||||
* an attribute change anyway.
|
||||
* has an attribute change anyway.
|
||||
*/
|
||||
if (CHECK_FLAG(selected->flags, BGP_INFO_IGP_CHANGED)
|
||||
|| CHECK_FLAG(selected->flags, BGP_INFO_MULTIPATH_CHG))
|
||||
|
@ -2249,6 +2249,40 @@ static void bgp_zebra_connected(struct zclient *zclient)
|
||||
*/
|
||||
}
|
||||
|
||||
static int bgp_zebra_process_local_es(int cmd, struct zclient *zclient,
|
||||
zebra_size_t length, vrf_id_t vrf_id)
|
||||
{
|
||||
esi_t esi;
|
||||
struct bgp *bgp = NULL;
|
||||
struct stream *s = NULL;
|
||||
char buf[ESI_STR_LEN];
|
||||
char buf1[INET6_ADDRSTRLEN];
|
||||
struct ipaddr originator_ip;
|
||||
|
||||
memset(&esi, 0, sizeof(esi_t));
|
||||
memset(&originator_ip, 0, sizeof(struct ipaddr));
|
||||
|
||||
bgp = bgp_lookup_by_vrf_id(vrf_id);
|
||||
if (!bgp)
|
||||
return 0;
|
||||
|
||||
s = zclient->ibuf;
|
||||
stream_get(&esi, s, sizeof(esi_t));
|
||||
stream_get(&originator_ip, s, sizeof(struct ipaddr));
|
||||
|
||||
if (BGP_DEBUG(zebra, ZEBRA))
|
||||
zlog_debug("Rx %s ESI %s originator-ip %s",
|
||||
(cmd == ZEBRA_LOCAL_ES_ADD) ? "add" : "del",
|
||||
esi_to_str(&esi, buf, sizeof(buf)),
|
||||
ipaddr2str(&originator_ip, buf1, sizeof(buf1)));
|
||||
|
||||
if (cmd == ZEBRA_LOCAL_ES_ADD)
|
||||
bgp_evpn_local_es_add(bgp, &esi, &originator_ip);
|
||||
else
|
||||
bgp_evpn_local_es_del(bgp, &esi, &originator_ip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bgp_zebra_process_local_l3vni(int cmd, struct zclient *zclient,
|
||||
zebra_size_t length, vrf_id_t vrf_id)
|
||||
{
|
||||
@ -2484,6 +2518,8 @@ void bgp_zebra_init(struct thread_master *master)
|
||||
zclient->nexthop_update = bgp_read_nexthop_update;
|
||||
zclient->import_check_update = bgp_read_import_check_update;
|
||||
zclient->fec_update = bgp_read_fec_update;
|
||||
zclient->local_es_add = bgp_zebra_process_local_es;
|
||||
zclient->local_es_del = bgp_zebra_process_local_es;
|
||||
zclient->local_vni_add = bgp_zebra_process_local_vni;
|
||||
zclient->local_vni_del = bgp_zebra_process_local_vni;
|
||||
zclient->local_macip_add = bgp_zebra_process_local_macip;
|
||||
|
@ -534,6 +534,9 @@ struct bgp {
|
||||
|
||||
struct bgp_pbr_config *bgp_pbr_cfg;
|
||||
|
||||
/* local esi hash table */
|
||||
struct hash *esihash;
|
||||
|
||||
QOBJ_FIELDS
|
||||
};
|
||||
DECLARE_QOBJ_TYPE(bgp)
|
||||
|
@ -945,6 +945,8 @@ static const struct zebra_desc_table command_types[] = {
|
||||
DESC_ENTRY(ZEBRA_ADVERTISE_ALL_VNI),
|
||||
DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW),
|
||||
DESC_ENTRY(ZEBRA_ADVERTISE_SUBNET),
|
||||
DESC_ENTRY(ZEBRA_LOCAL_ES_ADD),
|
||||
DESC_ENTRY(ZEBRA_LOCAL_ES_DEL),
|
||||
DESC_ENTRY(ZEBRA_VNI_ADD),
|
||||
DESC_ENTRY(ZEBRA_VNI_DEL),
|
||||
DESC_ENTRY(ZEBRA_L3VNI_ADD),
|
||||
|
68
lib/prefix.c
68
lib/prefix.c
@ -429,6 +429,14 @@ static const struct in6_addr maskbytes6[] = {
|
||||
|
||||
#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff)
|
||||
|
||||
void prefix_hexdump(const struct prefix *p)
|
||||
{
|
||||
char buf[PREFIX_STRLEN];
|
||||
zlog_debug("prefix: %s",
|
||||
prefix2str(p, buf, sizeof(buf)));
|
||||
zlog_hexdump(p, sizeof(struct prefix));
|
||||
}
|
||||
|
||||
int is_zero_mac(struct ethaddr *mac)
|
||||
{
|
||||
int i = 0;
|
||||
@ -1262,7 +1270,12 @@ static const char *prefixevpn_imet2str(const struct prefix_evpn *p, char *str,
|
||||
static const char *prefixevpn_es2str(const struct prefix_evpn *p, char *str,
|
||||
int size)
|
||||
{
|
||||
snprintf(str, size, "Unsupported EVPN prefix");
|
||||
char buf[ESI_STR_LEN];
|
||||
|
||||
snprintf(str, size, "[%d]:[%s]:[%s]/%d", p->prefix.route_type,
|
||||
esi_to_str(&p->prefix.es_addr.esi, buf, sizeof(buf)),
|
||||
inet_ntoa(p->prefix.es_addr.ip.ipaddr_v4),
|
||||
p->prefixlen);
|
||||
return str;
|
||||
}
|
||||
|
||||
@ -1540,3 +1553,56 @@ unsigned prefix_hash_key(void *pp)
|
||||
offsetof(struct prefix, u.prefix) + PSIZE(copy.prefixlen),
|
||||
0x55aa5a5a);
|
||||
}
|
||||
|
||||
/* converts to internal representation of esi
|
||||
* returns 1 on success, 0 otherwise
|
||||
* format accepted: aa:aa:aa:aa:aa:aa:aa:aa:aa:aa
|
||||
* if esi parameter is null, then check only
|
||||
*/
|
||||
int str_to_esi(const char *str, esi_t *esi)
|
||||
{
|
||||
int i;
|
||||
unsigned int a[ESI_BYTES];
|
||||
|
||||
if (!str)
|
||||
return 0;
|
||||
|
||||
if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x",
|
||||
a + 0, a + 1, a + 2, a + 3,
|
||||
a + 4, a + 5, a + 6, a + 7,
|
||||
a + 8, a + 9)
|
||||
!= ESI_BYTES) {
|
||||
/* error in incoming str length */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* valid ESI */
|
||||
if (!esi)
|
||||
return 1;
|
||||
for (i = 0; i < ESI_BYTES; ++i)
|
||||
esi->val[i] = a[i] & 0xff;
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *esi_to_str(const esi_t *esi, char *buf, int size)
|
||||
{
|
||||
char *ptr;
|
||||
|
||||
if (!esi)
|
||||
return NULL;
|
||||
if (!buf)
|
||||
ptr = (char *)XMALLOC(MTYPE_TMP,
|
||||
ESI_STR_LEN * sizeof(char));
|
||||
else {
|
||||
assert(size >= ESI_STR_LEN);
|
||||
ptr = buf;
|
||||
}
|
||||
|
||||
snprintf(ptr, ESI_STR_LEN,
|
||||
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
||||
esi->val[0], esi->val[1], esi->val[2],
|
||||
esi->val[3], esi->val[4], esi->val[5],
|
||||
esi->val[6], esi->val[7], esi->val[8],
|
||||
esi->val[9]);
|
||||
return ptr;
|
||||
}
|
||||
|
15
lib/prefix.h
15
lib/prefix.h
@ -39,6 +39,9 @@
|
||||
#define ETH_ALEN 6
|
||||
#endif
|
||||
|
||||
#define ESI_BYTES 10
|
||||
#define ESI_STR_LEN (3* ESI_BYTES)
|
||||
|
||||
#define ETHER_ADDR_STRLEN (3*ETH_ALEN)
|
||||
/*
|
||||
* there isn't a portable ethernet address type. We define our
|
||||
@ -213,6 +216,8 @@ static inline int is_evpn_prefix_ipaddr_none(const struct prefix_evpn *evp)
|
||||
return IS_IPADDR_NONE(&(evp)->prefix.macip_addr.ip);
|
||||
if (evp->prefix.route_type == 3)
|
||||
return IS_IPADDR_NONE(&(evp)->prefix.imet_addr.ip);
|
||||
if (evp->prefix.route_type == 4)
|
||||
return IS_IPADDR_NONE(&(evp)->prefix.es_addr.ip);
|
||||
if (evp->prefix.route_type == 5)
|
||||
return IS_IPADDR_NONE(&(evp)->prefix.prefix_addr.ip);
|
||||
return 0;
|
||||
@ -224,6 +229,8 @@ static inline int is_evpn_prefix_ipaddr_v4(const struct prefix_evpn *evp)
|
||||
return IS_IPADDR_V4(&(evp)->prefix.macip_addr.ip);
|
||||
if (evp->prefix.route_type == 3)
|
||||
return IS_IPADDR_V4(&(evp)->prefix.imet_addr.ip);
|
||||
if (evp->prefix.route_type == 4)
|
||||
return IS_IPADDR_V4(&(evp)->prefix.es_addr.ip);
|
||||
if (evp->prefix.route_type == 5)
|
||||
return IS_IPADDR_V4(&(evp)->prefix.prefix_addr.ip);
|
||||
return 0;
|
||||
@ -235,6 +242,8 @@ static inline int is_evpn_prefix_ipaddr_v6(const struct prefix_evpn *evp)
|
||||
return IS_IPADDR_V6(&(evp)->prefix.macip_addr.ip);
|
||||
if (evp->prefix.route_type == 3)
|
||||
return IS_IPADDR_V6(&(evp)->prefix.imet_addr.ip);
|
||||
if (evp->prefix.route_type == 4)
|
||||
return IS_IPADDR_V6(&(evp)->prefix.es_addr.ip);
|
||||
if (evp->prefix.route_type == 5)
|
||||
return IS_IPADDR_V6(&(evp)->prefix.prefix_addr.ip);
|
||||
return 0;
|
||||
@ -432,6 +441,11 @@ extern char *prefix_mac2str(const struct ethaddr *mac, char *buf, int size);
|
||||
|
||||
extern unsigned prefix_hash_key(void *pp);
|
||||
|
||||
extern int str_to_esi(const char *str, esi_t *esi);
|
||||
extern char *esi_to_str(const esi_t *esi, char *buf, int size);
|
||||
extern void prefix_hexdump(const struct prefix *p);
|
||||
extern void prefix_evpn_hexdump(const struct prefix_evpn *p);
|
||||
|
||||
static inline int ipv6_martian(struct in6_addr *addr)
|
||||
{
|
||||
struct in6_addr localhost_addr;
|
||||
@ -482,5 +496,4 @@ static inline int is_host_route(struct prefix *p)
|
||||
return (p->prefixlen == IPV6_MAX_BITLEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* _ZEBRA_PREFIX_H */
|
||||
|
@ -2760,6 +2760,16 @@ static int zclient_read(struct thread *thread)
|
||||
if (zclient->fec_update)
|
||||
(*zclient->fec_update)(command, zclient, length);
|
||||
break;
|
||||
case ZEBRA_LOCAL_ES_ADD:
|
||||
if (zclient->local_es_add)
|
||||
(*zclient->local_es_add)(command, zclient, length,
|
||||
vrf_id);
|
||||
break;
|
||||
case ZEBRA_LOCAL_ES_DEL:
|
||||
if (zclient->local_es_del)
|
||||
(*zclient->local_es_del)(command, zclient, length,
|
||||
vrf_id);
|
||||
break;
|
||||
case ZEBRA_VNI_ADD:
|
||||
if (zclient->local_vni_add)
|
||||
(*zclient->local_vni_add)(command, zclient, length,
|
||||
|
@ -124,6 +124,8 @@ typedef enum {
|
||||
ZEBRA_ADVERTISE_DEFAULT_GW,
|
||||
ZEBRA_ADVERTISE_SUBNET,
|
||||
ZEBRA_ADVERTISE_ALL_VNI,
|
||||
ZEBRA_LOCAL_ES_ADD,
|
||||
ZEBRA_LOCAL_ES_DEL,
|
||||
ZEBRA_VNI_ADD,
|
||||
ZEBRA_VNI_DEL,
|
||||
ZEBRA_L3VNI_ADD,
|
||||
@ -237,6 +239,8 @@ struct zclient {
|
||||
int (*redistribute_route_del)(int, struct zclient *, uint16_t,
|
||||
vrf_id_t);
|
||||
int (*fec_update)(int, struct zclient *, uint16_t);
|
||||
int (*local_es_add)(int, struct zclient *, uint16_t, vrf_id_t);
|
||||
int (*local_es_del)(int, struct zclient *, uint16_t, vrf_id_t);
|
||||
int (*local_vni_add)(int, struct zclient *, uint16_t, vrf_id_t);
|
||||
int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
|
||||
int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t);
|
||||
|
Loading…
Reference in New Issue
Block a user