Merge pull request #2873 from vivek-cumulus/evpn-extended-mobility

EVPN extended mobility support
This commit is contained in:
Russ White 2018-09-05 18:09:31 -04:00 committed by GitHub
commit 7f0f6e7b75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 921 additions and 752 deletions

View File

@ -543,7 +543,7 @@ static void evpn_convert_nexthop_to_ipv6(struct attr *attr)
static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
struct prefix_evpn *p,
struct in_addr remote_vtep_ip, int add,
uint8_t flags)
uint8_t flags, uint32_t seq)
{
struct stream *s;
int ipa_len;
@ -579,19 +579,22 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
stream_put_in_addr(s, &remote_vtep_ip);
/* TX flags - MAC sticky status and/or gateway mac */
if (add)
/* Also TX the sequence number of the best route. */
if (add) {
stream_putc(s, flags);
stream_putl(s, seq);
}
stream_putw_at(s, 0, stream_get_endp(s));
if (bgp_debug_zebra(NULL))
zlog_debug(
"Tx %s MACIP, VNI %u MAC %s IP %s (flags: 0x%x) remote VTEP %s",
"Tx %s MACIP, VNI %u MAC %s IP %s flags 0x%x seq %u remote VTEP %s",
add ? "ADD" : "DEL", vpn->vni,
prefix_mac2str(&p->prefix.macip_addr.mac,
buf1, sizeof(buf1)),
ipaddr2str(&p->prefix.macip_addr.ip,
buf3, sizeof(buf3)), flags,
buf3, sizeof(buf3)), flags, seq,
inet_ntop(AF_INET, &remote_vtep_ip, buf2,
sizeof(buf2)));
@ -875,13 +878,14 @@ static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr)
/* Install EVPN route into zebra. */
static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn,
struct prefix_evpn *p,
struct in_addr remote_vtep_ip, uint8_t flags)
struct in_addr remote_vtep_ip, uint8_t flags,
uint32_t seq)
{
int ret;
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
ret = bgp_zebra_send_remote_macip(bgp, vpn, p, remote_vtep_ip,
1, flags);
1, flags, seq);
else
ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, 1);
@ -897,7 +901,7 @@ static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn,
if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
ret = bgp_zebra_send_remote_macip(bgp, vpn, p, remote_vtep_ip,
0, 0);
0, 0, 0);
else
ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, 0);
@ -1141,7 +1145,8 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
ret = evpn_zebra_install(
bgp, vpn, (struct prefix_evpn *)&rn->p,
old_select->attr->nexthop, flags);
old_select->attr->nexthop, flags,
mac_mobility_seqnum(old_select->attr));
}
UNSET_FLAG(old_select->flags, BGP_INFO_MULTIPATH_CHG);
bgp_zebra_clear_route_change_flags(rn);
@ -1177,7 +1182,8 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
ret = evpn_zebra_install(bgp, vpn, (struct prefix_evpn *)&rn->p,
new_select->attr->nexthop, flags);
new_select->attr->nexthop, flags,
mac_mobility_seqnum(new_select->attr));
/* If an old best existed and it was a "local" route, the only
* reason
* it would be supplanted is due to MAC mobility procedures. So,
@ -1557,11 +1563,12 @@ static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
*/
static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
afi_t afi, safi_t safi, struct bgp_node *rn,
struct attr *attr, int add, int vni_table,
struct bgp_info **ri, uint8_t flags)
struct attr *attr, int add,
struct bgp_info **ri, uint8_t flags,
uint32_t seq)
{
struct bgp_info *tmp_ri;
struct bgp_info *local_ri, *remote_ri;
struct bgp_info *local_ri;
struct attr *attr_new;
mpls_label_t label[BGP_MAX_LABELS];
uint32_t num_labels = 1;
@ -1573,28 +1580,13 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
evp = (struct prefix_evpn *)&rn->p;
memset(&label, 0, sizeof(label));
/* See if this is an update of an existing route, or a new add. Also,
* identify if already known from remote, and if so, the one with the
* highest sequence number; this is only when adding to the VNI routing
* table.
*/
local_ri = remote_ri = NULL;
/* See if this is an update of an existing route, or a new add. */
local_ri = NULL;
for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) {
if (tmp_ri->peer == bgp->peer_self
&& tmp_ri->type == ZEBRA_ROUTE_BGP
&& tmp_ri->sub_type == BGP_ROUTE_STATIC)
local_ri = tmp_ri;
if (vni_table) {
if (tmp_ri->type == ZEBRA_ROUTE_BGP
&& tmp_ri->sub_type == BGP_ROUTE_IMPORTED
&& CHECK_FLAG(tmp_ri->flags, BGP_INFO_VALID)) {
if (!remote_ri)
remote_ri = tmp_ri;
else if (mac_mobility_seqnum(tmp_ri->attr)
> mac_mobility_seqnum(remote_ri->attr))
remote_ri = tmp_ri;
}
}
}
/* If route doesn't exist already, create a new one, if told to.
@ -1604,22 +1596,11 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
if (!local_ri && !add)
return 0;
/* For non-GW MACs, update MAC mobility seq number, if needed. */
if (seq && !CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW))
add_mac_mobility_to_attr(seq, attr);
if (!local_ri) {
/* When learnt locally for the first time but already known from
* remote, we have to initiate appropriate MAC mobility steps.
* This is applicable when updating the VNI routing table.
* We need to skip mobility steps for g/w macs (local mac on g/w
* SVI) advertised in EVPN.
* This will ensure that local routes are preferred for g/w macs
*/
if (remote_ri && !CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW)) {
uint32_t cur_seqnum;
/* Add MM extended community to route. */
cur_seqnum = mac_mobility_seqnum(remote_ri->attr);
add_mac_mobility_to_attr(cur_seqnum + 1, attr);
}
/* Add (or update) attribute to hash. */
attr_new = bgp_attr_intern(attr);
@ -1685,6 +1666,11 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
attr_new = bgp_attr_intern(attr);
bgp_info_set_flag(rn, tmp_ri, BGP_INFO_ATTR_CHANGED);
/* Extract MAC mobility sequence number, if any. */
attr_new->mm_seqnum =
bgp_attr_mac_mobility_seqnum(attr_new, &sticky);
attr_new->sticky = sticky;
/* Restore route, if needed. */
if (CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
bgp_info_restore(rn, tmp_ri);
@ -1706,7 +1692,8 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
* and schedule for processing.
*/
static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
struct prefix_evpn *p, uint8_t flags)
struct prefix_evpn *p, uint8_t flags,
uint32_t seq)
{
struct bgp_node *rn;
struct attr attr;
@ -1758,7 +1745,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
/* Create or update route entry. */
route_change = update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr,
1, 1, &ri, flags);
1, &ri, flags, seq);
assert(ri);
attr_new = ri->attr;
@ -1778,8 +1765,8 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
rn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi,
(struct prefix *)p, &vpn->prd);
update_evpn_route_entry(bgp, vpn, afi, safi, rn, attr_new, 1, 0,
&global_ri, flags);
update_evpn_route_entry(bgp, vpn, afi, safi, rn, attr_new, 1,
&global_ri, flags, seq);
/* Schedule for processing and unlock node. */
bgp_process(bgp, rn, afi, safi);
@ -1954,58 +1941,14 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
afi_t afi;
safi_t safi;
struct bgp_node *rn;
struct bgp_info *ri;
struct bgp_info *ri, *tmp_ri;
struct attr attr;
struct attr attr_sticky;
struct attr attr_def_gw;
struct attr attr_ip6_ll;
struct attr *attr_new;
uint32_t seq;
int add_l3_ecomm = 0;
afi = AFI_L2VPN;
safi = SAFI_EVPN;
memset(&attr, 0, sizeof(struct attr));
memset(&attr_sticky, 0, sizeof(struct attr));
memset(&attr_def_gw, 0, sizeof(struct attr));
memset(&attr_ip6_ll, 0, sizeof(struct attr));
/* Build path-attribute - multiple type-2 routes for this VNI will share
* the same path attribute, but we need separate structures for sticky
* MACs, default gateway and IPv6 link-local addresses (no L3 RT/RMAC).
*/
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
bgp_attr_default_set(&attr_sticky, BGP_ORIGIN_IGP);
bgp_attr_default_set(&attr_def_gw, BGP_ORIGIN_IGP);
attr.nexthop = vpn->originator_ip;
attr.mp_nexthop_global_in = vpn->originator_ip;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
bgpevpn_get_rmac(vpn, &attr.rmac);
attr_sticky.nexthop = vpn->originator_ip;
attr_sticky.mp_nexthop_global_in = vpn->originator_ip;
attr_sticky.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
attr_sticky.sticky = 1;
bgpevpn_get_rmac(vpn, &attr_sticky.rmac);
attr_def_gw.nexthop = vpn->originator_ip;
attr_def_gw.mp_nexthop_global_in = vpn->originator_ip;
attr_def_gw.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
attr_def_gw.default_gw = 1;
bgpevpn_get_rmac(vpn, &attr_def_gw.rmac);
bgp_attr_default_set(&attr_ip6_ll, BGP_ORIGIN_IGP);
attr_ip6_ll.nexthop = vpn->originator_ip;
attr_ip6_ll.mp_nexthop_global_in = vpn->originator_ip;
attr_ip6_ll.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
/* Add L3 VNI RTs and RMAC for non IPv6 link-local attributes if
* using L3 VNI for type-2 routes also.
*/
if (CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) &&
bgpevpn_get_l3vni(vpn))
add_l3_ecomm = 1;
build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm);
build_evpn_route_extcomm(vpn, &attr_sticky, add_l3_ecomm);
build_evpn_route_extcomm(vpn, &attr_def_gw, add_l3_ecomm);
build_evpn_route_extcomm(vpn, &attr_ip6_ll, 0);
/* Walk this VNI's route table and update local type-2 routes. For any
* routes updated, update corresponding entry in the global table too.
@ -2019,35 +1962,57 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
continue;
if (is_evpn_prefix_ipaddr_v6(evp) &&
IN6_IS_ADDR_LINKLOCAL(&evp->prefix.macip_addr.ip.ipaddr_v6))
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
&attr_ip6_ll, 0, 1, &ri, 0);
else {
if (evpn_route_is_sticky(bgp, rn))
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
&attr_sticky, 0, 1, &ri,
0);
else if (evpn_route_is_def_gw(bgp, rn)) {
if (is_evpn_prefix_ipaddr_v6(evp))
attr_def_gw.router_flag = 1;
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
&attr_def_gw, 0, 1, &ri,
0);
} else
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
&attr, 0, 1, &ri, 0);
/* Identify local route. */
for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next) {
if (tmp_ri->peer == bgp->peer_self
&& tmp_ri->type == ZEBRA_ROUTE_BGP
&& tmp_ri->sub_type == BGP_ROUTE_STATIC)
break;
}
/* If a local route exists for this prefix, we need to update
* the global routing table too.
*/
if (!ri)
if (!tmp_ri)
continue;
/*
* Build attribute per local route as the MAC mobility and
* some other values could differ for different routes. The
* attributes will be shared in the hash table.
*/
bgp_attr_default_set(&attr, BGP_ORIGIN_IGP);
attr.nexthop = vpn->originator_ip;
attr.mp_nexthop_global_in = vpn->originator_ip;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
bgpevpn_get_rmac(vpn, &attr.rmac);
if (evpn_route_is_sticky(bgp, rn))
attr.sticky = 1;
else if (evpn_route_is_def_gw(bgp, rn)) {
attr.default_gw = 1;
if (is_evpn_prefix_ipaddr_v6(evp))
attr.router_flag = 1;
}
/* Add L3 VNI RTs and RMAC for non IPv6 link-local if
* using L3 VNI for type-2 routes also.
*/
if ((is_evpn_prefix_ipaddr_v4(evp) ||
!IN6_IS_ADDR_LINKLOCAL(
&evp->prefix.macip_addr.ip.ipaddr_v6)) &&
CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS) &&
bgpevpn_get_l3vni(vpn))
add_l3_ecomm = 1;
/* Set up extended community. */
build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm);
seq = mac_mobility_seqnum(tmp_ri->attr);
/* Update the route entry. */
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
&attr, 0, &ri, 0, seq);
/* Perform route selection; this is just to set the flags
* correctly
* as local route in the VNI always wins.
* correctly as local route in the VNI always wins.
*/
evpn_route_select_install(bgp, vpn, rn);
@ -2058,18 +2023,17 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
(struct prefix *)evp, &vpn->prd);
assert(rd_rn);
update_evpn_route_entry(bgp, vpn, afi, safi, rd_rn, attr_new, 0,
0, &global_ri, 0);
&global_ri, 0,
mac_mobility_seqnum(attr_new));
/* Schedule for processing and unlock node. */
bgp_process(bgp, rd_rn, afi, safi);
bgp_unlock_node(rd_rn);
}
/* Unintern temporary. */
aspath_unintern(&attr.aspath);
aspath_unintern(&attr_sticky.aspath);
aspath_unintern(&attr_def_gw.aspath);
aspath_unintern(&attr_ip6_ll.aspath);
/* Unintern temporary. */
aspath_unintern(&attr.aspath);
}
return 0;
}
@ -2206,7 +2170,7 @@ static int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
* locally learnt type-2 routes (MACIP) - for this VNI.
*/
build_evpn_type3_prefix(&p, vpn->originator_ip);
ret = update_evpn_route(bgp, vpn, &p, 0);
ret = update_evpn_route(bgp, vpn, &p, 0, 0);
if (ret)
return ret;
@ -3451,8 +3415,8 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
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, 0, &ri,
0);
update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr, 1, &ri,
0, mac_mobility_seqnum(attr));
/* Schedule for processing and unlock node. */
bgp_process(bgp, global_rn, afi, safi);
@ -3485,7 +3449,8 @@ static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
(struct prefix *)evp, &vpn->prd);
assert(global_rn);
update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr, 1,
0, &global_ri, 0);
&global_ri, 0,
mac_mobility_seqnum(attr));
/* Schedule for processing and unlock node. */
bgp_process(bgp, global_rn, afi, safi);
@ -5213,7 +5178,7 @@ int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
* Handle add of a local MACIP.
*/
int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
struct ipaddr *ip, uint8_t flags)
struct ipaddr *ip, uint8_t flags, uint32_t seq)
{
struct bgpevpn *vpn;
struct prefix_evpn p;
@ -5228,7 +5193,7 @@ int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
/* Create EVPN type-2 route and schedule for processing. */
build_evpn_type2_prefix(&p, mac, ip);
if (update_evpn_route(bgp, vpn, &p, flags)) {
if (update_evpn_route(bgp, vpn, &p, flags, seq)) {
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
@ -5525,7 +5490,7 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
/* 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)) {
if (update_evpn_route(bgp, vpn, &p, 0, 0)) {
flog_err(BGP_ERR_EVPN_ROUTE_CREATE,
"%u: Type3 route creation failure for VNI %u",
bgp->vrf_id, vni);

View File

@ -132,7 +132,7 @@ extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni,
struct ethaddr *mac, struct ipaddr *ip);
extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
struct ethaddr *mac, struct ipaddr *ip,
uint8_t flags);
uint8_t flags, uint32_t seq);
extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
struct ethaddr *rmac,
struct in_addr originator_ip, int filter);

View File

@ -2380,7 +2380,8 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient,
int ipa_len;
char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
uint8_t flags;
uint8_t flags = 0;
uint32_t seqnum = 0;
memset(&ip, 0, sizeof(ip));
s = zclient->ibuf;
@ -2401,20 +2402,24 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient,
(ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6;
stream_get(&ip.ip.addr, s, ipa_len);
}
flags = stream_getc(s);
if (command == ZEBRA_MACIP_ADD) {
flags = stream_getc(s);
seqnum = stream_getl(s);
}
bgp = bgp_lookup_by_vrf_id(vrf_id);
if (!bgp)
return 0;
if (BGP_DEBUG(zebra, ZEBRA))
zlog_debug("%u:Recv MACIP %s flags 0x%x MAC %s IP %s VNI %u",
zlog_debug("%u:Recv MACIP %s flags 0x%x MAC %s IP %s VNI %u seq %u",
vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del",
flags, prefix_mac2str(&mac, buf, sizeof(buf)),
ipaddr2str(&ip, buf1, sizeof(buf1)), vni);
ipaddr2str(&ip, buf1, sizeof(buf1)), vni, seqnum);
if (command == ZEBRA_MACIP_ADD)
return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, flags);
return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip,
flags, seqnum);
else
return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip);
}

File diff suppressed because it is too large Load Diff

View File

@ -260,6 +260,10 @@ struct zebra_mac_t_ {
struct in_addr r_vtep_ip;
} fwd_info;
/* Mobility sequence numbers associated with this entry. */
uint32_t rem_seq;
uint32_t loc_seq;
/* List of neigh associated with this mac */
struct list *neigh_list;
@ -338,6 +342,16 @@ struct zebra_neigh_t_ {
/* Remote VTEP IP - applicable only for remote neighbors. */
struct in_addr r_vtep_ip;
/*
* Mobility sequence numbers associated with this entry. The rem_seq
* represents the sequence number from the client (BGP) for the most
* recent add or update of this entry while the loc_seq represents
* the sequence number informed (or to be informed) by zebra to BGP
* for this entry.
*/
uint32_t rem_seq;
uint32_t loc_seq;
/* list of hosts pointing to this remote NH entry */
struct host_rb_tree_entry host_rb;
};