bgpd: advertise/withdraw type-5 routes upon user config

CLI config for enabling/disabling type-5 routes

router bgp <as> vrf <vrf>
  address-family l2vpn evpn
    [no] advertise <ipv4|ipv6|both>

loop through all the routes in VRF instance and advertise/withdraw
all ip routes as type-5 routes in default instance.

Signed-off-by: Mitesh Kanjariya <mitesh@cumulusnetworks.com>
This commit is contained in:
mitesh 2017-10-27 14:15:45 -07:00 committed by Mitesh Kanjariya
parent b67a60d2cf
commit 342dd0c623
7 changed files with 413 additions and 13 deletions

View File

@ -601,6 +601,52 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
return zclient_send_message(zclient);
}
/*
* Build extended communities for EVPN prefix route.
*/
static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
struct attr *attr)
{
struct ecommunity ecom_encap;
struct ecommunity ecom_rmac;
struct ecommunity_val eval;
struct ecommunity_val eval_rmac;
bgp_encap_types tnl_type;
struct listnode *node, *nnode;
struct ecommunity *ecom;
struct list *vrf_export_rtl = NULL;
/* Encap */
tnl_type = BGP_ENCAP_TYPE_VXLAN;
memset(&ecom_encap, 0, sizeof(ecom_encap));
encode_encap_extcomm(tnl_type, &eval);
ecom_encap.size = 1;
ecom_encap.val = (u_int8_t *)eval.val;
/* Add Encap */
attr->ecommunity = ecommunity_dup(&ecom_encap);
/* Add the export RTs for L3VNI/VRF */
vrf_export_rtl = bgp_vrf->vrf_export_rtl;
if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom))
attr->ecommunity = ecommunity_merge(attr->ecommunity,
ecom);
}
/* add the router mac extended community */
if (!is_zero_mac(&attr->rmac)) {
memset(&ecom_rmac, 0, sizeof(ecom_rmac));
encode_rmac_extcomm(&eval_rmac, &attr->rmac);
ecom_rmac.size = 1;
ecom_rmac.val = (uint8_t *)eval_rmac.val;
attr->ecommunity = ecommunity_merge(attr->ecommunity,
&ecom_rmac);
}
attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES);
}
/*
* Build extended communities for EVPN route. RT and ENCAP are
* applicable to all routes.
@ -907,6 +953,112 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn)
return local_ri->attr->sticky;
}
static int update_evpn_type5_route_entry(struct bgp *bgp_def,
struct bgp *bgp_vrf, afi_t afi,
safi_t safi, struct bgp_node *rn,
struct attr *attr)
{
struct attr *attr_new = NULL;
struct bgp_info *ri = NULL;
mpls_label_t label = MPLS_INVALID_LABEL;
struct bgp_info *local_ri = NULL;
struct bgp_info *tmp_ri = NULL;
/* locate the local route entry if any */
for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) {
if (tmp_ri->peer == bgp_def->peer_self
&& tmp_ri->type == ZEBRA_ROUTE_BGP
&& tmp_ri->sub_type == BGP_ROUTE_STATIC)
local_ri = tmp_ri;
}
/* create a new route entry if one doesnt exist.
Otherwise see if route attr has changed */
if (!local_ri) {
/* Add (or update) attribute to hash. */
attr_new = bgp_attr_intern(attr);
/* create the route info from attribute */
ri = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0,
bgp_def->peer_self, attr_new, rn);
SET_FLAG(ri->flags, BGP_INFO_VALID);
/* L3-VNI goes in the label2 field */
bgp_info_extra_get(ri);
vni2label(bgp_vrf->l3vni, &label);
memcpy(&ri->extra->label2, &label, BGP_LABEL_BYTES);
/* add the route entry to route node*/
bgp_info_add(rn, ri);
} else {
tmp_ri = local_ri;
if (!attrhash_cmp(tmp_ri->attr, attr)) {
/* The attribute has changed. */
/* Add (or update) attribute to hash. */
attr_new = bgp_attr_intern(attr);
bgp_info_set_flag(rn, tmp_ri, BGP_INFO_ATTR_CHANGED);
/* Restore route, if needed. */
if (CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
bgp_info_restore(rn, tmp_ri);
/* Unintern existing, set to new. */
bgp_attr_unintern(&tmp_ri->attr);
tmp_ri->attr = attr_new;
tmp_ri->uptime = bgp_clock();
}
}
return 0;
}
/* update evpn type-5 route entry */
static int update_evpn_type5_route(struct bgp *bgp_vrf,
struct prefix_evpn *evp)
{
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
struct attr attr;
struct bgp_node *rn = NULL;
struct bgp *bgp_def = NULL;
bgp_def = bgp_get_default();
if (!bgp_def)
return -1;
/* build path attribute for this route */
memset(&attr, 0, sizeof(struct attr));
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
attr.nexthop = bgp_vrf->originator_ip;
attr.mp_nexthop_global_in = bgp_vrf->originator_ip;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
memcpy(&attr.rmac, &bgp_vrf->rmac, sizeof(struct ethaddr));
/* Setup RT and encap extended community */
build_evpn_type5_route_extcomm(bgp_vrf, &attr);
/* get the route node in global table */
rn = bgp_afi_node_get(bgp_def->rib[afi][safi], afi, safi,
(struct prefix *)evp,
&bgp_vrf->vrf_prd);
assert(rn);
/* create or update the route entry within the route node */
update_evpn_type5_route_entry(bgp_def, bgp_vrf,
afi, safi,
rn, &attr);
/* schedule for processing and unlock node */
bgp_process(bgp_def, rn, afi, safi);
bgp_unlock_node(rn);
/* uninten temporary */
aspath_unintern(&attr.aspath);
return 0;
}
/*
* Create or update EVPN route entry. This could be in the VNI route table
* or the global route table.
@ -1089,6 +1241,58 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
return 0;
}
/* Delete EVPN type5 route entry from global table */
static void delete_evpn_type5_route_entry(struct bgp *bgp_def,
struct bgp *bgp_vrf,
afi_t afi, safi_t safi,
struct bgp_node *rn,
struct bgp_info **ri)
{
struct bgp_info *tmp_ri = NULL;
*ri = NULL;
/* find the matching route entry */
for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
if (tmp_ri->peer == bgp_def->peer_self
&& tmp_ri->type == ZEBRA_ROUTE_BGP
&& tmp_ri->sub_type == BGP_ROUTE_STATIC)
break;
*ri = tmp_ri;
/* Mark route for delete. */
if (tmp_ri)
bgp_info_delete(rn, tmp_ri);
}
/* Delete EVPN type5 route */
static int delete_evpn_type5_route(struct bgp *bgp_vrf,
struct prefix_evpn *evp)
{
afi_t afi = AFI_L2VPN;
safi_t safi = SAFI_EVPN;
struct bgp_node *rn = NULL;
struct bgp_info *ri = NULL;
struct bgp *bgp_def = NULL; /* default bgp instance */
bgp_def = bgp_get_default();
if (!bgp_def)
return -1;
/* locate the global route entry for this type-5 prefix */
rn = bgp_afi_node_lookup(bgp_def->rib[afi][safi], afi, safi,
(struct prefix *)evp, &bgp_vrf->vrf_prd);
if (!rn)
return 0;
delete_evpn_type5_route_entry(bgp_def, bgp_vrf, afi, safi, rn, &ri);
if (ri)
bgp_process(bgp_def, rn, afi, safi);
bgp_unlock_node(rn);
return 0;
}
/*
* Delete EVPN route entry. This could be in the VNI route table
* or the global route table.
@ -2804,6 +3008,58 @@ static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf)
* Public functions.
*/
/* withdraw all type-5 routes for an address family */
void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf,
afi_t afi)
{
struct bgp_table *table = NULL;
struct bgp_node *rn = NULL;
table = bgp_vrf->rib[afi][SAFI_UNICAST];
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
int ret = 0;
struct prefix_evpn evp;
char buf[PREFIX_STRLEN];
build_type5_prefix_from_ip_prefix(&evp, &rn->p);
ret = delete_evpn_type5_route(bgp_vrf, &evp);
if (ret) {
zlog_err(
"%u failed to delete type-5 route for prefix %s in vrf %s",
bgp_vrf->vrf_id,
prefix2str(&rn->p, buf, sizeof(buf)),
vrf_id_to_name(bgp_vrf->vrf_id));
}
}
}
/* advertise all type-5 routes for an address family */
void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf,
afi_t afi)
{
struct bgp_table *table = NULL;
struct bgp_node *rn = NULL;
table = bgp_vrf->rib[afi][SAFI_UNICAST];
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
int ret = 0;
struct prefix_evpn evp;
char buf[PREFIX_STRLEN];
build_type5_prefix_from_ip_prefix(&evp, &rn->p);
ret = update_evpn_type5_route(bgp_vrf, &evp);
if (ret) {
zlog_err(
"%u failed to create type-5 route for prefix %s in vrf %s",
bgp_vrf->vrf_id,
prefix2str(&rn->p, buf, sizeof(buf)),
vrf_id_to_name(bgp_vrf->vrf_id));
}
}
}
void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni,
struct list *rtl)
{
@ -3128,6 +3384,13 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len)
inet_ntop(family, &p->prefix.ip.ip.addr, buf2,
PREFIX2STR_BUFFER));
}
} else if (p->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
snprintf(buf, len, "[%d]:[0]:[%d]:[%s]",
p->prefix.route_type,
p->prefix.ip_prefix_length,
IS_EVPN_PREFIX_IPADDR_V4(p) ?
inet_ntoa(p->prefix.ip.ipaddr_v4) :
inet6_ntoa(p->prefix.ip.ipaddr_v6));
} else {
/* For EVPN route types not supported yet. */
snprintf(buf, len, "(unsupported route type %d)",

View File

@ -25,6 +25,8 @@
#define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */
extern void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi);
extern void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi);
extern void bgp_evpn_vrf_delete(struct bgp *);
extern void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw);
extern char *bgp_evpn_label2str(mpls_label_t *label, char *buf, int len);

View File

@ -33,6 +33,7 @@
/* EVPN prefix lengths. */
#define EVPN_TYPE_2_ROUTE_PREFIXLEN 224
#define EVPN_TYPE_3_ROUTE_PREFIXLEN 224
#define EVPN_TYPE_5_ROUTE_PREFIXLEN 168
/* EVPN route types. */
typedef enum {
@ -128,11 +129,14 @@ static inline int bgp_evpn_vrf_rd_matches_existing(struct bgp *bgp_vrf,
static inline int is_evpn_prefix_routes_adv_enabled(struct bgp *bgp_vrf)
{
if (!bgp_vrf->l3vni ||
!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_EVPN_PREFIX_ROUTE))
if (!bgp_vrf->l3vni)
return 0;
return 1;
if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV4_IN_EVPN) ||
CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_ADVERTISE_IPV6_IN_EVPN))
return 1;
return 0;
}
static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn)
@ -312,6 +316,31 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
memcpy(&p->prefix.ip, ip, sizeof(*ip));
}
static inline void build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp,
struct prefix *ip_prefix)
{
struct ipaddr ip;
memset(&ip, 0, sizeof(struct ipaddr));
if (ip_prefix->family == AF_INET) {
ip.ipa_type = IPADDR_V4;
memcpy(&ip.ipaddr_v4, &ip_prefix->u.prefix4,
sizeof(struct in_addr));
} else {
ip.ipa_type = IPADDR_V6;
memcpy(&ip.ipaddr_v6, &ip_prefix->u.prefix6,
sizeof(struct in6_addr));
}
memset(evp, 0, sizeof(struct prefix_evpn));
evp->family = AF_EVPN;
evp->prefixlen = EVPN_TYPE_5_ROUTE_PREFIXLEN;
evp->prefix.ip_prefix_length = ip_prefix->prefixlen;
evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
evp->prefix.ip.ipa_type = ip.ipa_type;
memcpy(&evp->prefix.ip, &ip, sizeof(struct ipaddr));
}
static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
struct in_addr originator_ip)
{

View File

@ -1978,11 +1978,11 @@ static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp,
/* RD header and legend - once overall. */
if (rd_header && !json) {
vty_out(vty,
"EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:"
"[MAC]\n");
"EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]\n");
vty_out(vty,
"EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:"
"[OrigIP]\n\n");
"EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
vty_out(vty,
"EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
rd_header = 0;
}
@ -2440,6 +2440,105 @@ DEFUN (no_bgp_evpn_advertise_all_vni,
return CMD_SUCCESS;
}
DEFUN (bgp_evpn_advertise_type5,
bgp_evpn_advertise_type5_cmd,
"advertise <ipv4|ipv6|both>",
"Advertise prefix routes\n"
"advertise ipv4 prefix only\n"
"advertise ipv6 prefix only\n"
"advertise both ipv4/ipv6 prefix\n")
{
struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
uint32_t type = 0;
if (strcmp(argv[1]->text, "ipv4")) {
type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN;
} else if (strcmp(argv[1]->text, "ipv6")) {
type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN;
} else if (strcmp(argv[1]->text, "both")) {
type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN |
BGP_VRF_ADVERTISE_IPV6_IN_EVPN;
} else {
vty_out(vty, "%%invalid command");
return CMD_WARNING;
}
if (CHECK_FLAG(type, BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
/* if we are already advertising ipv4 prefix as type-5
* nothing to do */
if (!CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
SET_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
bgp_evpn_advertise_type5_routes(bgp_vrf,
AFI_IP);
}
} else {
/* if we are already advertising ipv6 prefix as type-5
* nothing to do */
if (!CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) {
SET_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
bgp_evpn_advertise_type5_routes(bgp_vrf,
AFI_IP6);
}
}
return CMD_SUCCESS;
}
DEFUN (no_bgp_evpn_advertise_type5,
no_bgp_evpn_advertise_type5_cmd,
"no advertise <ipv4|ipv6|both>",
NO_STR
"Advertise prefix routes\n"
"advertise ipv4 prefix only\n"
"advertise ipv6 prefix only\n"
"advertise both ipv4/ipv6 prefix\n")
{
struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
uint32_t type = 0;
if (strcmp(argv[1]->text, "ipv4")) {
type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN;
} else if (strcmp(argv[1]->text, "ipv6")) {
type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN;
} else if (strcmp(argv[1]->text, "both")) {
type = BGP_VRF_ADVERTISE_IPV4_IN_EVPN |
BGP_VRF_ADVERTISE_IPV6_IN_EVPN;
} else {
vty_out(vty, "%%invalid command");
return CMD_WARNING;
}
if (CHECK_FLAG(type, BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
/* if we are already advertising ipv4 prefix as type-5
* nothing to do */
if (CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV4_IN_EVPN)) {
bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP);
UNSET_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV4_IN_EVPN);
}
} else {
/* if we are already advertising ipv6 prefix as type-5
* nothing to do */
if (CHECK_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV6_IN_EVPN)) {
bgp_evpn_withdraw_type5_routes(bgp_vrf,
AFI_IP6);
UNSET_FLAG(bgp_vrf->vrf_flags,
BGP_VRF_ADVERTISE_IPV6_IN_EVPN);
}
}
return CMD_SUCCESS;
}
/*
* Display VNI information - for all or a specific VNI
*/
@ -3955,6 +4054,8 @@ void bgp_ethernetvpn_init(void)
install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd);
install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_type5_cmd);
install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd);
/* "show bgp l2vpn evpn" commands. */
install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);

View File

@ -73,9 +73,12 @@ struct bgp_info_extra {
/* Nexthop reachability check. */
u_int32_t igpmetric;
/* MPLS label. */
/* MPLS label - L2VNI */
mpls_label_t label;
/* MPLS label - L3-VNI */
mpls_label_t label2;
#if ENABLE_BGP_VNC
union {

View File

@ -424,10 +424,11 @@ struct bgp {
/* vrf flags */
uint32_t vrf_flags;
#define BGP_VRF_AUTO (1 << 0)
#define BGP_VRF_ADVERTISE_EVPN_PREFIX_ROUTE (1 << 1)
#define BGP_VRF_IMPORT_RT_CFGD (1 << 2)
#define BGP_VRF_EXPORT_RT_CFGD (1 << 3)
#define BGP_VRF_RD_CFGD (1 << 4)
#define BGP_VRF_ADVERTISE_IPV4_IN_EVPN (1 << 1)
#define BGP_VRF_ADVERTISE_IPV6_IN_EVPN (1 << 2)
#define BGP_VRF_IMPORT_RT_CFGD (1 << 3)
#define BGP_VRF_EXPORT_RT_CFGD (1 << 4)
#define BGP_VRF_RD_CFGD (1 << 5)
/* unique ID for auto derivation of RD for this vrf */
uint16_t vrf_rd_id;

View File

@ -1043,10 +1043,11 @@ static const char *prefixevpn2str(const struct prefix *p, char *str, int size)
family = IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p)
? AF_INET
: AF_INET6;
snprintf(str, size, "[%d]:[%u][%s]/%d",
snprintf(str, size, "[%d]:[%u][%s/%d]/%d",
p->u.prefix_evpn.route_type, p->u.prefix_evpn.eth_tag,
inet_ntop(family, &p->u.prefix_evpn.ip.ip.addr, buf,
PREFIX2STR_BUFFER),
p->u.prefix_evpn.ip_prefix_length,
p->prefixlen);
} else {
sprintf(str, "Unsupported EVPN route type %d",