bgpd: advertise VNI subnet

In EVPN symmetric routing, not all subnets are presents everywhere.
We have multiple scenarios where a host might not get learned locally.
1. GARP miss
2. SVI down/up
3. Silent host

We need a mechanism to resolve such hosts. In order to achieve this,
we will be advertising a subnet route from a box and that box will help
in resolving the ARP to such hosts.

Signed-off-by: Mitesh Kanjariya <mitesh@cumulusnetworks.com>
This commit is contained in:
Mitesh Kanjariya 2017-11-19 21:47:04 -08:00 committed by mitesh
parent b57ba6d2a8
commit 31310b25f2
15 changed files with 342 additions and 18 deletions

View File

@ -3188,20 +3188,20 @@ static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf)
*/
/* withdraw type-5 route corresponding to ip prefix */
void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn,
void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, struct prefix *p,
afi_t afi, safi_t safi)
{
int ret = 0;
struct prefix_evpn evp;
char buf[PREFIX_STRLEN];
build_type5_prefix_from_ip_prefix(&evp, &rn->p);
build_type5_prefix_from_ip_prefix(&evp, 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)),
prefix2str(p, buf, sizeof(buf)),
vrf_id_to_name(bgp_vrf->vrf_id));
}
}
@ -3218,12 +3218,12 @@ void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf,
table = bgp_vrf->rib[afi][safi];
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn))
bgp_evpn_withdraw_type5_route(bgp_vrf, rn, afi, safi);
bgp_evpn_withdraw_type5_route(bgp_vrf, &rn->p, afi, safi);
}
/* advertise ip prefix as type-5 route*/
void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn,
void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct prefix *p,
afi_t afi, safi_t safi)
{
int ret = 0;
@ -3233,20 +3233,17 @@ void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct bgp_node *rn,
if (!advertise_type5_routes(bgp_vrf, afi))
return;
if (!rn->info)
return;
/* only advertise subnet routes as type-5 */
if (is_host_route(&rn->p))
if (is_host_route(p))
return;
build_type5_prefix_from_ip_prefix(&evp, &rn->p);
build_type5_prefix_from_ip_prefix(&evp, 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)),
prefix2str(p, buf, sizeof(buf)),
vrf_id_to_name(bgp_vrf->vrf_id));
}
}
@ -3259,8 +3256,12 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf,
struct bgp_node *rn = NULL;
table = bgp_vrf->rib[afi][safi];
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn))
bgp_evpn_advertise_type5_route(bgp_vrf, rn, afi, safi);
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
if (!rn->info)
continue;
bgp_evpn_advertise_type5_route(bgp_vrf, &rn->p, afi, safi);
}
}
void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni,

View File

@ -56,10 +56,10 @@ static inline vni_t label2vni(mpls_label_t *label)
}
extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf,
struct bgp_node *rn,
struct prefix *p,
afi_t afi, safi_t safi);
extern void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf,
struct bgp_node *rn,
struct prefix *p,
afi_t afi, safi_t safi);
extern void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi,
safi_t safi);

View File

@ -65,6 +65,9 @@ struct bgpevpn {
/* Flag to indicate if we are advertising the g/w mac ip for this VNI*/
u_int8_t advertise_gw_macip;
/* Flag to indicate if we are advertising subnet for this VNI */
u_int8_t advertise_subnet;
/* Id for deriving the RD automatically for this VNI */
u_int16_t rd_id;

View File

@ -2271,6 +2271,32 @@ static void evpn_unset_advertise_default_gw(struct bgp *bgp,
return;
}
/*
* evpn - enable advertisement of default g/w
*/
static void evpn_set_advertise_subnet(struct bgp *bgp,
struct bgpevpn *vpn)
{
if (vpn->advertise_subnet)
return;
vpn->advertise_subnet = 1;
bgp_zebra_advertise_subnet(bgp, vpn->advertise_subnet, vpn->vni);
}
/*
* evpn - disable advertisement of default g/w
*/
static void evpn_unset_advertise_subnet(struct bgp *bgp,
struct bgpevpn *vpn)
{
if (!vpn->advertise_subnet)
return;
vpn->advertise_subnet = 0;
bgp_zebra_advertise_subnet(bgp, vpn->advertise_subnet, vpn->vni);
}
/*
* EVPN (VNI advertisement) enabled. Register with zebra.
*/
@ -2330,6 +2356,9 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn)
if (vpn->advertise_gw_macip)
vty_out(vty, " advertise-default-gw\n");
if (vpn->advertise_subnet)
vty_out(vty, " advertise-subnet\n");
vty_out(vty, " exit-vni\n");
}
}
@ -2440,6 +2469,56 @@ DEFUN (no_bgp_evpn_advertise_all_vni,
return CMD_SUCCESS;
}
DEFUN (bgp_evpn_advertise_vni_subnet,
bgp_evpn_advertise_vni_subnet_cmd,
"advertise-subnet",
"Advertise the subnet corresponding to VNI\n")
{
struct bgp *bgp_vrf = NULL;
struct bgp *bgp = VTY_GET_CONTEXT(bgp);
VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
if (!bgp)
return CMD_WARNING;
if (!vpn)
return CMD_WARNING;
bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
if (!bgp_vrf)
return CMD_WARNING;
if (!(advertise_type5_routes(bgp_vrf, AFI_IP) ||
advertise_type5_routes(bgp_vrf, AFI_IP6))) {
vty_out(vty,
"%%Please enable ip prefix advertisement under l2vpn evpn in %s",
vrf_id_to_name(bgp_vrf->vrf_id));
return CMD_WARNING;
}
evpn_set_advertise_subnet(bgp, vpn);
return CMD_SUCCESS;
}
DEFUN (no_bgp_evpn_advertise_vni_subnet,
no_bgp_evpn_advertise_vni_subnet_cmd,
"no advertise-subnet",
NO_STR
"Advertise All local VNIs\n")
{
struct bgp *bgp = VTY_GET_CONTEXT(bgp);
VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
if (!bgp)
return CMD_WARNING;
if (!vpn)
return CMD_WARNING;
evpn_unset_advertise_subnet(bgp, vpn);
return CMD_SUCCESS;
}
DEFUN (bgp_evpn_advertise_type5,
bgp_evpn_advertise_type5_cmd,
"advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR,
@ -4118,5 +4197,8 @@ void bgp_ethernetvpn_init(void)
&bgp_evpn_advertise_default_gw_vni_cmd);
install_element(BGP_EVPN_VNI_NODE,
&no_bgp_evpn_advertise_default_gw_vni_cmd);
install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_vni_subnet_cmd);
install_element(BGP_EVPN_VNI_NODE,
&no_bgp_evpn_advertise_vni_subnet_cmd);
#endif
}

View File

@ -2226,9 +2226,9 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
/* advertise/withdraw type-5 routes */
if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
if (new_select)
bgp_evpn_advertise_type5_route(bgp, rn, afi, safi);
bgp_evpn_advertise_type5_route(bgp, &rn->p, afi, safi);
else if (old_select)
bgp_evpn_withdraw_type5_route(bgp, rn, afi, safi);
bgp_evpn_withdraw_type5_route(bgp, &rn->p, afi, safi);
}
/* Clear any route change flags. */

View File

@ -1635,6 +1635,29 @@ void bgp_zebra_terminate_radv(struct bgp *bgp, struct peer *peer)
zclient_send_interface_radv_req(zclient, bgp->vrf_id, peer->ifp, 0, 0);
}
int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise, vni_t vni)
{
struct stream *s = NULL;
/* 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_ADVERTISE_SUBNET, bgp->vrf_id);
stream_putc(s, advertise);
stream_put3(s, vni);
stream_putw_at(s, 0, stream_get_endp(s));
return zclient_send_message(zclient);
}
int bgp_zebra_advertise_gw_macip(struct bgp *bgp, int advertise, vni_t vni)
{
struct stream *s = NULL;
@ -1821,6 +1844,53 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient,
return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip);
}
static void bgp_zebra_process_local_ip_prefix(int cmd,
struct zclient *zclient,
zebra_size_t length,
vrf_id_t vrf_id)
{
struct stream *s = NULL;
struct bgp *bgp_vrf = NULL;
struct prefix p;
char buf[PREFIX_STRLEN];
memset(&p, 0, sizeof(struct prefix));
s = zclient->ibuf;
stream_get(&p, s, sizeof(struct prefix));
bgp_vrf = bgp_lookup_by_vrf_id(vrf_id);
if (!bgp_vrf)
return;
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("Recv prefix %s %s on vrf %s",
prefix2str(&p, buf, sizeof(buf)),
(cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL",
vrf_id_to_name(vrf_id));
if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) {
if (p.family == AF_INET)
return bgp_evpn_advertise_type5_route(bgp_vrf, &p,
AFI_IP,
SAFI_UNICAST);
else
return bgp_evpn_advertise_type5_route(bgp_vrf, &p,
AFI_IP6,
SAFI_UNICAST);
} else {
if (p.family == AF_INET)
return 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);
}
}
extern struct zebra_privs_t bgpd_privs;
void bgp_zebra_init(struct thread_master *master)
@ -1853,6 +1923,8 @@ void bgp_zebra_init(struct thread_master *master)
zclient->local_macip_del = bgp_zebra_process_local_macip;
zclient->local_l3vni_add = bgp_zebra_process_local_l3vni;
zclient->local_l3vni_del = bgp_zebra_process_local_l3vni;
zclient->local_ip_prefix_add = bgp_zebra_process_local_ip_prefix;
zclient->local_ip_prefix_del = bgp_zebra_process_local_ip_prefix;
}
void bgp_zebra_destroy(void)

View File

@ -58,7 +58,8 @@ extern struct interface *if_lookup_by_ipv6(struct in6_addr *, ifindex_t,
vrf_id_t);
extern struct interface *if_lookup_by_ipv6_exact(struct in6_addr *, ifindex_t,
vrf_id_t);
extern int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise,
vni_t vni);
extern int bgp_zebra_advertise_gw_macip(struct bgp *, int, vni_t);
extern int bgp_zebra_advertise_all_vni(struct bgp *, int);

View File

@ -943,6 +943,7 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_RELEASE_LABEL_CHUNK),
DESC_ENTRY(ZEBRA_ADVERTISE_ALL_VNI),
DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW),
DESC_ENTRY(ZEBRA_ADVERTISE_SUBNET),
DESC_ENTRY(ZEBRA_VNI_ADD),
DESC_ENTRY(ZEBRA_VNI_DEL),
DESC_ENTRY(ZEBRA_L3VNI_ADD),
@ -951,6 +952,8 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_REMOTE_VTEP_DEL),
DESC_ENTRY(ZEBRA_MACIP_ADD),
DESC_ENTRY(ZEBRA_MACIP_DEL),
DESC_ENTRY(ZEBRA_IP_PREFIX_ROUTE_ADD),
DESC_ENTRY(ZEBRA_IP_PREFIX_ROUTE_DEL),
DESC_ENTRY(ZEBRA_REMOTE_MACIP_ADD),
DESC_ENTRY(ZEBRA_REMOTE_MACIP_DEL),
DESC_ENTRY(ZEBRA_PW_ADD),

View File

@ -2234,6 +2234,16 @@ static int zclient_read(struct thread *thread)
(*zclient->local_macip_del)(command, zclient, length,
vrf_id);
break;
case ZEBRA_IP_PREFIX_ROUTE_ADD:
if (zclient->local_ip_prefix_add)
(*zclient->local_ip_prefix_add)(command, zclient,
length, vrf_id);
break;
case ZEBRA_IP_PREFIX_ROUTE_DEL:
if (zclient->local_ip_prefix_del)
(*zclient->local_ip_prefix_del)(command, zclient,
length, vrf_id);
break;
case ZEBRA_PW_STATUS_UPDATE:
if (zclient->pw_status_update)
(*zclient->pw_status_update)(command, zclient, length,

View File

@ -109,6 +109,7 @@ typedef enum {
ZEBRA_FEC_UNREGISTER,
ZEBRA_FEC_UPDATE,
ZEBRA_ADVERTISE_DEFAULT_GW,
ZEBRA_ADVERTISE_SUBNET,
ZEBRA_ADVERTISE_ALL_VNI,
ZEBRA_VNI_ADD,
ZEBRA_VNI_DEL,
@ -118,6 +119,8 @@ typedef enum {
ZEBRA_REMOTE_VTEP_DEL,
ZEBRA_MACIP_ADD,
ZEBRA_MACIP_DEL,
ZEBRA_IP_PREFIX_ROUTE_ADD,
ZEBRA_IP_PREFIX_ROUTE_DEL,
ZEBRA_REMOTE_MACIP_ADD,
ZEBRA_REMOTE_MACIP_DEL,
ZEBRA_PW_ADD,
@ -204,6 +207,8 @@ struct zclient {
int (*local_vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_l3vni_add)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_l3vni_del)(int, struct zclient *, uint16_t, vrf_id_t);
void (*local_ip_prefix_add)(int, struct zclient *, uint16_t, vrf_id_t);
void (*local_ip_prefix_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_add)(int, struct zclient *, uint16_t, vrf_id_t);
int (*local_macip_del)(int, struct zclient *, uint16_t, vrf_id_t);
int (*pw_status_update)(int, struct zclient *, uint16_t, vrf_id_t);

View File

@ -59,6 +59,9 @@ DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor");
/* static function declarations */
static int ip_prefix_send_to_client(vrf_id_t vrf_id,
struct prefix *p,
uint16_t cmd);
static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json);
static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt);
static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
@ -1710,7 +1713,37 @@ static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni)
zvni_gw_macip_add(ifp, zvni, &macaddr, &ip);
}
return 0;
}
static int zvni_advertise_subnet(zebra_vni_t *zvni,
struct interface *ifp,
int advertise)
{
struct listnode *cnode = NULL, *cnnode = NULL;
struct connected *c = NULL;
struct ethaddr macaddr;
memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
struct prefix p;
memcpy(&p, c->address, sizeof(struct prefix));
/* skip link local address */
if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6))
continue;
apply_mask(&p);
if (advertise)
ip_prefix_send_to_client(ifp->vrf_id, &p,
ZEBRA_IP_PREFIX_ROUTE_ADD);
else
ip_prefix_send_to_client(ifp->vrf_id, &p,
ZEBRA_IP_PREFIX_ROUTE_DEL);
}
return 0;
}
@ -3749,6 +3782,43 @@ static void zl3vni_del_nh_hash_entry(struct hash_backet *backet,
zl3vni_nh_del(zl3vni, n);
}
static int ip_prefix_send_to_client(vrf_id_t vrf_id,
struct prefix *p,
uint16_t cmd)
{
struct zserv *client = NULL;
struct stream *s = NULL;
char buf[PREFIX_STRLEN];
client = zebra_find_client(ZEBRA_ROUTE_BGP);
/* BGP may not be running. */
if (!client)
return 0;
s = client->obuf;
stream_reset(s);
zserv_create_header(s, cmd, vrf_id);
stream_put(s, p, sizeof(struct prefix));
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
"Send ip prefix %s %s on vrf %s",
prefix2str(p, buf, sizeof(buf)),
(cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL",
vrf_id_to_name(vrf_id));
if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD)
client->prefixadd_cnt++;
else
client->prefixdel_cnt++;
return zebra_server_send_message(client);
}
/* Public functions */
/* handle evpn route in vrf table */
@ -6461,6 +6531,73 @@ int zebra_vxlan_vrf_delete(struct zebra_vrf *zvrf)
return 0;
}
/*
* Handle message from client to enable/disable advertisement of g/w macip
* routes
*/
int zebra_vxlan_advertise_subnet(struct zserv *client, u_short length,
struct zebra_vrf *zvrf)
{
struct stream *s;
int advertise;
vni_t vni = 0;
zebra_vni_t *zvni = NULL;
struct interface *ifp = NULL;
struct zebra_if *zif = NULL;
struct zebra_l2info_vxlan zl2_info;
struct interface *vlan_if = NULL;
if (zvrf_id(zvrf) != VRF_DEFAULT) {
zlog_err("EVPN GW-MACIP Adv for non-default VRF %u",
zvrf_id(zvrf));
return -1;
}
s = client->ibuf;
advertise = stream_getc(s);
vni = stream_get3(s);
zvni = zvni_lookup(vni);
if (!zvni)
return 0;
if (zvni->advertise_subnet == advertise)
return 0;
if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug(
"EVPN subnet Adv %s on VNI %d , currently %s",
advertise ? "enabled" : "disabled", vni,
zvni->advertise_subnet ? "enabled" : "disabled");
zvni->advertise_subnet = advertise;
ifp = zvni->vxlan_if;
if (!ifp)
return 0;
zif = ifp->info;
/* If down or not mapped to a bridge, we're done. */
if (!if_is_operative(ifp) || !zif->brslave_info.br_if)
return 0;
zl2_info = zif->l2info.vxl;
vlan_if = zvni_map_to_svi(zl2_info.access_vlan,
zif->brslave_info.br_if);
if (!vlan_if)
return 0;
if (zvni->advertise_subnet)
zvni_advertise_subnet(zvni, vlan_if, 1);
else
zvni_advertise_subnet(zvni, vlan_if, 0);
return 0;
}
/*
* Handle message from client to enable/disable advertisement of g/w macip
* routes

View File

@ -140,6 +140,8 @@ extern int zebra_vxlan_remote_vtep_add(struct zserv *client,
u_short length, struct zebra_vrf *zvrf);
extern int zebra_vxlan_remote_vtep_del(struct zserv *client,
u_short length, struct zebra_vrf *zvrf);
extern int zebra_vxlan_advertise_subnet(struct zserv *client, u_short length,
struct zebra_vrf *zvrf);
extern int zebra_vxlan_advertise_gw_macip(struct zserv *client,
u_short length,
struct zebra_vrf *zvrf);

View File

@ -70,6 +70,9 @@ struct zebra_vni_t_ {
/* Flag for advertising gw macip */
u_int8_t advertise_gw_macip;
/* Flag for advertising gw macip */
u_int8_t advertise_subnet;
/* Corresponding VxLAN interface. */
struct interface *vxlan_if;

View File

@ -2603,6 +2603,9 @@ static inline void zserv_handle_commands(struct zserv *client,
case ZEBRA_ADVERTISE_DEFAULT_GW:
zebra_vxlan_advertise_gw_macip(client, length, zvrf);
break;
case ZEBRA_ADVERTISE_SUBNET:
zebra_vxlan_advertise_subnet(client, length, zvrf);
break;
case ZEBRA_ADVERTISE_ALL_VNI:
zebra_vxlan_advertise_all_vni(client, length, zvrf);
break;

View File

@ -114,6 +114,8 @@ struct zserv {
u_int32_t l3vnidel_cnt;
u_int32_t macipadd_cnt;
u_int32_t macipdel_cnt;
u_int32_t prefixadd_cnt;
u_int32_t prefixdel_cnt;
time_t connect_time;
time_t last_read_time;