summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_attr.c81
-rw-r--r--bgpd/bgp_attr_evpn.c2
-rw-r--r--bgpd/bgp_evpn.c90
-rw-r--r--bgpd/bgp_evpn.h2
-rw-r--r--bgpd/bgp_evpn_private.h7
-rw-r--r--bgpd/bgp_evpn_vty.c504
-rw-r--r--bgpd/bgp_route.c33
-rw-r--r--bgpd/bgp_routemap.c158
-rw-r--r--bgpd/bgp_vty.c2
-rw-r--r--bgpd/bgp_zebra.c36
-rw-r--r--bgpd/bgp_zebra.h3
-rw-r--r--bgpd/bgpd.h6
-rw-r--r--ldpd/ldp_vty_cmds.c18
-rw-r--r--lib/command.c1
-rw-r--r--lib/command.h1
-rw-r--r--lib/filter.c212
-rw-r--r--lib/log.c1
-rw-r--r--lib/prefix.c704
-rw-r--r--lib/prefix.h16
-rw-r--r--lib/zclient.h5
-rwxr-xr-xtools/frr-reload.py40
-rw-r--r--vtysh/vtysh.c8
-rw-r--r--vtysh/vtysh_config.c11
-rw-r--r--zebra/if_netlink.c13
-rw-r--r--zebra/interface.h14
-rw-r--r--zebra/redistribute.c5
-rw-r--r--zebra/zebra_mpls.c8
-rw-r--r--zebra/zebra_vrf.h5
-rw-r--r--zebra/zebra_vty.c92
-rw-r--r--zebra/zebra_vxlan.c1561
-rw-r--r--zebra/zebra_vxlan.h34
-rw-r--r--zebra/zebra_vxlan_private.h34
-rw-r--r--zebra/zserv.c3
33 files changed, 2843 insertions, 867 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index dd18797637..b03b408f7d 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -2606,10 +2606,9 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
stream_putc(s, pkt_safi); /* SAFI */
/* Nexthop AFI */
- if (afi == AFI_IP && safi == SAFI_UNICAST) {
+ if (afi == AFI_IP
+ && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST))
nh_afi = peer_cap_enhe(peer, afi, safi) ? AFI_IP6 : AFI_IP;
- } else if (safi == SAFI_LABELED_UNICAST)
- nh_afi = afi;
else
nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len);
@@ -2800,9 +2799,8 @@ static void bgp_packet_mpattr_tea(struct bgp *bgp, struct peer *peer,
if (attrlenfield > 0xff) {
/* 2-octet length field */
- stream_putc(s,
- BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc(s, attrtype);
stream_putw(s, attrlenfield & 0xffff);
} else {
@@ -3040,15 +3038,14 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
&& (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) {
if (attr->community->size * 4 > 255) {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
- | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc(s, BGP_ATTR_COMMUNITIES);
stream_putw(s, attr->community->size * 4);
} else {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_COMMUNITIES);
stream_putc(s, attr->community->size * 4);
}
@@ -3062,15 +3059,14 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
PEER_FLAG_SEND_LARGE_COMMUNITY)
&& (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))) {
if (attr->lcommunity->size * 12 > 255) {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
- | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
stream_putw(s, attr->lcommunity->size * 12);
} else {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
stream_putc(s, attr->lcommunity->size * 12);
}
@@ -3122,16 +3118,14 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
if (peer->sort == BGP_PEER_IBGP
|| peer->sort == BGP_PEER_CONFED) {
if (attr->ecommunity->size * 8 > 255) {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_TRANS
- | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc(s, BGP_ATTR_EXT_COMMUNITIES);
stream_putw(s, attr->ecommunity->size * 8);
} else {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_EXT_COMMUNITIES);
stream_putc(s, attr->ecommunity->size * 8);
}
@@ -3197,9 +3191,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
label_index = attr->label_index;
if (label_index != BGP_INVALID_LABEL_INDEX) {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_PREFIX_SID);
stream_putc(s, 10);
stream_putc(s, BGP_PREFIX_SID_LABEL_INDEX);
@@ -3227,9 +3220,8 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
*/
aspath = aspath_delete_confed_seq(aspath);
- stream_putc(s,
- BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc(s, BGP_ATTR_AS4_PATH);
aspath_sizep = stream_get_endp(s);
stream_putw(s, 0);
@@ -3414,15 +3406,14 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
/* Community attribute. */
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) {
if (attr->community->size * 4 > 255) {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
- | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc(s, BGP_ATTR_COMMUNITIES);
stream_putw(s, attr->community->size * 4);
} else {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_COMMUNITIES);
stream_putc(s, attr->community->size * 4);
}
@@ -3432,15 +3423,14 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
/* Large Community attribute. */
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) {
if (attr->lcommunity->size * 12 > 255) {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
- | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
stream_putw(s, attr->lcommunity->size * 12);
} else {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
stream_putc(s, attr->lcommunity->size * 12);
}
@@ -3485,9 +3475,8 @@ void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
/* Prefix SID */
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) {
if (attr->label_index != BGP_INVALID_LABEL_INDEX) {
- stream_putc(s,
- BGP_ATTR_FLAG_OPTIONAL
- | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
stream_putc(s, BGP_ATTR_PREFIX_SID);
stream_putc(s, 10);
stream_putc(s, BGP_PREFIX_SID_LABEL_INDEX);
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c
index 2f0b566ccf..6ead059261 100644
--- a/bgpd/bgp_attr_evpn.c
+++ b/bgpd/bgp_attr_evpn.c
@@ -169,7 +169,7 @@ extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag,
prefix_copy(src, dst);
memset(dst, 0, sizeof(struct prefix));
p_evpn_p = &(dst->u.prefix_evpn);
- dst->family = AF_ETHERNET;
+ dst->family = AF_EVPN;
p_evpn_p->route_type = evpn_type;
if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
p_evpn_p->eth_tag = eth_tag;
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index fe311832a2..0560dc46f9 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -347,9 +347,9 @@ static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
s = zclient->obuf;
stream_reset(s);
- zclient_create_header(
- s, add ? ZEBRA_REMOTE_MACIP_ADD : ZEBRA_REMOTE_MACIP_DEL,
- bgp->vrf_id);
+ zclient_create_header(s, add ? ZEBRA_REMOTE_MACIP_ADD
+ : ZEBRA_REMOTE_MACIP_DEL,
+ bgp->vrf_id);
stream_putl(s, vpn->vni);
stream_put(s, &p->prefix.mac.octet, ETH_ALEN); /* Mac Addr */
/* IP address length and IP address, if any. */
@@ -400,9 +400,9 @@ static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
s = zclient->obuf;
stream_reset(s);
- zclient_create_header(
- s, add ? ZEBRA_REMOTE_VTEP_ADD : ZEBRA_REMOTE_VTEP_DEL,
- bgp->vrf_id);
+ zclient_create_header(s, add ? ZEBRA_REMOTE_VTEP_ADD
+ : ZEBRA_REMOTE_VTEP_DEL,
+ bgp->vrf_id);
stream_putl(s, vpn->vni);
if (IS_EVPN_PREFIX_IPADDR_V4(p))
stream_put_in_addr(s, &p->prefix.ip.ipaddr_v4);
@@ -472,7 +472,7 @@ static void add_mac_mobility_to_attr(u_int32_t seq_num, struct attr *attr)
{
struct ecommunity ecom_tmp;
struct ecommunity_val eval;
- struct ecommunity *ecom_mm;
+ u_int8_t *ecom_val_ptr;
int i;
u_int8_t *pnt;
int type = 0;
@@ -482,7 +482,7 @@ static void add_mac_mobility_to_attr(u_int32_t seq_num, struct attr *attr)
encode_mac_mobility_extcomm(0, seq_num, &eval);
/* Find current MM ecommunity */
- ecom_mm = NULL;
+ ecom_val_ptr = NULL;
if (attr->ecommunity) {
for (i = 0; i < attr->ecommunity->size; i++) {
@@ -493,17 +493,17 @@ static void add_mac_mobility_to_attr(u_int32_t seq_num, struct attr *attr)
if (type == ECOMMUNITY_ENCODE_EVPN
&& sub_type
== ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
- ecom_mm = (struct ecommunity *)
- attr->ecommunity->val
- + (i * 8);
+ ecom_val_ptr =
+ (u_int8_t *)(attr->ecommunity->val
+ + (i * 8));
break;
}
}
}
/* Update the existing MM ecommunity */
- if (ecom_mm) {
- memcpy(ecom_mm->val, eval.val, sizeof(char) * ECOMMUNITY_SIZE);
+ if (ecom_val_ptr) {
+ memcpy(ecom_val_ptr, eval.val, sizeof(char) * ECOMMUNITY_SIZE);
}
/* Add MM to existing */
else {
@@ -704,7 +704,7 @@ static int evpn_route_is_sticky(struct bgp *bgp, struct bgp_node *rn)
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)
+ struct bgp_info **ri, u_char flags)
{
struct bgp_info *tmp_ri;
struct bgp_info *local_ri, *remote_ri;
@@ -751,8 +751,11 @@ static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
* 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) {
+ if (remote_ri && !CHECK_FLAG(flags, ZEBRA_MAC_TYPE_GW)) {
u_int32_t cur_seqnum;
/* Add MM extended community to route. */
@@ -811,7 +814,7 @@ 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, u_char sticky)
+ struct prefix_evpn *p, u_char flags)
{
struct bgp_node *rn;
struct attr attr;
@@ -828,7 +831,7 @@ static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
attr.nexthop = vpn->originator_ip;
attr.mp_nexthop_global_in = vpn->originator_ip;
attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
- attr.sticky = sticky;
+ attr.sticky = CHECK_FLAG(flags, ZEBRA_MAC_TYPE_STICKY) ? 1 : 0;
/* Set up RT and ENCAP extended community. */
build_evpn_route_extcomm(vpn, &attr);
@@ -839,7 +842,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);
+ 1, 1, &ri, flags);
assert(ri);
attr_new = ri->attr;
@@ -860,7 +863,7 @@ 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);
+ &global_ri, flags);
/* Schedule for processing and unlock node. */
bgp_process(bgp, rn, afi, safi);
@@ -998,10 +1001,10 @@ static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
if (evpn_route_is_sticky(bgp, rn))
update_evpn_route_entry(bgp, vpn, afi, safi, rn,
- &attr_sticky, 0, 1, &ri);
+ &attr_sticky, 0, 1, &ri, 0);
else
update_evpn_route_entry(bgp, vpn, afi, safi, rn, &attr,
- 0, 1, &ri);
+ 0, 1, &ri, 0);
/* If a local route exists for this prefix, we need to update
* the global routing table too.
@@ -1022,7 +1025,7 @@ 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);
/* Schedule for processing and unlock node. */
bgp_process(bgp, rd_rn, afi, safi);
@@ -1190,6 +1193,12 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn,
{
struct prefix_evpn p;
+ /* If VNI is not live, we only need to update the originator ip */
+ if (!is_vni_live(vpn)) {
+ vpn->originator_ip = originator_ip;
+ return 0;
+ }
+
/* Need to withdraw type-3 route as the originator IP is part
* of the key.
*/
@@ -1631,8 +1640,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);
+ update_evpn_route_entry(bgp, vpn, afi, safi, global_rn, attr, 1, 0, &ri,
+ 0);
/* Schedule for processing and unlock node. */
bgp_process(bgp, global_rn, afi, safi);
@@ -1665,7 +1674,7 @@ 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);
/* Schedule for processing and unlock node. */
bgp_process(bgp, global_rn, afi, safi);
@@ -1798,7 +1807,7 @@ static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
/* Make EVPN prefix. */
memset(&p, 0, sizeof(struct prefix_evpn));
- p.family = AF_ETHERNET;
+ p.family = AF_EVPN;
p.prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
@@ -1887,7 +1896,7 @@ static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
/* Make EVPN prefix. */
memset(&p, 0, sizeof(struct prefix_evpn));
- p.family = AF_ETHERNET;
+ p.family = AF_EVPN;
p.prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN;
p.prefix.route_type = BGP_EVPN_IMET_ROUTE;
@@ -1952,7 +1961,7 @@ static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
/* Make EVPN prefix. */
memset(&p, 0, sizeof(struct prefix_evpn));
- p.family = AF_ETHERNET;
+ p.family = AF_EVPN;
p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
/* Additional information outside of prefix - ESI and GW IP */
@@ -2021,7 +2030,7 @@ static void evpn_mpattr_encode_type5(struct stream *s, struct prefix *p,
struct evpn_addr *p_evpn_p;
memset(&temp, 0, 16);
- if (p->family != AF_ETHERNET)
+ if (p->family != AF_EVPN)
return;
p_evpn_p = &(p->u.prefix_evpn);
@@ -2204,7 +2213,7 @@ char *bgp_evpn_route2str(struct prefix_evpn *p, char *buf, int len)
PREFIX2STR_BUFFER));
}
} else {
- /* Currently, this is to cater to other AF_ETHERNET code. */
+ /* For EVPN route types not supported yet. */
}
return (buf);
@@ -2586,7 +2595,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, u_char sticky)
+ struct ipaddr *ip, u_char flags)
{
struct bgpevpn *vpn;
struct prefix_evpn p;
@@ -2606,13 +2615,15 @@ 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, sticky)) {
+ if (update_evpn_route(bgp, vpn, &p, flags)) {
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
zlog_err(
- "%u:Failed to create Type-2 route, VNI %u %sMAC %s IP %s",
- bgp->vrf_id, vpn->vni, sticky ? "sticky" : "",
+ "%u:Failed to create Type-2 route, VNI %u %s MAC %s IP %s",
+ bgp->vrf_id, vpn->vni,
+ CHECK_FLAG(flags, ZEBRA_MAC_TYPE_STICKY) ? "sticky gateway"
+ : "",
prefix_mac2str(mac, buf, sizeof(buf)),
ipaddr2str(ip, buf2, sizeof(buf2)));
return -1;
@@ -2671,14 +2682,15 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
/* Lookup VNI. If present and no change, exit. */
vpn = bgp_evpn_lookup_vni(bgp, vni);
- if (vpn && is_vni_live(vpn)) {
- if (IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip))
+ if (vpn) {
+ if (is_vni_live(vpn)
+ && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip))
/* Probably some other param has changed that we don't
* care about. */
return 0;
/* Local tunnel endpoint IP address has changed */
- return handle_tunnel_ip_change(bgp, vpn, originator_ip);
+ handle_tunnel_ip_change(bgp, vpn, originator_ip);
}
/* Create or update as appropriate. */
@@ -2692,6 +2704,10 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
}
}
+ /* if the VNI is live already, there is nothibng more to do */
+ if (is_vni_live(vpn))
+ return 0;
+
/* Mark as "live" */
SET_FLAG(vpn->flags, VNI_FLAG_LIVE);
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index f4c7e68a5d..e9b7857212 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -42,7 +42,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,
- u_char sticky);
+ u_char flags);
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);
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
index 095dfa1b15..9dc459cd4e 100644
--- a/bgpd/bgp_evpn_private.h
+++ b/bgpd/bgp_evpn_private.h
@@ -58,6 +58,9 @@ struct bgpevpn {
#define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */
#define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */
+ /* Flag to indicate if we are advertising the g/w mac ip for this VNI*/
+ u_int8_t advertise_gw_macip;
+
/* Id for deriving the RD automatically for this VNI */
u_int16_t rd_id;
@@ -171,7 +174,7 @@ static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
struct ipaddr *ip)
{
memset(p, 0, sizeof(struct prefix_evpn));
- p->family = AF_ETHERNET;
+ p->family = AF_EVPN;
p->prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN;
p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
memcpy(&p->prefix.mac.octet, mac->octet, ETH_ALEN);
@@ -184,7 +187,7 @@ static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
struct in_addr originator_ip)
{
memset(p, 0, sizeof(struct prefix_evpn));
- p->family = AF_ETHERNET;
+ p->family = AF_EVPN;
p->prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN;
p->prefix.route_type = BGP_EVPN_IMET_ROUTE;
p->prefix.ip.ipa_type = IPADDR_V4;
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index 1225354c0a..948c7f50f2 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -195,6 +195,8 @@ static void display_vni(struct vty *vty, struct bgpevpn *vpn)
vty_out(vty, " RD: %s\n",
prefix_rd2str(&vpn->prd, buf1, RD_ADDRSTRLEN));
vty_out(vty, " Originator IP: %s\n", inet_ntoa(vpn->originator_ip));
+ vty_out(vty, " Advertise-gw-macip : %s\n",
+ vpn->advertise_gw_macip ? "Yes" : "No");
vty_out(vty, " Import Route Target:\n");
for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
@@ -1642,6 +1644,51 @@ static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp)
}
/*
+ * evpn - enable advertisement of default g/w
+ */
+static void evpn_set_advertise_default_gw(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ if (!vpn) {
+ if (bgp->advertise_gw_macip)
+ return;
+
+ bgp->advertise_gw_macip = 1;
+ bgp_zebra_advertise_gw_macip(bgp, bgp->advertise_gw_macip, 0);
+ } else {
+ if (vpn->advertise_gw_macip)
+ return;
+
+ vpn->advertise_gw_macip = 1;
+ bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip,
+ vpn->vni);
+ }
+ return;
+}
+
+/*
+ * evpn - disable advertisement of default g/w
+ */
+static void evpn_unset_advertise_default_gw(struct bgp *bgp,
+ struct bgpevpn *vpn)
+{
+ if (!vpn) {
+ if (!bgp->advertise_gw_macip)
+ return;
+
+ bgp->advertise_gw_macip = 0;
+ bgp_zebra_advertise_gw_macip(bgp, bgp->advertise_gw_macip, 0);
+ } else {
+ if (!vpn->advertise_gw_macip)
+ return;
+
+ vpn->advertise_gw_macip = 0;
+ bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip,
+ vpn->vni);
+ }
+ return;
+}
+
+/*
* EVPN (VNI advertisement) enabled. Register with zebra.
*/
static void evpn_set_advertise_all_vni(struct bgp *bgp)
@@ -1700,6 +1747,9 @@ static void write_vni_config(struct vty *vty, struct bgpevpn *vpn, int *write)
}
}
+ if (vpn->advertise_gw_macip)
+ vty_out(vty, " advertise-default-gw\n");
+
vty_out(vty, " exit-vni\n");
}
}
@@ -1712,6 +1762,77 @@ static void write_vni_config_for_entry(struct hash_backet *backet,
}
#if defined(HAVE_CUMULUS)
+DEFUN (bgp_evpn_advertise_default_gw_vni,
+ bgp_evpn_advertise_default_gw_vni_cmd,
+ "advertise-default-gw",
+ "Advertise defualt g/w mac-ip routes in EVPN for a VNI\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_set_advertise_default_gw(bgp, vpn);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_default_vni_gw,
+ no_bgp_evpn_advertise_default_gw_vni_cmd,
+ "no advertise-default-gw",
+ NO_STR
+ "Withdraw default g/w mac-ip routes from EVPN for a VNI\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_default_gw(bgp, vpn);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (bgp_evpn_advertise_default_gw,
+ bgp_evpn_advertise_default_gw_cmd,
+ "advertise-default-gw",
+ "Advertise All defualt g/w mac-ip routes in EVPN\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ evpn_set_advertise_default_gw(bgp, NULL);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_default_gw,
+ no_bgp_evpn_advertise_default_gw_cmd,
+ "no advertise-default-gw",
+ NO_STR
+ "Withdraw All default g/w mac-ip routes from EVPN\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ evpn_unset_advertise_default_gw(bgp, NULL);
+
+ return CMD_SUCCESS;
+}
+
DEFUN (bgp_evpn_advertise_all_vni,
bgp_evpn_advertise_all_vni_cmd,
"advertise-all-vni",
@@ -1739,86 +1860,95 @@ DEFUN (no_bgp_evpn_advertise_all_vni,
return CMD_SUCCESS;
}
-DEFUN (show_bgp_evpn_vni,
- show_bgp_evpn_vni_cmd,
- "show bgp evpn vni",
+/*
+ * Display VNI information - for all or a specific VNI
+ */
+DEFUN (show_bgp_l2vpn_evpn_vni,
+ show_bgp_l2vpn_evpn_vni_cmd,
+ "show bgp l2vpn evpn vni [(1-16777215)]",
SHOW_STR
BGP_STR
+ L2VPN_HELP_STR
EVPN_HELP_STR
- "Show VNI\n")
-{
- struct bgp *bgp;
-
- bgp = bgp_get_default();
- if (!bgp)
- return CMD_WARNING;
-
- vty_out(vty, "Advertise All VNI flag: %s\n",
- bgp->advertise_all_vni ? "Enabled" : "Disabled");
-
- evpn_show_all_vnis(vty, bgp);
- return CMD_SUCCESS;
-}
-
-DEFUN (show_bgp_evpn_vni_num,
- show_bgp_evpn_vni_num_cmd,
- "show bgp evpn vni (1-16777215)",
- SHOW_STR
- BGP_STR
- "Address family modifier\n"
"Show VNI\n"
"VNI number\n")
{
- vni_t vni;
struct bgp *bgp;
+ vni_t vni;
+ int idx = 0;
bgp = bgp_get_default();
if (!bgp)
return CMD_WARNING;
- vni = strtoul(argv[4]->arg, NULL, 10);
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ if (argc == ((idx + 1) + 1)) {
+ vty_out(vty, "Advertise gateway macip flag: %s\n",
+ bgp->advertise_gw_macip ? "Enabled" : "Disabled");
+
+ /* Display all VNIs */
+ vty_out(vty, "Advertise All VNI flag: %s\n",
+ bgp->advertise_all_vni ? "Enabled" : "Disabled");
+ evpn_show_all_vnis(vty, bgp);
+ } else {
+ /* Display specific VNI */
+ vni = strtoul(argv[argc - 1]->arg, NULL, 10);
+ evpn_show_vni(vty, bgp, vni);
+ }
- evpn_show_vni(vty, bgp, vni);
return CMD_SUCCESS;
}
-/* `show bgp evpn summary' commands. */
-DEFUN (show_bgp_evpn_summary,
- show_bgp_evpn_summary_cmd,
- "show bgp evpn summary [json]",
+/*
+ * Display EVPN neighbor summary.
+ */
+DEFUN (show_bgp_l2vpn_evpn_summary,
+ show_bgp_l2vpn_evpn_summary_cmd,
+ "show bgp l2vpn evpn summary [json]",
SHOW_STR
BGP_STR
- "EVPN\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
"Summary of BGP neighbor status\n"
- "JavaScript Object Notation\n")
+ JSON_STR)
{
u_char uj = use_json(argc, argv);
return bgp_show_summary_vty(vty, NULL, AFI_L2VPN, SAFI_EVPN, uj);
}
-/* Show bgp evpn route */
-DEFUN (show_bgp_evpn_route,
- show_bgp_evpn_route_cmd,
- "show bgp evpn route [type <macip|multicast>]",
+/*
+ * Display global EVPN routing table.
+ */
+DEFUN (show_bgp_l2vpn_evpn_route,
+ show_bgp_l2vpn_evpn_route_cmd,
+ "show bgp l2vpn evpn route [type <macip|multicast>]",
SHOW_STR
BGP_STR
- "Address Family Modifier\n"
- "Display EVPN route information\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
"Specify Route type\n"
"MAC-IP (Type-2) route\n"
"Multicast (Type-3) route\n")
{
struct bgp *bgp;
+ int idx = 0;
int type = 0;
bgp = bgp_get_default();
if (!bgp)
return CMD_WARNING;
- if (argc == 6) {
- if (strncmp(argv[5]->arg, "ma", 2) == 0)
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ if (argc == ((idx + 1) + 3)) {
+ /* Specific type is requested */
+ if (strncmp(argv[argc - 1]->arg, "ma", 2) == 0)
type = BGP_EVPN_MAC_IP_ROUTE;
- else if (strncmp(argv[5]->arg, "mu", 2) == 0)
+ else if (strncmp(argv[argc - 1]->arg, "mu", 2) == 0)
type = BGP_EVPN_IMET_ROUTE;
else
return CMD_WARNING;
@@ -1828,13 +1958,17 @@ DEFUN (show_bgp_evpn_route,
return CMD_SUCCESS;
}
-DEFUN (show_bgp_evpn_route_rd,
- show_bgp_evpn_route_rd_cmd,
- "show bgp evpn route rd ASN:nn_or_IP-address:nn [type <macip|multicast>]",
+/*
+ * Display global EVPN routing table for specific RD.
+ */
+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>]",
SHOW_STR
BGP_STR
- "Address Family Modifier\n"
- "Display EVPN route information\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
"Route Distinguisher\n"
"ASN:XX or A.B.C.D:XX\n"
"Specify Route type\n"
@@ -1844,22 +1978,27 @@ DEFUN (show_bgp_evpn_route_rd,
struct bgp *bgp;
int ret;
struct prefix_rd prd;
+ int idx = 0;
int type = 0;
bgp = bgp_get_default();
if (!bgp)
return CMD_WARNING;
- ret = str2prefix_rd(argv[5]->arg, &prd);
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ ret = str2prefix_rd(argv[idx + 3]->arg, &prd);
if (!ret) {
vty_out(vty, "%% Malformed Route Distinguisher\n");
return CMD_WARNING;
}
- if (argc == 8) {
- if (strncmp(argv[7]->arg, "ma", 2) == 0)
+ if (argc == ((idx + 1) + 5)) {
+ /* Specific type is requested */
+ if (strncmp(argv[argc - 1]->arg, "ma", 2) == 0)
type = BGP_EVPN_MAC_IP_ROUTE;
- else if (strncmp(argv[7]->arg, "mu", 2) == 0)
+ else if (strncmp(argv[argc - 1]->arg, "mu", 2) == 0)
type = BGP_EVPN_IMET_ROUTE;
else
return CMD_WARNING;
@@ -1869,13 +2008,17 @@ DEFUN (show_bgp_evpn_route_rd,
return CMD_SUCCESS;
}
-DEFUN (show_bgp_evpn_route_rd_macip,
- show_bgp_evpn_route_rd_macip_cmd,
- "show bgp evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD]",
+/*
+ * Display global EVPN routing table for specific RD and MACIP.
+ */
+DEFUN (show_bgp_l2vpn_evpn_route_rd_macip,
+ show_bgp_l2vpn_evpn_route_rd_macip_cmd,
+ "show bgp l2vpn evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD]",
SHOW_STR
BGP_STR
- "Address Family Modifier\n"
- "Display EVPN route information\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
"Route Distinguisher\n"
"ASN:XX or A.B.C.D:XX\n"
"MAC\n"
@@ -1888,23 +2031,28 @@ DEFUN (show_bgp_evpn_route_rd_macip,
struct prefix_rd prd;
struct ethaddr mac;
struct ipaddr ip;
+ int idx = 0;
bgp = bgp_get_default();
if (!bgp)
return CMD_WARNING;
- ret = str2prefix_rd(argv[5]->arg, &prd);
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ ret = str2prefix_rd(argv[idx + 3]->arg, &prd);
if (!ret) {
vty_out(vty, "%% Malformed Route Distinguisher\n");
return CMD_WARNING;
}
- if (!prefix_str2mac(argv[7]->arg, &mac)) {
+ if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) {
vty_out(vty, "%% Malformed MAC address\n");
return CMD_WARNING;
}
memset(&ip, 0, sizeof(ip));
- if (argc == 10 && argv[9]->arg != NULL) {
- if (str2ipaddr(argv[9]->arg, &ip) != 0) {
+ if (argc == (idx + 1 + 7) && argv[argc - 1]->arg != NULL) {
+ /* Specific MAC+IP requested */
+ if (str2ipaddr(argv[argc - 1]->arg, &ip) != 0) {
vty_out(vty, "%% Malformed IP address\n");
return CMD_WARNING;
}
@@ -1914,13 +2062,17 @@ DEFUN (show_bgp_evpn_route_rd_macip,
return CMD_SUCCESS;
}
-DEFUN (show_bgp_evpn_route_vni,
- show_bgp_evpn_route_vni_cmd,
- "show bgp evpn route vni (1-16777215) [<type <macip|multicast> | vtep A.B.C.D>]",
+/*
+ * Display per-VNI EVPN routing table.
+ */
+DEFUN (show_bgp_l2vpn_evpn_route_vni,
+ show_bgp_l2vpn_evpn_route_vni_cmd,
+ "show bgp l2vpn evpn route vni (1-16777215) [<type <macip|multicast> | vtep A.B.C.D>]",
SHOW_STR
BGP_STR
- "Address Family Modifier\n"
- "Display EVPN route information\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
"VXLAN Network Identifier\n"
"VNI number\n"
"Specify Route type\n"
@@ -1933,25 +2085,29 @@ DEFUN (show_bgp_evpn_route_vni,
struct bgp *bgp;
struct in_addr vtep_ip;
int type = 0;
+ int idx = 0;
bgp = bgp_get_default();
if (!bgp)
return CMD_WARNING;
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
vtep_ip.s_addr = 0;
- vni = strtoul(argv[5]->arg, NULL, 10);
+ vni = strtoul(argv[idx + 3]->arg, NULL, 10);
- if (argc == 8 && argv[6]->arg) {
- if (strncmp(argv[6]->arg, "type", 4) == 0) {
- if (strncmp(argv[7]->arg, "ma", 2) == 0)
+ if (argc == (idx + 1 + 5) && argv[idx + 4]->arg) {
+ if (strncmp(argv[idx + 4]->arg, "type", 4) == 0) {
+ if (strncmp(argv[idx + 5]->arg, "ma", 2) == 0)
type = BGP_EVPN_MAC_IP_ROUTE;
- else if (strncmp(argv[7]->arg, "mu", 2) == 0)
+ else if (strncmp(argv[idx + 5]->arg, "mu", 2) == 0)
type = BGP_EVPN_IMET_ROUTE;
else
return CMD_WARNING;
- } else if (strncmp(argv[6]->arg, "vtep", 4) == 0) {
- if (!inet_aton(argv[7]->arg, &vtep_ip)) {
+ } else if (strncmp(argv[idx + 4]->arg, "vtep", 4) == 0) {
+ if (!inet_aton(argv[idx + 5]->arg, &vtep_ip)) {
vty_out(vty, "%% Malformed VTEP IP address\n");
return CMD_WARNING;
}
@@ -1963,13 +2119,17 @@ DEFUN (show_bgp_evpn_route_vni,
return CMD_SUCCESS;
}
-DEFUN (show_bgp_evpn_route_vni_macip,
- show_bgp_evpn_route_vni_macip_cmd,
- "show bgp evpn route vni (1-16777215) mac WORD [ip WORD]",
+/*
+ * Display per-VNI EVPN routing table for specific MACIP.
+ */
+DEFUN (show_bgp_l2vpn_evpn_route_vni_macip,
+ show_bgp_l2vpn_evpn_route_vni_macip_cmd,
+ "show bgp l2vpn evpn route vni (1-16777215) mac WORD [ip WORD]",
SHOW_STR
BGP_STR
- "Address Family Modifier\n"
- "Display EVPN route information\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
"VXLAN Network Identifier\n"
"VNI number\n"
"MAC\n"
@@ -1981,19 +2141,23 @@ DEFUN (show_bgp_evpn_route_vni_macip,
struct bgp *bgp;
struct ethaddr mac;
struct ipaddr ip;
+ int idx = 0;
bgp = bgp_get_default();
if (!bgp)
return CMD_WARNING;
- vni = strtoul(argv[5]->arg, NULL, 10);
- if (!prefix_str2mac(argv[7]->arg, &mac)) {
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ vni = strtoul(argv[idx + 3]->arg, NULL, 10);
+ if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) {
vty_out(vty, "%% Malformed MAC address\n");
return CMD_WARNING;
}
memset(&ip, 0, sizeof(ip));
- if (argc == 10 && argv[9]->arg != NULL) {
- if (str2ipaddr(argv[9]->arg, &ip) != 0) {
+ if (argc == (idx + 1 + 7) && argv[idx + 7]->arg != NULL) {
+ if (str2ipaddr(argv[idx + 7]->arg, &ip) != 0) {
vty_out(vty, "%% Malformed IP address\n");
return CMD_WARNING;
}
@@ -2003,13 +2167,17 @@ DEFUN (show_bgp_evpn_route_vni_macip,
return CMD_SUCCESS;
}
-DEFUN (show_bgp_evpn_route_vni_multicast,
- show_bgp_evpn_route_vni_multicast_cmd,
- "show bgp evpn route vni (1-16777215) multicast A.B.C.D",
+/*
+ * Display per-VNI EVPN routing table for specific multicast IP (remote VTEP).
+ */
+DEFUN (show_bgp_l2vpn_evpn_route_vni_multicast,
+ show_bgp_l2vpn_evpn_route_vni_multicast_cmd,
+ "show bgp l2vpn evpn route vni (1-16777215) multicast A.B.C.D",
SHOW_STR
BGP_STR
- "Address Family Modifier\n"
- "Display EVPN route information\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
"VXLAN Network Identifier\n"
"VNI number\n"
"Multicast (Type-3) route\n"
@@ -2019,13 +2187,17 @@ DEFUN (show_bgp_evpn_route_vni_multicast,
struct bgp *bgp;
int ret;
struct in_addr orig_ip;
+ int idx = 0;
bgp = bgp_get_default();
if (!bgp)
return CMD_WARNING;
- vni = strtoul(argv[5]->arg, NULL, 10);
- ret = inet_aton(argv[7]->arg, &orig_ip);
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ vni = strtoul(argv[idx + 3]->arg, NULL, 10);
+ ret = inet_aton(argv[idx + 5]->arg, &orig_ip);
if (!ret) {
vty_out(vty, "%% Malformed Originating Router IP address\n");
return CMD_WARNING;
@@ -2035,13 +2207,17 @@ DEFUN (show_bgp_evpn_route_vni_multicast,
return CMD_SUCCESS;
}
-DEFUN (show_bgp_evpn_route_vni_all,
- show_bgp_evpn_route_vni_all_cmd,
- "show bgp evpn route vni all [vtep A.B.C.D]",
+/*
+ * Display per-VNI EVPN routing table - for all VNIs.
+ */
+DEFUN (show_bgp_l2vpn_evpn_route_vni_all,
+ show_bgp_l2vpn_evpn_route_vni_all_cmd,
+ "show bgp l2vpn evpn route vni all [vtep A.B.C.D]",
SHOW_STR
BGP_STR
- "Address Family Modifier\n"
- "Display EVPN route information\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "EVPN route information\n"
"VXLAN Network Identifier\n"
"All VNIs\n"
"Remote VTEP\n"
@@ -2049,14 +2225,18 @@ DEFUN (show_bgp_evpn_route_vni_all,
{
struct bgp *bgp;
struct in_addr vtep_ip;
+ int idx = 0;
bgp = bgp_get_default();
if (!bgp)
return CMD_WARNING;
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
vtep_ip.s_addr = 0;
- if (argc == 8 && argv[7]->arg) {
- if (!inet_aton(argv[7]->arg, &vtep_ip)) {
+ if (argc == (idx + 1 + 5) && argv[idx + 5]->arg) {
+ if (!inet_aton(argv[idx + 5]->arg, &vtep_ip)) {
vty_out(vty, "%% Malformed VTEP IP address\n");
return CMD_WARNING;
}
@@ -2066,12 +2246,16 @@ DEFUN (show_bgp_evpn_route_vni_all,
return CMD_SUCCESS;
}
-DEFUN (show_bgp_evpn_import_rt,
- show_bgp_evpn_import_rt_cmd,
- "show bgp evpn import-rt",
+/*
+ * Display EVPN import route-target hash table
+ */
+DEFUN (show_bgp_l2vpn_evpn_import_rt,
+ show_bgp_l2vpn_evpn_import_rt_cmd,
+ "show bgp l2vpn evpn import-rt",
SHOW_STR
BGP_STR
- "Address family modifier\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
"Show import route target\n")
{
struct bgp *bgp;
@@ -2084,6 +2268,97 @@ DEFUN (show_bgp_evpn_import_rt,
return CMD_SUCCESS;
}
+#if defined(HAVE_CUMULUS)
+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"
+ "VNI number\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_summary, show_bgp_evpn_summary_cmd,
+ "show bgp evpn summary [json]", SHOW_STR BGP_STR EVPN_HELP_STR
+ "Summary of BGP neighbor status\n"
+ JSON_STR)
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route, show_bgp_evpn_route_cmd,
+ "show bgp evpn route [type <macip|multicast>]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "Specify Route type\n"
+ "MAC-IP (Type-2) route\n"
+ "Multicast (Type-3) route\n")
+
+ALIAS_HIDDEN(
+ show_bgp_l2vpn_evpn_route_rd, show_bgp_evpn_route_rd_cmd,
+ "show bgp evpn route rd ASN:nn_or_IP-address:nn [type <macip|multicast>]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n"
+ "Specify Route type\n"
+ "MAC-IP (Type-2) route\n"
+ "Multicast (Type-3) route\n")
+
+ALIAS_HIDDEN(
+ show_bgp_l2vpn_evpn_route_rd_macip, show_bgp_evpn_route_rd_macip_cmd,
+ "show bgp evpn route rd ASN:nn_or_IP-address:nn mac WORD [ip WORD]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n"
+ "MAC\n"
+ "MAC address (e.g., 00:e0:ec:20:12:62)\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n")
+
+ALIAS_HIDDEN(
+ show_bgp_l2vpn_evpn_route_vni, show_bgp_evpn_route_vni_cmd,
+ "show bgp evpn route vni (1-16777215) [<type <macip|multicast> | vtep A.B.C.D>]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ "Specify Route type\n"
+ "MAC-IP (Type-2) route\n"
+ "Multicast (Type-3) route\n"
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_macip,
+ show_bgp_evpn_route_vni_macip_cmd,
+ "show bgp evpn route vni (1-16777215) mac WORD [ip WORD]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ "MAC\n"
+ "MAC address (e.g., 00:e0:ec:20:12:62)\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_multicast,
+ show_bgp_evpn_route_vni_multicast_cmd,
+ "show bgp evpn route vni (1-16777215) multicast A.B.C.D",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ "Multicast (Type-3) route\n"
+ "Originating Router IP address\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_all, show_bgp_evpn_route_vni_all_cmd,
+ "show bgp evpn route vni all [vtep A.B.C.D]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "VXLAN Network Identifier\n"
+ "All VNIs\n"
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_import_rt, show_bgp_evpn_import_rt_cmd,
+ "show bgp evpn import-rt",
+ SHOW_STR BGP_STR EVPN_HELP_STR "Show import route target\n")
+#endif
+
DEFUN_NOSH (bgp_evpn_vni,
bgp_evpn_vni_cmd,
"vni (1-16777215)",
@@ -2290,11 +2565,11 @@ DEFUN (bgp_evpn_vni_rt,
if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) {
ecomadd = ecommunity_str2com(argv[2]->arg,
ECOMMUNITY_ROUTE_TARGET, 0);
- ecommunity_str(ecomadd);
if (!ecomadd) {
vty_out(vty, "%% Malformed Route Target list\n");
return CMD_WARNING;
}
+ ecommunity_str(ecomadd);
/* Do nothing if we already have this import route-target */
if (!bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomadd))
@@ -2305,11 +2580,11 @@ DEFUN (bgp_evpn_vni_rt,
if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) {
ecomadd = ecommunity_str2com(argv[2]->arg,
ECOMMUNITY_ROUTE_TARGET, 0);
- ecommunity_str(ecomadd);
if (!ecomadd) {
vty_out(vty, "%% Malformed Route Target list\n");
return CMD_WARNING;
}
+ ecommunity_str(ecomadd);
/* Do nothing if we already have this export route-target */
if (!bgp_evpn_rt_matches_existing(vpn->export_rtl, ecomadd))
@@ -2372,11 +2647,11 @@ DEFUN (no_bgp_evpn_vni_rt,
}
ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
- ecommunity_str(ecomdel);
if (!ecomdel) {
vty_out(vty, "%% Malformed Route Target list\n");
return CMD_WARNING;
}
+ ecommunity_str(ecomdel);
if (rt_type == RT_TYPE_IMPORT) {
if (!bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomdel)) {
@@ -2484,6 +2759,11 @@ void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
bgp_config_write_family_header(vty, afi, safi, write);
vty_out(vty, " advertise-all-vni\n");
}
+
+ if (bgp->advertise_gw_macip) {
+ bgp_config_write_family_header(vty, afi, safi, write);
+ vty_out(vty, " advertise-default-gw\n");
+ }
}
void bgp_ethernetvpn_init(void)
@@ -2509,10 +2789,24 @@ void bgp_ethernetvpn_init(void)
#if defined(HAVE_CUMULUS)
install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_all_vni_cmd);
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);
+
+ /* "show bgp l2vpn evpn" commands. */
+ 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_vni_cmd);
+ install_element(VIEW_NODE,
+ &show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd);
/* "show bgp evpn" commands. */
install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd);
- install_element(VIEW_NODE, &show_bgp_evpn_vni_num_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_summary_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_route_cmd);
install_element(VIEW_NODE, &show_bgp_evpn_route_rd_cmd);
@@ -2532,5 +2826,9 @@ void bgp_ethernetvpn_init(void)
install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rt_cmd);
install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_cmd);
install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_without_val_cmd);
+ install_element(BGP_EVPN_VNI_NODE,
+ &bgp_evpn_advertise_default_gw_vni_cmd);
+ install_element(BGP_EVPN_VNI_NODE,
+ &no_bgp_evpn_advertise_default_gw_vni_cmd);
#endif
}
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index e4e421510f..35f793f861 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -1580,10 +1580,18 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_info *ri,
/* Route map & unsuppress-map apply. */
if (ROUTE_MAP_OUT_NAME(filter) || (ri->extra && ri->extra->suppress)) {
struct bgp_info info;
+ struct bgp_info_extra dummy_info_extra;
struct attr dummy_attr;
info.peer = peer;
info.attr = attr;
+
+ if (ri->extra) {
+ memcpy(&dummy_info_extra, ri->extra,
+ sizeof(struct bgp_info_extra));
+ info.extra = &dummy_info_extra;
+ }
+
/* don't confuse inbound and outbound setting */
RESET_FLAG(attr->rmap_change_flags);
@@ -6238,6 +6246,9 @@ static void route_vty_out_route(struct prefix *p, struct vty *vty)
} else
len += vty_out(vty, "/%d", p->prefixlen);
} else if (p->family == AF_ETHERNET) {
+ prefix2str(p, buf, PREFIX_STRLEN);
+ len = vty_out(vty, "%s", buf);
+ } else if (p->family == AF_EVPN) {
#if defined(HAVE_CUMULUS)
len = vty_out(vty, "%s",
bgp_evpn_route2str((struct prefix_evpn *)p, buf,
@@ -6505,15 +6516,14 @@ void route_vty_out(struct vty *vty, struct prefix *p, struct bgp_info *binfo,
len = vty_out(
vty, "%s",
binfo->peer->conf_if);
- len =
- 7 - len; /* len of IPv6
- addr + max
- len of def
- ifname */
+ len = 16 - len; /* len of IPv6
+ addr + max
+ len of def
+ ifname */
if (len < 1)
vty_out(vty, "\n%*s",
- 45, " ");
+ 36, " ");
else
vty_out(vty, "%*s", len,
" ");
@@ -6801,7 +6811,7 @@ void route_vty_out_tag(struct vty *vty, struct prefix *p,
if (attr) {
if (((p->family == AF_INET)
&& ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)))
- || (safi == SAFI_EVPN && p->family == AF_ETHERNET
+ || (safi == SAFI_EVPN
&& !BGP_ATTR_NEXTHOP_AFI_IP6(attr))
|| (!BGP_ATTR_NEXTHOP_AFI_IP6(attr))) {
if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
@@ -6826,7 +6836,7 @@ void route_vty_out_tag(struct vty *vty, struct prefix *p,
}
} else if (((p->family == AF_INET6)
&& ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)))
- || (safi == SAFI_EVPN && p->family == AF_ETHERNET
+ || (safi == SAFI_EVPN
&& BGP_ATTR_NEXTHOP_AFI_IP6(attr))
|| (BGP_ATTR_NEXTHOP_AFI_IP6(attr))) {
char buf_a[BUFSIZ];
@@ -7326,7 +7336,8 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
/* Line2 display Next-hop, Neighbor, Router-id */
/* Display the nexthop */
- if ((p->family == AF_INET || p->family == AF_ETHERNET)
+ if ((p->family == AF_INET || p->family == AF_ETHERNET ||
+ p->family == AF_EVPN)
&& (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
|| safi == SAFI_EVPN
|| !BGP_ATTR_NEXTHOP_AFI_IP6(attr))) {
@@ -10232,6 +10243,10 @@ static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer,
afi_t afi, safi_t safi,
enum bgp_show_type type, u_char use_json)
{
+ /* labeled-unicast routes live in the unicast table */
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
if (!peer || !peer->afc[afi][safi]) {
if (use_json) {
json_object *json_no = NULL;
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 285bb9a80c..a8e111d361 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -54,6 +54,9 @@
#include "bgpd/bgp_lcommunity.h"
#include "bgpd/bgp_vty.h"
#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_vty.h"
#if ENABLE_BGP_VNC
#include "bgpd/rfapi/bgp_rfapi_cfg.h"
@@ -572,6 +575,106 @@ struct route_map_rule_cmd route_match_ip_route_source_prefix_list_cmd = {
route_match_ip_route_source_prefix_list_compile,
route_match_ip_route_source_prefix_list_free};
+/* `match mac address MAC_ACCESS_LIST' */
+
+/* Match function should return 1 if match is success else return
+ zero. */
+static route_map_result_t route_match_mac_address(void *rule,
+ struct prefix *prefix,
+ route_map_object_t type,
+ void *object)
+{
+ struct access_list *alist;
+ struct prefix p;
+
+ if (type == RMAP_BGP) {
+ alist = access_list_lookup(AFI_L2VPN, (char *)rule);
+ if (alist == NULL)
+ return RMAP_NOMATCH;
+
+ if (prefix->u.prefix_evpn.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ return RMAP_NOMATCH;
+
+ p.family = AF_ETHERNET;
+ p.prefixlen = ETH_ALEN * 8;
+ p.u.prefix_eth = prefix->u.prefix_evpn.mac;
+
+ return (access_list_apply(alist, &p)
+ == FILTER_DENY
+ ? RMAP_NOMATCH
+ : RMAP_MATCH);
+ }
+
+ return RMAP_NOMATCH;
+}
+
+/* Route map `mac address' match statement. `arg' should be
+ access-list name. */
+static void *route_match_mac_address_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void route_match_mac_address_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for mac address matching. */
+struct route_map_rule_cmd route_match_mac_address_cmd = {
+ "mac address", route_match_mac_address, route_match_mac_address_compile,
+ route_match_mac_address_free};
+
+/* `match vni' */
+
+/* Match function should return 1 if match is success else return
+ zero. */
+static route_map_result_t route_match_vni(void *rule, struct prefix *prefix,
+ route_map_object_t type, void *object)
+{
+ vni_t vni = 0;
+ struct bgp_info *bgp_info = NULL;
+
+ if (type == RMAP_BGP) {
+ vni = *((vni_t *)rule);
+ bgp_info = (struct bgp_info *)object;
+
+ if (vni == label2vni(&bgp_info->extra->label))
+ return RMAP_MATCH;
+ }
+
+ return RMAP_NOMATCH;
+}
+
+/* Route map `vni' match statement. */
+static void *route_match_vni_compile(const char *arg)
+{
+ vni_t *vni = NULL;
+ char *end = NULL;
+
+ vni = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(vni_t));
+ if (!vni)
+ return NULL;
+
+ *vni = strtoul(arg, &end, 10);
+ if (*end != '\0')
+ return NULL;
+
+ return vni;
+}
+
+/* Free route map's compiled `vni' value. */
+static void route_match_vni_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for vni matching. */
+struct route_map_rule_cmd route_match_evpn_vni_cmd = {
+ "evpn vni", route_match_vni, route_match_vni_compile,
+ route_match_vni_free};
+
/* `match local-preference LOCAL-PREF' */
/* Match function return 1 if match is success else return zero. */
@@ -2994,6 +3097,55 @@ static void bgp_route_map_event(route_map_event_t event, const char *rmap_name)
route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED);
}
+DEFUN (match_mac_address,
+ match_mac_address_cmd,
+ "match mac address WORD",
+ MATCH_STR
+ "mac address\n"
+ "Match address of route\n"
+ "MAC Access-list name\n")
+{
+ return bgp_route_match_add(vty, "mac address", argv[3]->arg,
+ RMAP_EVENT_FILTER_ADDED);
+}
+
+DEFUN (no_match_mac_address,
+ no_match_mac_address_cmd,
+ "no match mac address WORD",
+ NO_STR
+ MATCH_STR
+ "mac\n"
+ "Match address of route\n"
+ "MAC acess-list name\n")
+{
+ return bgp_route_match_delete(vty, "mac address", argv[4]->arg,
+ RMAP_EVENT_FILTER_DELETED);
+}
+
+DEFUN (match_evpn_vni,
+ match_evpn_vni_cmd,
+ "match evpn vni (1-16777215)",
+ MATCH_STR
+ EVPN_HELP_STR
+ "Match VNI\n"
+ "VNI ID\n")
+{
+ return bgp_route_match_add(vty, "evpn vni", argv[3]->arg,
+ RMAP_EVENT_MATCH_ADDED);
+}
+
+DEFUN (no_match_evpn_vni,
+ no_match_evpn_vni_cmd,
+ "no match evpn vni (1-16777215)",
+ NO_STR
+ MATCH_STR
+ EVPN_HELP_STR
+ "Match VNI\n"
+ "VNI ID\n")
+{
+ return bgp_route_match_delete(vty, "evpn vni", argv[4]->arg,
+ RMAP_EVENT_MATCH_DELETED);
+}
DEFUN (match_peer,
match_peer_cmd,
@@ -4351,6 +4503,8 @@ void bgp_route_map_init(void)
route_map_install_match(&route_match_probability_cmd);
route_map_install_match(&route_match_interface_cmd);
route_map_install_match(&route_match_tag_cmd);
+ route_map_install_match(&route_match_mac_address_cmd);
+ route_map_install_match(&route_match_evpn_vni_cmd);
route_map_install_set(&route_set_ip_nexthop_cmd);
route_map_install_set(&route_set_local_pref_cmd);
@@ -4381,6 +4535,10 @@ void bgp_route_map_init(void)
install_element(RMAP_NODE, &no_match_ip_route_source_cmd);
install_element(RMAP_NODE, &match_ip_route_source_prefix_list_cmd);
install_element(RMAP_NODE, &no_match_ip_route_source_prefix_list_cmd);
+ install_element(RMAP_NODE, &match_mac_address_cmd);
+ install_element(RMAP_NODE, &no_match_mac_address_cmd);
+ install_element(RMAP_NODE, &match_evpn_vni_cmd);
+ install_element(RMAP_NODE, &no_match_evpn_vni_cmd);
install_element(RMAP_NODE, &match_aspath_cmd);
install_element(RMAP_NODE, &no_match_aspath_cmd);
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 0220a7e55d..01c27920f5 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -12047,6 +12047,8 @@ void bgp_vty_init(void)
install_element(BGP_VPNV4_NODE, &no_neighbor_route_map_cmd);
install_element(BGP_VPNV6_NODE, &neighbor_route_map_cmd);
install_element(BGP_VPNV6_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_route_map_cmd);
/* "neighbor unsuppress-map" commands. */
install_element(BGP_NODE, &neighbor_unsuppress_map_hidden_cmd);
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index 5071be909e..2fc75ea5a2 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -2034,6 +2034,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_gw_macip(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_DEFAULT_GW, 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_all_vni(struct bgp *bgp, int advertise)
{
struct stream *s;
@@ -2120,7 +2143,7 @@ static int bgp_zebra_process_local_macip(int command, struct zclient *zclient,
int ipa_len;
char buf[ETHER_ADDR_STRLEN];
char buf1[INET6_ADDRSTRLEN];
- u_char sticky;
+ u_char flags;
memset(&ip, 0, sizeof(ip));
s = zclient->ibuf;
@@ -2140,21 +2163,20 @@ 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);
}
- sticky = stream_getc(s);
+ flags = stream_getc(s);
bgp = bgp_lookup_by_vrf_id(vrf_id);
if (!bgp)
return 0;
if (BGP_DEBUG(zebra, ZEBRA))
- zlog_debug("%u:Recv MACIP %s %sMAC %s IP %s VNI %u", vrf_id,
- (command == ZEBRA_MACIP_ADD) ? "Add" : "Del",
- sticky ? "sticky " : "",
- prefix_mac2str(&mac, buf, sizeof(buf)),
+ zlog_debug("%u:Recv MACIP %s flags 0x%x MAC %s IP %s VNI %u",
+ vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del",
+ flags, prefix_mac2str(&mac, buf, sizeof(buf)),
ipaddr2str(&ip, buf1, sizeof(buf1)), vni);
if (command == ZEBRA_MACIP_ADD)
- return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, sticky);
+ return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, flags);
else
return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip);
}
diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h
index 8e55eb6d8d..11405d1c1b 100644
--- a/bgpd/bgp_zebra.h
+++ b/bgpd/bgp_zebra.h
@@ -21,6 +21,8 @@
#ifndef _QUAGGA_BGP_ZEBRA_H
#define _QUAGGA_BGP_ZEBRA_H
+#include "vxlan.h"
+
extern void bgp_zebra_init(struct thread_master *master);
extern void bgp_zebra_destroy(void);
extern int bgp_if_update_all(void);
@@ -57,6 +59,7 @@ extern struct interface *if_lookup_by_ipv6(struct in6_addr *, ifindex_t,
extern struct interface *if_lookup_by_ipv6_exact(struct in6_addr *, ifindex_t,
vrf_id_t);
+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_num_connects(void);
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 208a4e4b4e..bfdddc69b1 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -380,6 +380,9 @@ struct bgp {
/* EVI hash table */
struct hash *vnihash;
+ /* EVPN enable - advertise gateway macip routes */
+ int advertise_gw_macip;
+
/* EVPN enable - advertise local VNIs and their MACs etc. */
int advertise_all_vni;
@@ -1488,7 +1491,8 @@ static inline int peer_group_af_configured(struct peer_group *group)
|| peer->afc[AFI_IP6][SAFI_MULTICAST]
|| peer->afc[AFI_IP6][SAFI_LABELED_UNICAST]
|| peer->afc[AFI_IP6][SAFI_MPLS_VPN]
- || peer->afc[AFI_IP6][SAFI_ENCAP] || peer->afc[AFI_IP6][SAFI_EVPN])
+ || peer->afc[AFI_IP6][SAFI_ENCAP]
+ || peer->afc[AFI_L2VPN][SAFI_EVPN])
return 1;
return 0;
}
diff --git a/ldpd/ldp_vty_cmds.c b/ldpd/ldp_vty_cmds.c
index 3f39ad926c..db92b93628 100644
--- a/ldpd/ldp_vty_cmds.c
+++ b/ldpd/ldp_vty_cmds.c
@@ -21,6 +21,8 @@
#include "command.h"
#include "vty.h"
+#include "json.h"
+
#include "ldpd/ldpd.h"
#include "ldpd/ldp_vty.h"
#include "ldpd/ldp_vty_cmds_clippy.c"
@@ -586,7 +588,7 @@ DEFPY (ldp_show_mpls_ldp_binding,
"IPv6 Address Family\n"
"Label Information Base (LIB) information\n"
"Show detailed information\n"
- "JavaScript Object Notation\n")
+ JSON_STR)
{
return (ldp_vty_show_binding(vty, af, detail, json));
}
@@ -601,7 +603,7 @@ DEFPY (ldp_show_mpls_ldp_discovery,
"IPv6 Address Family\n"
"Discovery Hello Information\n"
"Show detailed information\n"
- "JavaScript Object Notation\n")
+ JSON_STR)
{
return (ldp_vty_show_discovery(vty, af, detail, json));
}
@@ -615,7 +617,7 @@ DEFPY (ldp_show_mpls_ldp_interface,
"IPv4 Address Family\n"
"IPv6 Address Family\n"
"interface information\n"
- "JavaScript Object Notation\n")
+ JSON_STR)
{
return (ldp_vty_show_interface(vty, af, json));
}
@@ -627,7 +629,7 @@ DEFPY (ldp_show_mpls_ldp_capabilities,
"MPLS information\n"
"Label Distribution Protocol\n"
"Display LDP Capabilities information\n"
- "JavaScript Object Notation\n")
+ JSON_STR)
{
return (ldp_vty_show_capabilities(vty, json));
}
@@ -640,7 +642,7 @@ DEFPY (ldp_show_mpls_ldp_neighbor,
"Label Distribution Protocol\n"
"Neighbor information\n"
"Show detailed information\n"
- "JavaScript Object Notation\n")
+ JSON_STR)
{
return (ldp_vty_show_neighbor(vty, 0, detail, json));
}
@@ -653,7 +655,7 @@ DEFPY (ldp_show_mpls_ldp_neighbor_capabilities,
"Label Distribution Protocol\n"
"Neighbor information\n"
"Display neighbor capability information\n"
- "JavaScript Object Notation\n")
+ JSON_STR)
{
return (ldp_vty_show_neighbor(vty, 1, NULL, json));
}
@@ -665,7 +667,7 @@ DEFPY (ldp_show_l2vpn_atom_binding,
"Show information about Layer2 VPN\n"
"Show Any Transport over MPLS information\n"
"Show AToM label binding information\n"
- "JavaScript Object Notation\n")
+ JSON_STR)
{
return (ldp_vty_show_atom_binding(vty, json));
}
@@ -677,7 +679,7 @@ DEFPY (ldp_show_l2vpn_atom_vc,
"Show information about Layer2 VPN\n"
"Show Any Transport over MPLS information\n"
"Show AToM virtual circuit information\n"
- "JavaScript Object Notation\n")
+ JSON_STR)
{
return (ldp_vty_show_atom_vc(vty, json));
}
diff --git a/lib/command.c b/lib/command.c
index 09ffa6ce56..077a72398d 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -101,6 +101,7 @@ const char *node_names[] = {
"ipv4 access list", // ACCESS_NODE,
"ipv4 prefix list", // PREFIX_NODE,
"ipv6 access list", // ACCESS_IPV6_NODE,
+ "MAC access list", // ACCESS_MAC_NODE,
"ipv6 prefix list", // PREFIX_IPV6_NODE,
"as list", // AS_LIST_NODE,
"community list", // COMMUNITY_LIST_NODE,
diff --git a/lib/command.h b/lib/command.h
index d0c9f0eaf9..8f12e2aabd 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -123,6 +123,7 @@ enum node_type {
ACCESS_NODE, /* Access list node. */
PREFIX_NODE, /* Prefix list node. */
ACCESS_IPV6_NODE, /* Access list node. */
+ ACCESS_MAC_NODE, /* MAC access list node*/
PREFIX_IPV6_NODE, /* Prefix list node. */
AS_LIST_NODE, /* AS list node. */
COMMUNITY_LIST_NODE, /* Community list node. */
diff --git a/lib/filter.c b/lib/filter.c
index 0262234c78..cb6f743c01 100644
--- a/lib/filter.c
+++ b/lib/filter.c
@@ -90,6 +90,14 @@ struct access_master {
void (*delete_hook)(struct access_list *);
};
+/* Static structure for mac access_list's master. */
+static struct access_master access_master_mac = {
+ {NULL, NULL},
+ {NULL, NULL},
+ NULL,
+ NULL,
+};
+
/* Static structure for IPv4 access_list's master. */
static struct access_master access_master_ipv4 = {
{NULL, NULL},
@@ -112,6 +120,8 @@ static struct access_master *access_master_get(afi_t afi)
return &access_master_ipv4;
else if (afi == AFI_IP6)
return &access_master_ipv6;
+ else if (afi == AFI_L2VPN)
+ return &access_master_mac;
return NULL;
}
@@ -173,7 +183,7 @@ static int filter_match_cisco(struct filter *mfilter, struct prefix *p)
/* If filter match to the prefix then return 1. */
static int filter_match_zebra(struct filter *mfilter, struct prefix *p)
{
- struct filter_zebra *filter;
+ struct filter_zebra *filter = NULL;
filter = &mfilter->u.zfilter;
@@ -365,9 +375,7 @@ static struct access_list *access_list_get(afi_t afi, const char *name)
enum filter_type access_list_apply(struct access_list *access, void *object)
{
struct filter *filter;
- struct prefix *p;
-
- p = (struct prefix *)object;
+ struct prefix *p = (struct prefix *)object;
if (access == NULL)
return FILTER_DENY;
@@ -390,6 +398,7 @@ void access_list_add_hook(void (*func)(struct access_list *access))
{
access_master_ipv4.add_hook = func;
access_master_ipv6.add_hook = func;
+ access_master_mac.add_hook = func;
}
/* Delete hook function. */
@@ -397,6 +406,7 @@ void access_list_delete_hook(void (*func)(struct access_list *access))
{
access_master_ipv4.delete_hook = func;
access_master_ipv6.delete_hook = func;
+ access_master_mac.delete_hook = func;
}
/* Add new filter to the end of specified access_list. */
@@ -515,10 +525,10 @@ static struct filter *filter_lookup_zebra(struct access_list *access,
filter = &mfilter->u.zfilter;
if (filter->exact == new->exact
- && mfilter->type
- == mnew->type &&prefix_same(&filter->prefix,
- &new->prefix))
- return mfilter;
+ && mfilter->type == mnew->type) {
+ if (prefix_same(&filter->prefix, &new->prefix))
+ return mfilter;
+ }
}
return NULL;
}
@@ -1252,6 +1262,12 @@ static int filter_set_zebra(struct vty *vty, const char *name_str,
"IPv6 address prefix/prefixlen is malformed\n");
return CMD_WARNING_CONFIG_FAILED;
}
+ } else if (afi == AFI_L2VPN) {
+ ret = str2prefix_eth(prefix_str, (struct prefix_eth *)&p);
+ if (ret <= 0) {
+ vty_out(vty, "MAC address is malformed\n");
+ return CMD_WARNING;
+ }
} else
return CMD_WARNING_CONFIG_FAILED;
@@ -1274,7 +1290,6 @@ static int filter_set_zebra(struct vty *vty, const char *name_str,
access_list_filter_add(access, mfilter);
} else {
struct filter *delete_filter;
-
delete_filter = filter_lookup_zebra(access, mfilter);
if (delete_filter)
access_list_filter_delete(access, delete_filter);
@@ -1285,6 +1300,64 @@ static int filter_set_zebra(struct vty *vty, const char *name_str,
return CMD_SUCCESS;
}
+DEFUN (mac_access_list,
+ mac_access_list_cmd,
+ "mac access-list WORD <deny|permit> MAC",
+ "Add a mac access-list\n"
+ "Add an access list entry\n"
+ "MAC zebra access-list name\n"
+ "Specify packets to reject\n"
+ "Specify packets to forward\n"
+ "MAC address to match. e.g. 00:01:00:01:00:01\n")
+{
+ return filter_set_zebra(vty, argv[2]->arg, argv[3]->arg, AFI_L2VPN,
+ argv[4]->arg, 0, 1);
+}
+
+DEFUN (no_mac_access_list,
+ no_mac_access_list_cmd,
+ "no mac access-list WORD <deny|permit> MAC",
+ NO_STR
+ "Remove a mac access-list\n"
+ "Remove an access list entry\n"
+ "MAC zebra access-list name\n"
+ "Specify packets to reject\n"
+ "Specify packets to forward\n"
+ "MAC address to match. e.g. 00:01:00:01:00:01\n")
+{
+ return filter_set_zebra(vty, argv[3]->arg, argv[4]->arg, AFI_L2VPN,
+ argv[5]->arg, 0, 0);
+}
+
+DEFUN (mac_access_list_any,
+ mac_access_list_any_cmd,
+ "mac access-list WORD <deny|permit> any",
+ "Add a mac access-list\n"
+ "Add an access list entry\n"
+ "MAC zebra access-list name\n"
+ "Specify packets to reject\n"
+ "Specify packets to forward\n"
+ "MAC address to match. e.g. 00:01:00:01:00:01\n")
+{
+ return filter_set_zebra(vty, argv[2]->arg, argv[3]->arg, AFI_L2VPN,
+ "00:00:00:00:00:00", 0, 1);
+}
+
+DEFUN (no_mac_access_list_any,
+ no_mac_access_list_any_cmd,
+ "no mac access-list WORD <deny|permit> any",
+ NO_STR
+ "Remove a mac access-list\n"
+ "Remove an access list entry\n"
+ "MAC zebra access-list name\n"
+ "Specify packets to reject\n"
+ "Specify packets to forward\n"
+ "MAC address to match. e.g. 00:01:00:01:00:01\n")
+{
+ return filter_set_zebra(vty, argv[3]->arg, argv[4]->arg, AFI_L2VPN,
+ "00:00:00:00:00:00", 0, 0);
+}
+
DEFUN (access_list_exact,
access_list_exact_cmd,
"access-list WORD <deny|permit> A.B.C.D/M [exact-match]",
@@ -1666,12 +1739,15 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi)
filter = &mfilter->u.cfilter;
if (write) {
- vty_out(vty, "%s IP%s access list %s\n",
+ vty_out(vty, "%s %s access list %s\n",
mfilter->cisco ? (filter->extended
? "Extended"
: "Standard")
: "Zebra",
- afi == AFI_IP6 ? "v6" : "",
+ (afi == AFI_IP)
+ ? ("IP")
+ : ((afi == AFI_IP6) ? ("IPv6 ")
+ : ("MAC ")),
access->name);
write = 0;
}
@@ -1710,12 +1786,15 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi)
filter = &mfilter->u.cfilter;
if (write) {
- vty_out(vty, "%s IP%s access list %s\n",
+ vty_out(vty, "%s %s access list %s\n",
mfilter->cisco ? (filter->extended
? "Extended"
: "Standard")
: "Zebra",
- afi == AFI_IP6 ? "v6" : "",
+ (afi == AFI_IP)
+ ? ("IP")
+ : ((afi == AFI_IP6) ? ("IPv6 ")
+ : ("MAC ")),
access->name);
write = 0;
}
@@ -1746,6 +1825,28 @@ static int filter_show(struct vty *vty, const char *name, afi_t afi)
return CMD_SUCCESS;
}
+/* show MAC access list - this only has MAC filters for now*/
+DEFUN (show_mac_access_list,
+ show_mac_access_list_cmd,
+ "show mac access-list",
+ SHOW_STR
+ "mac access lists\n"
+ "List mac access lists\n")
+{
+ return filter_show(vty, NULL, AFI_L2VPN);
+}
+
+DEFUN (show_mac_access_list_name,
+ show_mac_access_list_name_cmd,
+ "show mac access-list WORD",
+ SHOW_STR
+ "mac access lists\n"
+ "List mac access lists\n"
+ "mac address\n")
+{
+ return filter_show(vty, argv[3]->arg, AFI_L2VPN);
+}
+
DEFUN (show_ip_access_list,
show_ip_access_list_cmd,
"show ip access-list",
@@ -1844,10 +1945,17 @@ void config_write_access_zebra(struct vty *vty, struct filter *mfilter)
if (p->prefixlen == 0 && !filter->exact)
vty_out(vty, " any");
- else
+ else if (p->family == AF_INET6 || p->family == AF_INET)
vty_out(vty, " %s/%d%s",
inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ),
p->prefixlen, filter->exact ? " exact-match" : "");
+ else if (p->family == AF_ETHERNET) {
+ if (p->prefixlen == 0)
+ vty_out(vty, " any");
+ else
+ vty_out(vty, " %s", prefix_mac2str(&(p->u.prefix_eth),
+ buf, sizeof(buf)));
+ }
vty_out(vty, "\n");
}
@@ -1866,15 +1974,19 @@ static int config_write_access(struct vty *vty, afi_t afi)
for (access = master->num.head; access; access = access->next) {
if (access->remark) {
vty_out(vty, "%saccess-list %s remark %s\n",
- afi == AFI_IP ? "" : "ipv6 ", access->name,
- access->remark);
+ (afi == AFI_IP) ? ("")
+ : ((afi == AFI_IP6) ? ("ipv6 ")
+ : ("mac ")),
+ access->name, access->remark);
write++;
}
for (mfilter = access->head; mfilter; mfilter = mfilter->next) {
vty_out(vty, "%saccess-list %s %s",
- afi == AFI_IP ? "" : "ipv6 ", access->name,
- filter_type_str(mfilter));
+ (afi == AFI_IP) ? ("")
+ : ((afi == AFI_IP6) ? ("ipv6 ")
+ : ("mac ")),
+ access->name, filter_type_str(mfilter));
if (mfilter->cisco)
config_write_access_cisco(vty, mfilter);
@@ -1888,15 +2000,19 @@ static int config_write_access(struct vty *vty, afi_t afi)
for (access = master->str.head; access; access = access->next) {
if (access->remark) {
vty_out(vty, "%saccess-list %s remark %s\n",
- afi == AFI_IP ? "" : "ipv6 ", access->name,
- access->remark);
+ (afi == AFI_IP) ? ("")
+ : ((afi == AFI_IP6) ? ("ipv6 ")
+ : ("mac ")),
+ access->name, access->remark);
write++;
}
for (mfilter = access->head; mfilter; mfilter = mfilter->next) {
vty_out(vty, "%saccess-list %s %s",
- afi == AFI_IP ? "" : "ipv6 ", access->name,
- filter_type_str(mfilter));
+ (afi == AFI_IP) ? ("")
+ : ((afi == AFI_IP6) ? ("ipv6 ")
+ : ("mac ")),
+ access->name, filter_type_str(mfilter));
if (mfilter->cisco)
config_write_access_cisco(vty, mfilter);
@@ -1909,6 +2025,56 @@ static int config_write_access(struct vty *vty, afi_t afi)
return write;
}
+static struct cmd_node access_mac_node = {
+ ACCESS_MAC_NODE, "", /* Access list has no interface. */
+ 1};
+
+static int config_write_access_mac(struct vty *vty)
+{
+ return config_write_access(vty, AFI_L2VPN);
+}
+
+static void access_list_reset_mac(void)
+{
+ struct access_list *access;
+ struct access_list *next;
+ struct access_master *master;
+
+ master = access_master_get(AFI_L2VPN);
+ if (master == NULL)
+ return;
+
+ for (access = master->num.head; access; access = next) {
+ next = access->next;
+ access_list_delete(access);
+ }
+ for (access = master->str.head; access; access = next) {
+ next = access->next;
+ access_list_delete(access);
+ }
+
+ assert(master->num.head == NULL);
+ assert(master->num.tail == NULL);
+
+ assert(master->str.head == NULL);
+ assert(master->str.tail == NULL);
+}
+
+/* Install vty related command. */
+static void access_list_init_mac(void)
+{
+ install_node(&access_mac_node, config_write_access_mac);
+
+ install_element(ENABLE_NODE, &show_mac_access_list_cmd);
+ install_element(ENABLE_NODE, &show_mac_access_list_name_cmd);
+
+ /* Zebra access-list */
+ install_element(CONFIG_NODE, &mac_access_list_cmd);
+ install_element(CONFIG_NODE, &no_mac_access_list_cmd);
+ install_element(CONFIG_NODE, &mac_access_list_any_cmd);
+ install_element(CONFIG_NODE, &no_mac_access_list_any_cmd);
+}
+
/* Access-list node. */
static struct cmd_node access_node = {ACCESS_NODE,
"", /* Access list has no interface. */
@@ -2050,10 +2216,12 @@ void access_list_init()
{
access_list_init_ipv4();
access_list_init_ipv6();
+ access_list_init_mac();
}
void access_list_reset()
{
access_list_reset_ipv4();
access_list_reset_ipv6();
+ access_list_reset_mac();
}
diff --git a/lib/log.c b/lib/log.c
index 49351a0656..5c89e7080e 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -917,6 +917,7 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY(ZEBRA_GET_LABEL_CHUNK),
DESC_ENTRY(ZEBRA_RELEASE_LABEL_CHUNK),
DESC_ENTRY(ZEBRA_ADVERTISE_ALL_VNI),
+ DESC_ENTRY(ZEBRA_ADVERTISE_DEFAULT_GW),
DESC_ENTRY(ZEBRA_VNI_ADD),
DESC_ENTRY(ZEBRA_VNI_DEL),
DESC_ENTRY(ZEBRA_REMOTE_VTEP_ADD),
diff --git a/lib/prefix.c b/lib/prefix.c
index 33b6ff1987..de521b2e3e 100644
--- a/lib/prefix.c
+++ b/lib/prefix.c
@@ -37,390 +37,262 @@ static const u_char maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
static const struct in6_addr maskbytes6[] = {
/* /0 */ {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /1 */
- {{{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /2 */
- {{{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /3 */
- {{{0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /4 */
- {{{0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /5 */
- {{{0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /6 */
- {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /7 */
- {{{0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /8 */
- {{{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /9 */
- {{{0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /10 */
- {{{0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /11 */
- {{{0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /12 */
- {{{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /13 */
- {{{0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /14 */
- {{{0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /15 */
- {{{0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /16 */
- {{{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /17 */
- {{{0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /18 */
- {{{0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /19 */
- {{{0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /20 */
- {{{0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /21 */
- {{{0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /22 */
- {{{0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /23 */
- {{{0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /24 */
- {{{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /25 */
- {{{0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /26 */
- {{{0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /27 */
- {{{0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /28 */
- {{{0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /29 */
- {{{0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /30 */
- {{{0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /31 */
- {{{0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /32 */
- {{{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /33 */
- {{{0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /34 */
- {{{0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /35 */
- {{{0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /36 */
- {{{0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /37 */
- {{{0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /38 */
- {{{0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /39 */
- {{{0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /40 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /41 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /42 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /43 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /44 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /45 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /46 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /47 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /48 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /49 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /50 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /51 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /52 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /53 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /54 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /55 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /56 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /57 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /58 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /59 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /60 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /61 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /62 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /63 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /64 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /65 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /66 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /67 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /68 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /69 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /70 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /71 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /72 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /73 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /74 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /75 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /76 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /77 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /78 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /79 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /80 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /81 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /82 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /83 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /84 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /85 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /86 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /87 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /88 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x00, 0x00, 0x00, 0x00, 0x00}}},
- /* /89 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0x80, 0x00, 0x00, 0x00, 0x00}}},
- /* /90 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xc0, 0x00, 0x00, 0x00, 0x00}}},
- /* /91 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xe0, 0x00, 0x00, 0x00, 0x00}}},
- /* /92 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xf0, 0x00, 0x00, 0x00, 0x00}}},
- /* /93 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xf8, 0x00, 0x00, 0x00, 0x00}}},
- /* /94 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xfc, 0x00, 0x00, 0x00, 0x00}}},
- /* /95 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xfe, 0x00, 0x00, 0x00, 0x00}}},
- /* /96 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0x00, 0x00, 0x00, 0x00}}},
- /* /97 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0x80, 0x00, 0x00, 0x00}}},
- /* /98 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xc0, 0x00, 0x00, 0x00}}},
- /* /99 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xe0, 0x00, 0x00, 0x00}}},
- /* /100 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xf0, 0x00, 0x00, 0x00}}},
- /* /101 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xf8, 0x00, 0x00, 0x00}}},
- /* /102 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfc, 0x00, 0x00, 0x00}}},
- /* /103 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xfe, 0x00, 0x00, 0x00}}},
- /* /104 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0x00, 0x00, 0x00}}},
- /* /105 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0x80, 0x00, 0x00}}},
- /* /106 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xc0, 0x00, 0x00}}},
- /* /107 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xe0, 0x00, 0x00}}},
- /* /108 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xf0, 0x00, 0x00}}},
- /* /109 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xf8, 0x00, 0x00}}},
- /* /110 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xfc, 0x00, 0x00}}},
- /* /111 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xfe, 0x00, 0x00}}},
- /* /112 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0x00, 0x00}}},
- /* /113 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0x80, 0x00}}},
- /* /114 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xc0, 0x00}}},
- /* /115 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xe0, 0x00}}},
- /* /116 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xf0, 0x00}}},
- /* /117 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xf8, 0x00}}},
- /* /118 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfc, 0x00}}},
- /* /119 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xfe, 0x00}}},
- /* /120 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x00}}},
- /* /121 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0x80}}},
- /* /122 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xc0}}},
- /* /123 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xe0}}},
- /* /124 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xf0}}},
- /* /125 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xf8}}},
- /* /126 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xfc}}},
- /* /127 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xfe}}},
- /* /128 */
- {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
- 0xff, 0xff, 0xff, 0xff, 0xff}}}};
+ /* /1 */ {{{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /2 */ {{{0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /3 */ {{{0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /4 */ {{{0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /5 */ {{{0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /6 */ {{{0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /7 */ {{{0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /8 */ {{{0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /9 */ {{{0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /10 */ {{{0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /11 */ {{{0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /12 */ {{{0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /13 */ {{{0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /14 */ {{{0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /15 */ {{{0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /16 */ {{{0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /17 */ {{{0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /18 */ {{{0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /19 */ {{{0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /20 */ {{{0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /21 */ {{{0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /22 */ {{{0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /23 */ {{{0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /24 */ {{{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /25 */ {{{0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /26 */ {{{0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /27 */ {{{0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /28 */ {{{0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /29 */ {{{0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /30 */ {{{0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /31 */ {{{0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /32 */ {{{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /33 */ {{{0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /34 */ {{{0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /35 */ {{{0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /36 */ {{{0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /37 */ {{{0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /38 */ {{{0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /39 */ {{{0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /40 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /41 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /42 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /43 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /44 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /45 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /46 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /47 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /48 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /49 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /50 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /51 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /52 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /53 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /54 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /55 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /56 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /57 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /58 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /59 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /60 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /61 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /62 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /63 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /64 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /65 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /66 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /67 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /68 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /69 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /70 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /71 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /72 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /73 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /74 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /75 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /76 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /77 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /78 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /79 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /80 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /81 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /82 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /83 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /84 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /85 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /86 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /87 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /88 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00}}},
+ /* /89 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00}}},
+ /* /90 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00, 0x00}}},
+ /* /91 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00, 0x00}}},
+ /* /92 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00, 0x00}}},
+ /* /93 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00}}},
+ /* /94 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00}}},
+ /* /95 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00, 0x00}}},
+ /* /96 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00}}},
+ /* /97 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00}}},
+ /* /98 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00, 0x00}}},
+ /* /99 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00, 0x00}}},
+ /* /100 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00, 0x00}}},
+ /* /101 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00}}},
+ /* /102 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00}}},
+ /* /103 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00, 0x00}}},
+ /* /104 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00}}},
+ /* /105 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00}}},
+ /* /106 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00, 0x00}}},
+ /* /107 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00, 0x00}}},
+ /* /108 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00, 0x00}}},
+ /* /109 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00, 0x00}}},
+ /* /110 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00, 0x00}}},
+ /* /111 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00, 0x00}}},
+ /* /112 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00}}},
+ /* /113 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00}}},
+ /* /114 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x00}}},
+ /* /115 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x00}}},
+ /* /116 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x00}}},
+ /* /117 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x00}}},
+ /* /118 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x00}}},
+ /* /119 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x00}}},
+ /* /120 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}}},
+ /* /121 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80}}},
+ /* /122 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0}}},
+ /* /123 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0}}},
+ /* /124 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0}}},
+ /* /125 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8}}},
+ /* /126 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc}}},
+ /* /127 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe}}},
+ /* /128 */ {{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}};
/* Number of bits in prefix type. */
#ifndef PNBBY
@@ -429,6 +301,18 @@ static const struct in6_addr maskbytes6[] = {
#define MASKBIT(offset) ((0xff << (PNBBY - (offset))) & 0xff)
+static int is_zero_mac(const struct ethaddr *mac)
+{
+ int i = 0;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ if (mac->octet[i])
+ return 0;
+ }
+
+ return 1;
+}
+
unsigned int prefix_bit(const u_char *prefix, const u_char prefixlen)
{
unsigned int offset = prefixlen / 8;
@@ -450,6 +334,8 @@ int str2family(const char *string)
return AF_INET6;
else if (!strcmp("ethernet", string))
return AF_ETHERNET;
+ else if (!strcmp("evpn", string))
+ return AF_EVPN;
return -1;
}
@@ -462,6 +348,7 @@ int afi2family(afi_t afi)
return AF_INET6;
else if (afi == AFI_L2VPN)
return AF_ETHERNET;
+ /* NOTE: EVPN code should NOT use this interface. */
return 0;
}
@@ -471,7 +358,7 @@ afi_t family2afi(int family)
return AFI_IP;
else if (family == AF_INET6)
return AFI_IP6;
- else if (family == AF_ETHERNET)
+ else if (family == AF_ETHERNET || family == AF_EVPN)
return AFI_L2VPN;
return 0;
}
@@ -577,6 +464,9 @@ void prefix_copy(struct prefix *dest, const struct prefix *src)
else if (src->family == AF_INET6)
dest->u.prefix6 = src->u.prefix6;
else if (src->family == AF_ETHERNET) {
+ memcpy(&dest->u.prefix_eth, &src->u.prefix_eth,
+ sizeof(struct ethaddr));
+ } else if (src->family == AF_EVPN) {
memcpy(&dest->u.prefix_evpn, &src->u.prefix_evpn,
sizeof(struct evpn_addr));
} else if (src->family == AF_UNSPEC) {
@@ -615,6 +505,10 @@ int prefix_same(const struct prefix *p1, const struct prefix *p2)
&p2->u.prefix6.s6_addr))
return 1;
if (p1->family == AF_ETHERNET)
+ if (!memcmp(&p1->u.prefix_eth, &p2->u.prefix_eth,
+ sizeof(struct ethaddr)))
+ return 1;
+ if (p1->family == AF_EVPN)
if (!memcmp(&p1->u.prefix_evpn, &p2->u.prefix_evpn,
sizeof(struct evpn_addr)))
return 1;
@@ -679,6 +573,8 @@ int prefix_common_bits(const struct prefix *p1, const struct prefix *p2)
if (p1->family == AF_INET6)
length = IPV6_MAX_BYTELEN;
if (p1->family == AF_ETHERNET)
+ length = ETH_ALEN;
+ if (p1->family == AF_EVPN)
length = 8 * sizeof(struct evpn_addr);
if (p1->family != p2->family || !length)
@@ -707,6 +603,8 @@ const char *prefix_family_str(const struct prefix *p)
return "inet6";
if (p->family == AF_ETHERNET)
return "ether";
+ if (p->family == AF_EVPN)
+ return "evpn";
return "unspec";
}
@@ -783,6 +681,13 @@ int str2prefix_eth(const char *str, struct prefix_eth *p)
const char *str_addr = str;
unsigned int a[6];
int i;
+ bool slash = false;
+
+ if (!strcmp(str, "any")) {
+ memset(p, 0, sizeof(*p));
+ p->family = AF_ETHERNET;
+ return 1;
+ }
/* Find slash inside string. */
pnt = strchr(str, '/');
@@ -800,6 +705,7 @@ int str2prefix_eth(const char *str, struct prefix_eth *p)
*(cp + (pnt - str)) = '\0';
str_addr = cp;
+ slash = true;
}
/* Convert string to prefix. */
@@ -814,6 +720,15 @@ int str2prefix_eth(const char *str, struct prefix_eth *p)
}
p->prefixlen = plen;
p->family = AF_ETHERNET;
+
+ /*
+ * special case to allow old configurations to work
+ * Since all zero's is implicitly meant to allow
+ * a comparison to zero, let's assume
+ */
+ if (!slash && is_zero_mac(&(p->eth_addr)))
+ p->prefixlen = 0;
+
ret = 1;
done:
@@ -1063,6 +978,7 @@ int prefix_blen(const struct prefix *p)
break;
case AF_ETHERNET:
return ETH_ALEN;
+ break;
}
return 0;
}
@@ -1090,7 +1006,7 @@ int str2prefix(const char *str, struct prefix *p)
return 0;
}
-static const char *prefixeth2str(const struct prefix *p, char *str, int size)
+static const char *prefixevpn2str(const struct prefix *p, char *str, int size)
{
u_char family;
char buf[PREFIX2STR_BUFFER];
@@ -1134,12 +1050,8 @@ static const char *prefixeth2str(const struct prefix *p, char *str, int size)
PREFIX2STR_BUFFER),
p->prefixlen);
} else {
- sprintf(str, "UNK AF_ETHER prefix");
- snprintf(str, size, "%02x:%02x:%02x:%02x:%02x:%02x/%d",
- p->u.prefix_eth.octet[0], p->u.prefix_eth.octet[1],
- p->u.prefix_eth.octet[2], p->u.prefix_eth.octet[3],
- p->u.prefix_eth.octet[4], p->u.prefix_eth.octet[5],
- p->prefixlen);
+ sprintf(str, "Unsupported EVPN route type %d",
+ p->u.prefix_evpn.route_type);
}
return str;
@@ -1159,7 +1071,13 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size)
break;
case AF_ETHERNET:
- prefixeth2str(p, str, size);
+ snprintf(str, size, "%s/%d",
+ prefix_mac2str(&p->u.prefix_eth, buf, sizeof(buf)),
+ p->prefixlen);
+ break;
+
+ case AF_EVPN:
+ prefixevpn2str(p, str, size);
break;
default:
diff --git a/lib/prefix.h b/lib/prefix.h
index 5f2b57ccce..f0644ea88e 100644
--- a/lib/prefix.h
+++ b/lib/prefix.h
@@ -116,7 +116,18 @@ struct evpn_addr {
#endif
#endif
-/* IPv4 and IPv6 unified prefix structure. */
+/* The 'family' in the prefix structure is internal to FRR and need not
+ * map to standard OS AF_ definitions except where needed for interacting
+ * with the kernel. However, AF_ definitions are currently in use and
+ * prevalent across the code. Define a new FRR-specific AF for EVPN to
+ * distinguish between 'ethernet' (MAC-only) and 'evpn' prefixes and
+ * ensure it does not conflict with any OS AF_ definition.
+ */
+#if !defined(AF_EVPN)
+#define AF_EVPN (AF_MAX + 1)
+#endif
+
+/* FRR generic prefix structure. */
struct prefix {
u_char family;
u_char prefixlen;
@@ -131,7 +142,7 @@ struct prefix {
struct ethaddr prefix_eth; /* AF_ETHERNET */
u_char val[8];
uintptr_t ptr;
- struct evpn_addr prefix_evpn;
+ struct evpn_addr prefix_evpn; /* AF_EVPN */
} u __attribute__((aligned(8)));
};
@@ -356,6 +367,7 @@ static inline int ipv6_martian(struct in6_addr *addr)
}
extern int all_digit(const char *);
+extern int macstr2prefix_evpn(const char *str, struct prefix_evpn *p);
/* NOTE: This routine expects the address argument in network byte order. */
static inline int ipv4_martian(struct in_addr *addr)
diff --git a/lib/zclient.h b/lib/zclient.h
index 5edb56f517..15d1858d84 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -111,6 +111,7 @@ typedef enum {
ZEBRA_FEC_REGISTER,
ZEBRA_FEC_UNREGISTER,
ZEBRA_FEC_UPDATE,
+ ZEBRA_ADVERTISE_DEFAULT_GW,
ZEBRA_ADVERTISE_ALL_VNI,
ZEBRA_VNI_ADD,
ZEBRA_VNI_DEL,
@@ -305,6 +306,10 @@ struct zapi_pw_status {
uint32_t status;
};
+/* Zebra MAC types */
+#define ZEBRA_MAC_TYPE_STICKY 0x01 /* Sticky MAC*/
+#define ZEBRA_MAC_TYPE_GW 0x02 /* gateway (SVI) mac*/
+
/* Prototypes of zebra client service functions. */
extern struct zclient *zclient_new(struct thread_master *);
extern void zclient_init(struct zclient *, int, u_short);
diff --git a/tools/frr-reload.py b/tools/frr-reload.py
index bfdc08ab4c..43496d4cbf 100755
--- a/tools/frr-reload.py
+++ b/tools/frr-reload.py
@@ -432,6 +432,8 @@ end
ctx_keys.append("address-family ipv6 unicast")
elif line == "address-family ipv4":
ctx_keys.append("address-family ipv4 unicast")
+ elif line == "address-family evpn":
+ ctx_keys.append("address-family l2vpn evpn")
else:
ctx_keys.append(line)
@@ -745,6 +747,37 @@ def ignore_delete_re_add_lines(lines_to_add, lines_to_del):
lines_to_del_to_del.append((ctx_keys, None))
lines_to_add_to_del.append(((tmpline,), None))
+ if (len(ctx_keys) == 3 and
+ ctx_keys[0].startswith('router bgp') and
+ ctx_keys[1] == 'address-family l2vpn evpn' and
+ ctx_keys[2].startswith('vni')):
+
+ re_route_target = re.search('^route-target import (.*)$', line) if line is not None else False
+
+ if re_route_target:
+ rt = re_route_target.group(1).strip()
+ route_target_import_line = line
+ route_target_export_line = "route-target export %s" % rt
+ route_target_both_line = "route-target both %s" % rt
+
+ found_route_target_export_line = line_exist(lines_to_del, ctx_keys, route_target_export_line)
+ found_route_target_both_line = line_exist(lines_to_add, ctx_keys, route_target_both_line)
+
+ '''
+ If the running configs has
+ route-target import 1:1
+ route-target export 1:1
+
+ and the config we are reloading against has
+ route-target both 1:1
+
+ then we can ignore deleting the import/export and ignore adding the 'both'
+ '''
+ if found_route_target_export_line and found_route_target_both_line:
+ lines_to_del_to_del.append((ctx_keys, route_target_import_line))
+ lines_to_del_to_del.append((ctx_keys, route_target_export_line))
+ lines_to_add_to_del.append((ctx_keys, route_target_both_line))
+
if not deleted:
found_add_line = line_exist(lines_to_add, ctx_keys, line)
@@ -822,6 +855,13 @@ def compare_context_objects(newconf, running):
elif "router bgp" in running_ctx_keys[0] and len(running_ctx_keys) > 1 and delete_bgpd:
continue
+ # Delete an entire vni sub-context under "address-family l2vpn evpn"
+ elif ("router bgp" in running_ctx_keys[0] and
+ len(running_ctx_keys) > 2 and
+ running_ctx_keys[1].startswith('address-family l2vpn evpn') and
+ running_ctx_keys[2].startswith('vni ')):
+ lines_to_del.append((running_ctx_keys, None))
+
elif ("router bgp" in running_ctx_keys[0] and
len(running_ctx_keys) > 1 and
running_ctx_keys[1].startswith('address-family')):
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index 76c42173da..6255726c47 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -1164,10 +1164,10 @@ DEFUNSH(VTYSH_BGPD, address_family_evpn, address_family_evpn_cmd,
}
#if defined(HAVE_CUMULUS)
-DEFUNSH(VTYSH_BGPD, address_family_evpn2, address_family_evpn2_cmd,
- "address-family evpn",
- "Enter Address Family command mode\n"
- "EVPN Address family\n")
+DEFUNSH_HIDDEN(VTYSH_BGPD, address_family_evpn2, address_family_evpn2_cmd,
+ "address-family evpn",
+ "Enter Address Family command mode\n"
+ "EVPN Address family\n")
{
vty->node = BGP_EVPN_NODE;
return CMD_SUCCESS;
diff --git a/vtysh/vtysh_config.c b/vtysh/vtysh_config.c
index 43aff0e3a5..c561b5222f 100644
--- a/vtysh/vtysh_config.c
+++ b/vtysh/vtysh_config.c
@@ -224,6 +224,10 @@ void vtysh_config_parse_line(void *arg, const char *line)
strlen("ipv6 access-list"))
== 0)
config = config_get(ACCESS_IPV6_NODE, line);
+ else if (strncmp(line, "mac access-list",
+ strlen("mac access-list"))
+ == 0)
+ config = config_get(ACCESS_MAC_NODE, line);
else if (strncmp(line, "ip prefix-list",
strlen("ip prefix-list"))
== 0)
@@ -302,9 +306,10 @@ void vtysh_config_parse_line(void *arg, const char *line)
#define NO_DELIMITER(I) \
((I) == ACCESS_NODE || (I) == PREFIX_NODE || (I) == IP_NODE \
|| (I) == AS_LIST_NODE || (I) == COMMUNITY_LIST_NODE \
- || (I) == ACCESS_IPV6_NODE || (I) == PREFIX_IPV6_NODE \
- || (I) == SERVICE_NODE || (I) == FORWARDING_NODE || (I) == DEBUG_NODE \
- || (I) == AAA_NODE || (I) == VRF_DEBUG_NODE || (I) == MPLS_NODE)
+ || (I) == ACCESS_IPV6_NODE || (I) == ACCESS_MAC_NODE \
+ || (I) == PREFIX_IPV6_NODE || (I) == SERVICE_NODE \
+ || (I) == FORWARDING_NODE || (I) == DEBUG_NODE || (I) == AAA_NODE \
+ || (I) == VRF_DEBUG_NODE || (I) == MPLS_NODE)
/* Display configuration to file pointer. */
void vtysh_config_dump(FILE *fp)
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index a46657dd2e..0d08155178 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -251,6 +251,8 @@ static void netlink_determine_zebra_iftype(char *kind, zebra_iftype_t *zif_type)
*zif_type = ZEBRA_IF_VLAN;
else if (strcmp(kind, "vxlan") == 0)
*zif_type = ZEBRA_IF_VXLAN;
+ else if (strcmp(kind, "macvlan") == 0)
+ *zif_type = ZEBRA_IF_MACVLAN;
}
// Temporary Assignments to compile on older platforms.
@@ -401,16 +403,19 @@ static int get_iflink_speed(const char *ifname)
/* use ioctl to get IP address of an interface */
sd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sd < 0) {
- zlog_debug("Failure to read interface %s speed: %d %s", ifname,
- errno, safe_strerror(errno));
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("Failure to read interface %s speed: %d %s",
+ ifname, errno, safe_strerror(errno));
return 0;
}
/* Get the current link state for the interface */
rc = ioctl(sd, SIOCETHTOOL, (char *)&ifdata);
if (rc < 0) {
- zlog_debug("IOCTL failure to read interface %s speed: %d %s",
- ifname, errno, safe_strerror(errno));
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug(
+ "IOCTL failure to read interface %s speed: %d %s",
+ ifname, errno, safe_strerror(errno));
ecmd.speed_hi = 0;
ecmd.speed = 0;
}
diff --git a/zebra/interface.h b/zebra/interface.h
index ea72264696..970c3c5292 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -186,11 +186,12 @@ struct rtadvconf {
/* Zebra interface type - ones of interest. */
typedef enum {
- ZEBRA_IF_VXLAN, /* VxLAN interface */
- ZEBRA_IF_VRF, /* VRF device */
- ZEBRA_IF_BRIDGE, /* bridge device */
- ZEBRA_IF_VLAN, /* VLAN sub-interface */
- ZEBRA_IF_OTHER, /* Anything else */
+ ZEBRA_IF_VXLAN, /* VxLAN interface */
+ ZEBRA_IF_VRF, /* VRF device */
+ ZEBRA_IF_BRIDGE, /* bridge device */
+ ZEBRA_IF_VLAN, /* VLAN sub-interface */
+ ZEBRA_IF_MACVLAN, /* MAC VLAN interface*/
+ ZEBRA_IF_OTHER, /* Anything else */
} zebra_iftype_t;
/* Zebra "slave" interface type */
@@ -295,6 +296,9 @@ static inline void zebra_if_set_ziftype(struct interface *ifp,
#define IS_ZEBRA_IF_VXLAN(ifp) \
(((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VXLAN)
+#define IS_ZEBRA_IF_MACVLAN(ifp) \
+ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_MACVLAN)
+
#define IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) \
(((struct zebra_if *)(ifp->info))->zif_slave_type \
== ZEBRA_IF_SLAVE_BRIDGE)
diff --git a/zebra/redistribute.c b/zebra/redistribute.c
index c3bbf40b3f..92da5fe0ce 100644
--- a/zebra/redistribute.c
+++ b/zebra/redistribute.c
@@ -41,6 +41,7 @@
#include "zebra/debug.h"
#include "zebra/router-id.h"
#include "zebra/zebra_memory.h"
+#include "zebra/zebra_vxlan.h"
#define ZEBRA_PTM_SUPPORT
@@ -402,6 +403,8 @@ void zebra_interface_address_add_update(struct interface *ifp,
zlog_warn(
"WARNING: advertising address to clients that is not yet usable.");
+ zebra_vxlan_add_del_gw_macip(ifp, ifc->address, 1);
+
router_id_add_address(ifc);
for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client))
@@ -428,6 +431,8 @@ void zebra_interface_address_delete_update(struct interface *ifp,
prefix2str(p, buf, sizeof(buf)), ifc->ifp->name);
}
+ zebra_vxlan_add_del_gw_macip(ifp, ifc->address, 0);
+
router_id_del_address(ifc);
for (ALL_LIST_ELEMENTS(zebrad.client_list, node, nnode, client))
diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c
index d1ab2dbb85..47cf7a3cbf 100644
--- a/zebra/zebra_mpls.c
+++ b/zebra/zebra_mpls.c
@@ -1321,9 +1321,9 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty)
default:
break;
}
- vty_out(vty, "%s",
- CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) ? " (installed)"
- : "");
+ vty_out(vty, "%s", CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)
+ ? " (installed)"
+ : "");
vty_out(vty, "\n");
}
@@ -2807,6 +2807,8 @@ void zebra_mpls_close_tables(struct zebra_vrf *zvrf)
hash_free(zvrf->lsp_table);
hash_clean(zvrf->slsp_table, NULL);
hash_free(zvrf->slsp_table);
+ route_table_finish(zvrf->fec_table[AFI_IP]);
+ route_table_finish(zvrf->fec_table[AFI_IP6]);
}
/*
diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h
index dca53bb9f3..6e2dc613df 100644
--- a/zebra/zebra_vrf.h
+++ b/zebra/zebra_vrf.h
@@ -107,6 +107,11 @@ struct zebra_vrf {
*/
int advertise_all_vni;
+ /*
+ * Whether we are advertising g/w macip in EVPN or not.
+ */
+ int advertise_gw_macip;
+
/* Route Installs */
uint64_t installs;
uint64_t removals;
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index e260338131..f01f037ed5 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -2285,89 +2285,100 @@ DEFUN (show_vrf,
DEFUN (show_evpn_vni,
show_evpn_vni_cmd,
- "show evpn vni",
+ "show evpn vni [json]",
SHOW_STR
"EVPN\n"
- "VxLAN information\n")
+ "VxLAN information\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
+ u_char uj = use_json(argc, argv);
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_vnis(vty, zvrf);
+ zebra_vxlan_print_vnis(vty, zvrf, uj);
return CMD_SUCCESS;
}
DEFUN (show_evpn_vni_vni,
show_evpn_vni_vni_cmd,
- "show evpn vni " CMD_VNI_RANGE,
+ "show evpn vni " CMD_VNI_RANGE "[json]",
SHOW_STR
"EVPN\n"
"VxLAN Network Identifier\n"
- "VNI number\n")
+ "VNI number\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
vni_t vni;
+ u_char uj = use_json(argc, argv);
vni = strtoul(argv[3]->arg, NULL, 10);
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_vni(vty, zvrf, vni);
+ zebra_vxlan_print_vni(vty, zvrf, vni, uj);
return CMD_SUCCESS;
}
DEFUN (show_evpn_mac_vni,
show_evpn_mac_vni_cmd,
- "show evpn mac vni " CMD_VNI_RANGE,
+ "show evpn mac vni " CMD_VNI_RANGE "[json]",
SHOW_STR
"EVPN\n"
"MAC addresses\n"
"VxLAN Network Identifier\n"
- "VNI number\n")
+ "VNI number\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
vni_t vni;
+ u_char uj = use_json(argc, argv);
vni = strtoul(argv[4]->arg, NULL, 10);
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_macs_vni(vty, zvrf, vni);
+ zebra_vxlan_print_macs_vni(vty, zvrf, vni, uj);
return CMD_SUCCESS;
}
DEFUN (show_evpn_mac_vni_all,
show_evpn_mac_vni_all_cmd,
- "show evpn mac vni all",
+ "show evpn mac vni all [json]",
SHOW_STR
"EVPN\n"
"MAC addresses\n"
"VxLAN Network Identifier\n"
- "All VNIs\n")
+ "All VNIs\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
+ u_char uj = use_json(argc, argv);
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_macs_all_vni(vty, zvrf);
+ zebra_vxlan_print_macs_all_vni(vty, zvrf, uj);
return CMD_SUCCESS;
}
DEFUN (show_evpn_mac_vni_all_vtep,
show_evpn_mac_vni_all_vtep_cmd,
- "show evpn mac vni all vtep A.B.C.D",
+ "show evpn mac vni all vtep A.B.C.D [json]",
SHOW_STR
"EVPN\n"
"MAC addresses\n"
"VxLAN Network Identifier\n"
"All VNIs\n"
"Remote VTEP\n"
- "Remote VTEP IP address\n")
+ "Remote VTEP IP address\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
struct in_addr vtep_ip;
+ u_char uj = use_json(argc, argv);
if (!inet_aton(argv[6]->arg, &vtep_ip)) {
- vty_out(vty, "%% Malformed VTEP IP address\n");
+ if (!uj)
+ vty_out(vty, "%% Malformed VTEP IP address\n");
return CMD_WARNING;
}
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_macs_all_vni_vtep(vty, zvrf, vtep_ip);
+ zebra_vxlan_print_macs_all_vni_vtep(vty, zvrf, vtep_ip, uj);
return CMD_SUCCESS;
}
@@ -2400,112 +2411,125 @@ DEFUN (show_evpn_mac_vni_mac,
DEFUN (show_evpn_mac_vni_vtep,
show_evpn_mac_vni_vtep_cmd,
- "show evpn mac vni " CMD_VNI_RANGE " vtep A.B.C.D",
+ "show evpn mac vni " CMD_VNI_RANGE " vtep A.B.C.D" "[json]",
SHOW_STR
"EVPN\n"
"MAC addresses\n"
"VxLAN Network Identifier\n"
"VNI number\n"
"Remote VTEP\n"
- "Remote VTEP IP address\n")
+ "Remote VTEP IP address\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
vni_t vni;
struct in_addr vtep_ip;
+ u_char uj = use_json(argc, argv);
vni = strtoul(argv[4]->arg, NULL, 10);
if (!inet_aton(argv[6]->arg, &vtep_ip)) {
- vty_out(vty, "%% Malformed VTEP IP address\n");
+ if (!uj)
+ vty_out(vty, "%% Malformed VTEP IP address\n");
return CMD_WARNING;
}
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_macs_vni_vtep(vty, zvrf, vni, vtep_ip);
+ zebra_vxlan_print_macs_vni_vtep(vty, zvrf, vni, vtep_ip, uj);
return CMD_SUCCESS;
}
DEFUN (show_evpn_neigh_vni,
show_evpn_neigh_vni_cmd,
- "show evpn arp-cache vni " CMD_VNI_RANGE,
+ "show evpn arp-cache vni " CMD_VNI_RANGE "[json]",
SHOW_STR
"EVPN\n"
"ARP and ND cache\n"
"VxLAN Network Identifier\n"
- "VNI number\n")
+ "VNI number\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
vni_t vni;
+ u_char uj = use_json(argc, argv);
vni = strtoul(argv[4]->arg, NULL, 10);
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_neigh_vni(vty, zvrf, vni);
+ zebra_vxlan_print_neigh_vni(vty, zvrf, vni, uj);
return CMD_SUCCESS;
}
DEFUN (show_evpn_neigh_vni_all,
show_evpn_neigh_vni_all_cmd,
- "show evpn arp-cache vni all",
+ "show evpn arp-cache vni all [json]",
SHOW_STR
"EVPN\n"
"ARP and ND cache\n"
"VxLAN Network Identifier\n"
- "All VNIs\n")
+ "All VNIs\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
+ u_char uj = use_json(argc, argv);
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_neigh_all_vni(vty, zvrf);
+ zebra_vxlan_print_neigh_all_vni(vty, zvrf, uj);
return CMD_SUCCESS;
}
DEFUN (show_evpn_neigh_vni_neigh,
show_evpn_neigh_vni_neigh_cmd,
- "show evpn arp-cache vni " CMD_VNI_RANGE " ip WORD",
+ "show evpn arp-cache vni " CMD_VNI_RANGE " ip WORD [json]",
SHOW_STR
"EVPN\n"
"ARP and ND cache\n"
"VxLAN Network Identifier\n"
"VNI number\n"
"Neighbor\n"
- "Neighbor address (IPv4 or IPv6 address)\n")
+ "Neighbor address (IPv4 or IPv6 address)\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
vni_t vni;
struct ipaddr ip;
+ u_char uj = use_json(argc, argv);
vni = strtoul(argv[4]->arg, NULL, 10);
if (str2ipaddr(argv[6]->arg, &ip) != 0) {
- vty_out(vty, "%% Malformed Neighbor address\n");
+ if (!uj)
+ vty_out(vty, "%% Malformed Neighbor address\n");
return CMD_WARNING;
}
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_specific_neigh_vni(vty, zvrf, vni, &ip);
+ zebra_vxlan_print_specific_neigh_vni(vty, zvrf, vni, &ip, uj);
return CMD_SUCCESS;
}
DEFUN (show_evpn_neigh_vni_vtep,
show_evpn_neigh_vni_vtep_cmd,
- "show evpn arp-cache vni " CMD_VNI_RANGE " vtep A.B.C.D",
+ "show evpn arp-cache vni " CMD_VNI_RANGE " vtep A.B.C.D [json]",
SHOW_STR
"EVPN\n"
"ARP and ND cache\n"
"VxLAN Network Identifier\n"
"VNI number\n"
"Remote VTEP\n"
- "Remote VTEP IP address\n")
+ "Remote VTEP IP address\n"
+ JSON_STR)
{
struct zebra_vrf *zvrf;
vni_t vni;
struct in_addr vtep_ip;
+ u_char uj = use_json(argc, argv);
vni = strtoul(argv[4]->arg, NULL, 10);
if (!inet_aton(argv[6]->arg, &vtep_ip)) {
- vty_out(vty, "%% Malformed VTEP IP address\n");
+ if (!uj)
+ vty_out(vty, "%% Malformed VTEP IP address\n");
return CMD_WARNING;
}
zvrf = vrf_info_lookup(VRF_DEFAULT);
- zebra_vxlan_print_neigh_vni_vtep(vty, zvrf, vni, vtep_ip);
+ zebra_vxlan_print_neigh_vni_vtep(vty, zvrf, vni, vtep_ip, uj);
return CMD_SUCCESS;
}
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
index 7d265af309..f99c16ae91 100644
--- a/zebra/zebra_vxlan.c
+++ b/zebra/zebra_vxlan.c
@@ -46,6 +46,7 @@
#include "zebra/zebra_vxlan.h"
#include "zebra/zebra_memory.h"
#include "zebra/zebra_l2.h"
+#include "lib/json.h"
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash");
DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP");
@@ -56,24 +57,25 @@ DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor");
/* static function declarations */
-static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt);
+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,
- void *ctxt);
+ void **args);
static void zvni_print_mac(zebra_mac_t *mac, void *ctxt);
static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt);
static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt);
-static void zvni_print(zebra_vni_t *zvni, void *ctxt);
-static void zvni_print_hash(struct hash_backet *backet, void *ctxt);
+static void zvni_print(zebra_vni_t *zvni, void **ctxt);
+static void zvni_print_hash(struct hash_backet *backet, void *ctxt[]);
static int zvni_macip_send_msg_to_client(struct zebra_vrf *zvrf, vni_t vni,
struct ethaddr *macaddr,
- struct ipaddr *ip, u_char sticky,
+ struct ipaddr *ip, u_char flags,
u_int16_t cmd);
static unsigned int neigh_hash_keymake(void *p);
static int neigh_cmp(const void *p1, const void *p2);
static void *zvni_neigh_alloc(void *p);
-static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip);
+static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip,
+ struct ethaddr *mac);
static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n);
static int zvni_neigh_del_hash_entry(struct hash_backet *backet, void *arg);
static void zvni_neigh_del_from_vtep(zebra_vni_t *zvni, int uninstall,
@@ -83,10 +85,10 @@ static void zvni_neigh_del_all(struct zebra_vrf *zvrf, zebra_vni_t *zvni,
static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip);
static int zvni_neigh_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip,
- struct ethaddr *macaddr);
+ struct ethaddr *macaddr, u_char flags);
static int zvni_neigh_send_del_to_client(struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip,
- struct ethaddr *macaddr);
+ struct ethaddr *macaddr, u_char flags);
static int zvni_neigh_install(zebra_vni_t *zvni, zebra_neigh_t *n);
static int zvni_neigh_uninstall(zebra_vni_t *zvni, zebra_neigh_t *n);
static zebra_vni_t *zvni_map_svi(struct interface *ifp,
@@ -106,9 +108,9 @@ static void zvni_mac_del_all(struct zebra_vrf *zvrf, zebra_vni_t *zvni,
int uninstall, int upd_client, u_int32_t flags);
static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *macaddr);
static int zvni_mac_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni,
- struct ethaddr *macaddr, u_char sticky);
+ struct ethaddr *macaddr, u_char flags);
static int zvni_mac_send_del_to_client(struct zebra_vrf *zvrf, vni_t vni,
- struct ethaddr *macaddr, u_char sticky);
+ struct ethaddr *macaddr, u_char flags);
static zebra_vni_t *zvni_map_vlan(struct interface *ifp,
struct interface *br_if, vlanid_t vid);
static int zvni_mac_install(zebra_vni_t *zvni, zebra_mac_t *mac);
@@ -131,10 +133,31 @@ static int zvni_vtep_del(zebra_vni_t *zvni, zebra_vtep_t *zvtep);
static int zvni_vtep_del_all(zebra_vni_t *zvni, int uninstall);
static int zvni_vtep_install(zebra_vni_t *zvni, struct in_addr *vtep_ip);
static int zvni_vtep_uninstall(zebra_vni_t *zvni, struct in_addr *vtep_ip);
-
+static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni);
+static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni);
+static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
+ struct ethaddr *macaddr, struct ipaddr *ip);
+static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
+ struct ipaddr *ip);
+struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp);
+static int advertise_gw_macip_enabled(struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni);
+static void zvni_deref_ip2mac(zebra_vni_t *zvni, zebra_mac_t *mac,
+ int uninstall);
/* Private functions */
+static int advertise_gw_macip_enabled(struct zebra_vrf *zvrf, zebra_vni_t *zvni)
+{
+ if (zvrf && zvrf->advertise_gw_macip)
+ return 1;
+
+ if (zvni && zvni->advertise_gw_macip)
+ return 1;
+
+ return 0;
+}
+
/*
* Helper function to determine maximum width of neighbor IP address for
* display - just because we're dealing with IPv6 addresses that can
@@ -159,18 +182,37 @@ static void zvni_find_neigh_addr_width(struct hash_backet *backet, void *ctxt)
/*
* Print a specific neighbor entry.
*/
-static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt)
+static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt, json_object *json)
{
struct vty *vty;
char buf1[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
- ipaddr2str(&n->ip, buf2, sizeof(buf2)), vty = (struct vty *)ctxt;
- vty_out(vty, "IP: %s\n", ipaddr2str(&n->ip, buf2, sizeof(buf2)));
- vty_out(vty, " MAC: %s", prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
- if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
- vty_out(vty, " Remote VTEP: %s", inet_ntoa(n->r_vtep_ip));
- vty_out(vty, "\n");
+ ipaddr2str(&n->ip, buf2, sizeof(buf2));
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1));
+ vty = (struct vty *)ctxt;
+ if (json == NULL) {
+ vty_out(vty, "IP: %s\n",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ vty_out(vty, " MAC: %s",
+ prefix_mac2str(&n->emac, buf1, sizeof(buf1)));
+ } else {
+ json_object_string_add(json, "ip", buf2);
+ json_object_string_add(json, "mac", buf1);
+ }
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ if (json == NULL) {
+ vty_out(vty, " Remote VTEP: %s",
+ inet_ntoa(n->r_vtep_ip));
+ vty_out(vty, " State: %s", IS_ZEBRA_NEIGH_ACTIVE(n)
+ ? "Active"
+ : "Inactive");
+ } else
+ json_object_string_add(json, "remoteVtep",
+ inet_ntoa(n->r_vtep_ip));
+ }
+ if (json == NULL)
+ vty_out(vty, "\n");
}
/*
@@ -179,65 +221,115 @@ static void zvni_print_neigh(zebra_neigh_t *n, void *ctxt)
static void zvni_print_neigh_hash(struct hash_backet *backet, void *ctxt)
{
struct vty *vty;
+ json_object *json_vni = NULL, *json_row = NULL;
zebra_neigh_t *n;
char buf1[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
struct neigh_walk_ctx *wctx = ctxt;
vty = wctx->vty;
+ json_vni = wctx->json;
n = (zebra_neigh_t *)backet->data;
if (!n)
return;
+ if (json_vni)
+ json_row = json_object_new_object();
+
prefix_mac2str(&n->emac, buf1, sizeof(buf1));
ipaddr2str(&n->ip, buf2, sizeof(buf2));
if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)
&& !(wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP)) {
- vty_out(vty, "%*s %-6s %-17s\n", -wctx->addr_width, buf2,
- "local", buf1);
+ if (json_vni == NULL) {
+ vty_out(vty, "%*s %-6s %-17s\n", -wctx->addr_width,
+ buf2, "local", buf1);
+ } else {
+ json_object_string_add(json_row, "type", "local");
+ json_object_string_add(json_row, "mac", buf1);
+ }
wctx->count++;
} else {
if (wctx->flags & SHOW_REMOTE_NEIGH_FROM_VTEP) {
if (IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip)) {
- if (wctx->count == 0)
+ if (json_vni == NULL) {
+ if (wctx->count == 0)
+ vty_out(vty,
+ "%*s %-6s %-17s %-21s\n",
+ -wctx->addr_width,
+ "Neighbor", "Type",
+ "MAC", "Remote VTEP");
vty_out(vty, "%*s %-6s %-17s %-21s\n",
- -wctx->addr_width, "Neighbor",
- "Type", "MAC", "Remote VTEP");
+ -wctx->addr_width, buf2,
+ "remote", buf1,
+ inet_ntoa(n->r_vtep_ip));
+ } else {
+ json_object_string_add(json_row, "type",
+ "remote");
+ json_object_string_add(json_row, "mac",
+ buf1);
+ json_object_string_add(
+ json_row, "remoteVtep",
+ inet_ntoa(n->r_vtep_ip));
+ }
+ wctx->count++;
+ }
+ } else {
+ if (json_vni == NULL) {
vty_out(vty, "%*s %-6s %-17s %-21s\n",
-wctx->addr_width, buf2, "remote", buf1,
inet_ntoa(n->r_vtep_ip));
- wctx->count++;
+ } else {
+ json_object_string_add(json_row, "type",
+ "remote");
+ json_object_string_add(json_row, "mac", buf1);
+ json_object_string_add(json_row, "remoteVtep",
+ inet_ntoa(n->r_vtep_ip));
}
- } else {
- vty_out(vty, "%*s %-6s %-17s %-21s\n",
- -wctx->addr_width, buf2, "remote", buf1,
- inet_ntoa(n->r_vtep_ip));
wctx->count++;
}
}
+
+ if (json_vni)
+ json_object_object_add(json_vni, buf2, json_row);
}
/*
* Print neighbors for all VNI.
*/
static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
- void *ctxt)
+ void **args)
{
struct vty *vty;
+ json_object *json = NULL, *json_vni = NULL;
zebra_vni_t *zvni;
u_int32_t num_neigh;
struct neigh_walk_ctx wctx;
+ char vni_str[VNI_STR_LEN];
+
+ vty = (struct vty *)args[0];
+ json = (json_object *)args[1];
- vty = (struct vty *)ctxt;
zvni = (zebra_vni_t *)backet->data;
- if (!zvni)
+ if (!zvni) {
+ if (json)
+ vty_out(vty, "{}\n");
return;
-
+ }
num_neigh = hashcount(zvni->neigh_table);
- vty_out(vty, "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n",
- zvni->vni, num_neigh);
- if (!num_neigh)
+ if (json == NULL)
+ vty_out(vty,
+ "\nVNI %u #ARP (IPv4 and IPv6, local and remote) %u\n\n",
+ zvni->vni, num_neigh);
+ else {
+ json_vni = json_object_new_object();
+ json_object_int_add(json_vni, "numArpNd", num_neigh);
+ snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni);
+ }
+ if (!num_neigh) {
+ if (json)
+ json_object_object_add(json, vni_str, json_vni);
return;
+ }
/* Since we have IPv6 addresses to deal with which can vary widely in
* size, we try to be a bit more elegant in display by first computing
@@ -247,11 +339,16 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
wctx.zvni = zvni;
wctx.vty = vty;
wctx.addr_width = 15;
+ wctx.json = json_vni;
hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
- vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", "Type",
- "MAC", "Remote VTEP");
+ if (json == NULL)
+ vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP",
+ "Type", "MAC", "Remote VTEP");
hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+
+ if (json)
+ json_object_object_add(json, vni_str, json_vni);
}
/*
@@ -260,7 +357,10 @@ static void zvni_print_neigh_hash_all_vni(struct hash_backet *backet,
static void zvni_print_mac(zebra_mac_t *mac, void *ctxt)
{
struct vty *vty;
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
char buf1[20];
+ char buf2[INET6_ADDRSTRLEN];
vty = (struct vty *)ctxt;
vty_out(vty, "MAC: %s",
@@ -278,11 +378,30 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt)
vty_out(vty, " Intf: %s(%u)", ifp->name, ifindex);
if (mac->fwd_info.local.vid)
vty_out(vty, " VLAN: %u", mac->fwd_info.local.vid);
- } else {
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
vty_out(vty, " Remote VTEP: %s",
inet_ntoa(mac->fwd_info.r_vtep_ip));
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_AUTO)) {
+ vty_out(vty, " Auto Mac ");
+ }
+ vty_out(vty, " ARP ref: %u\n", mac->neigh_refcnt);
+
+ /* print all the associated neigh */
+ vty_out(vty, " Neighbors:\n");
+ if (!listcount(mac->neigh_list))
+ vty_out(vty, " No Neighbors\n");
+ else {
+ for (ALL_LIST_ELEMENTS_RO(mac->neigh_list, node, n)) {
+ vty_out(vty, " %s %s\n",
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)),
+ CHECK_FLAG(n->flags, ZEBRA_MAC_LOCAL)
+ ? (IS_ZEBRA_NEIGH_ACTIVE(n)
+ ? "Active"
+ : "Inactive")
+ : "");
+ }
}
- vty_out(vty, " ARP ref: %u", mac->neigh_refcnt);
+
vty_out(vty, "\n");
}
@@ -292,16 +411,22 @@ static void zvni_print_mac(zebra_mac_t *mac, void *ctxt)
static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt)
{
struct vty *vty;
+ json_object *json_mac_hdr = NULL, *json_mac = NULL;
zebra_mac_t *mac;
char buf1[20];
struct mac_walk_ctx *wctx = ctxt;
vty = wctx->vty;
+ json_mac_hdr = wctx->json;
mac = (zebra_mac_t *)backet->data;
if (!mac)
return;
prefix_mac2str(&mac->macaddr, buf1, sizeof(buf1));
+
+ if (json_mac_hdr)
+ json_mac = json_object_new_object();
+
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL)
&& !(wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP)) {
struct zebra_ns *zns;
@@ -315,29 +440,70 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt)
if (!ifp) // unexpected
return;
vid = mac->fwd_info.local.vid;
- vty_out(vty, "%-17s %-6s %-21s", buf1, "local", ifp->name);
- if (vid)
- vty_out(vty, " %-5u", vid);
- vty_out(vty, "\n");
+ if (json_mac_hdr == NULL)
+ vty_out(vty, "%-17s %-6s %-21s", buf1, "local",
+ ifp->name);
+ else {
+ json_object_string_add(json_mac, "type", "local");
+ json_object_string_add(json_mac, "intf", ifp->name);
+ }
+ if (vid) {
+ if (json_mac_hdr == NULL)
+ vty_out(vty, " %-5u", vid);
+ else
+ json_object_int_add(json_mac, "vlan", vid);
+ }
+ if (json_mac_hdr == NULL)
+ vty_out(vty, "\n");
+ else
+ json_object_object_add(json_mac_hdr, buf1, json_mac);
wctx->count++;
- } else {
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
if (wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP) {
if (IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip,
&wctx->r_vtep_ip)) {
if (wctx->count == 0) {
- vty_out(vty, "\nVNI %u",
- wctx->zvni->vni);
- vty_out(vty, "%-17s %-6s %-21s %-5s",
- "MAC", "Type",
- "Intf/Remote VTEP", "VLAN");
+ if (json_mac_hdr == NULL) {
+ vty_out(vty, "\nVNI %u\n\n",
+ wctx->zvni->vni);
+ vty_out(vty,
+ "%-17s %-6s %-21s %-5s\n",
+ "MAC", "Type",
+ "Intf/Remote VTEP",
+ "VLAN");
+ }
+ }
+ if (json_mac_hdr == NULL)
+ vty_out(vty, "%-17s %-6s %-21s\n", buf1,
+ "remote",
+ inet_ntoa(mac->fwd_info
+ .r_vtep_ip));
+ else {
+ json_object_string_add(json_mac, "type",
+ "remote");
+ json_object_string_add(
+ json_mac, "remoteVtep",
+ inet_ntoa(mac->fwd_info
+ .r_vtep_ip));
+ json_object_object_add(json_mac_hdr,
+ buf1, json_mac);
}
- vty_out(vty, "%-17s %-6s %-21s", buf1, "remote",
- inet_ntoa(mac->fwd_info.r_vtep_ip));
wctx->count++;
}
} else {
- vty_out(vty, "%-17s %-6s %-21s", buf1, "remote",
- inet_ntoa(mac->fwd_info.r_vtep_ip));
+ if (json_mac_hdr == NULL)
+ vty_out(vty, "%-17s %-6s %-21s\n", buf1,
+ "remote",
+ inet_ntoa(mac->fwd_info.r_vtep_ip));
+ else {
+ json_object_string_add(json_mac, "type",
+ "remote");
+ json_object_string_add(
+ json_mac, "remoteVtep",
+ inet_ntoa(mac->fwd_info.r_vtep_ip));
+ json_object_object_add(json_mac_hdr, buf1,
+ json_mac);
+ }
wctx->count++;
}
}
@@ -349,15 +515,22 @@ static void zvni_print_mac_hash(struct hash_backet *backet, void *ctxt)
static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt)
{
struct vty *vty;
+ json_object *json = NULL, *json_vni = NULL;
+ json_object *json_mac = NULL;
zebra_vni_t *zvni;
u_int32_t num_macs;
struct mac_walk_ctx *wctx = ctxt;
+ char vni_str[VNI_STR_LEN];
vty = (struct vty *)wctx->vty;
+ json = (struct json_object *)wctx->json;
zvni = (zebra_vni_t *)backet->data;
- if (!zvni)
+ if (!zvni) {
+ if (json)
+ vty_out(vty, "{}\n");
return;
+ }
wctx->zvni = zvni;
/*We are iterating over a new VNI, set the count to 0*/
@@ -366,59 +539,119 @@ static void zvni_print_mac_hash_all_vni(struct hash_backet *backet, void *ctxt)
num_macs = hashcount(zvni->mac_table);
if (!num_macs)
return;
- if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) {
- vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n",
- zvni->vni, num_macs);
- vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type",
- "Intf/Remote VTEP", "VLAN");
+
+ if (json) {
+ json_vni = json_object_new_object();
+ json_mac = json_object_new_object();
+ snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni);
}
+ if (!CHECK_FLAG(wctx->flags, SHOW_REMOTE_MAC_FROM_VTEP)) {
+ if (json == NULL) {
+ vty_out(vty, "\nVNI %u #MACs (local and remote) %u\n\n",
+ zvni->vni, num_macs);
+ vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type",
+ "Intf/Remote VTEP", "VLAN");
+ } else
+ json_object_int_add(json_vni, "numMacs", num_macs);
+ }
+ /* assign per-vni to wctx->json object to fill macs
+ * under the vni. Re-assign primary json object to fill
+ * next vni information.
+ */
+ wctx->json = json_mac;
hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx);
+ wctx->json = json;
+ if (json) {
+ if (wctx->count)
+ json_object_object_add(json_vni, "macs", json_mac);
+ json_object_object_add(json, vni_str, json_vni);
+ }
}
/*
* Print a specific VNI entry.
*/
-static void zvni_print(zebra_vni_t *zvni, void *ctxt)
+static void zvni_print(zebra_vni_t *zvni, void **ctxt)
{
struct vty *vty;
zebra_vtep_t *zvtep;
u_int32_t num_macs;
u_int32_t num_neigh;
+ json_object *json = NULL;
+ json_object *json_vtep_list = NULL;
+ json_object *json_ip_str = NULL;
- vty = (struct vty *)ctxt;
+ vty = ctxt[0];
+ json = ctxt[1];
+
+ if (json == NULL)
+ vty_out(vty, "VNI: %u\n", zvni->vni);
+ else
+ json_object_int_add(json, "vni", zvni->vni);
- vty_out(vty, "VNI: %u\n", zvni->vni);
if (!zvni->vxlan_if) { // unexpected
- vty_out(vty, " VxLAN interface: unknown\n");
+ if (json == NULL)
+ vty_out(vty, " VxLAN interface: unknown\n");
return;
}
- vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s\n",
- zvni->vxlan_if->name, zvni->vxlan_if->ifindex,
- inet_ntoa(zvni->local_vtep_ip));
-
+ num_macs = hashcount(zvni->mac_table);
+ num_neigh = hashcount(zvni->neigh_table);
+ if (json == NULL)
+ vty_out(vty, " VxLAN interface: %s ifIndex: %u VTEP IP: %s\n",
+ zvni->vxlan_if->name, zvni->vxlan_if->ifindex,
+ inet_ntoa(zvni->local_vtep_ip));
+ else {
+ json_object_string_add(json, "vxlanInterface",
+ zvni->vxlan_if->name);
+ json_object_int_add(json, "ifindex", zvni->vxlan_if->ifindex);
+ json_object_string_add(json, "vtepIp",
+ inet_ntoa(zvni->local_vtep_ip));
+ json_object_string_add(json, "advertiseGatewayMacip",
+ zvni->advertise_gw_macip ? "Yes" : "No");
+ json_object_int_add(json, "numMacs", num_macs);
+ json_object_int_add(json, "numArpNd", num_neigh);
+ }
if (!zvni->vteps) {
- vty_out(vty, " No remote VTEPs known for this VNI\n");
+ if (json == NULL)
+ vty_out(vty, " No remote VTEPs known for this VNI\n");
} else {
- vty_out(vty, " Remote VTEPs for this VNI:\n");
- for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next)
- vty_out(vty, " %s\n", inet_ntoa(zvtep->vtep_ip));
+ if (json == NULL)
+ vty_out(vty, " Remote VTEPs for this VNI:\n");
+ else
+ json_vtep_list = json_object_new_array();
+ for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) {
+ if (json == NULL)
+ vty_out(vty, " %s\n",
+ inet_ntoa(zvtep->vtep_ip));
+ else {
+ json_ip_str = json_object_new_string(
+ inet_ntoa(zvtep->vtep_ip));
+ json_object_array_add(json_vtep_list,
+ json_ip_str);
+ }
+ }
+ if (json)
+ json_object_object_add(json, "numRemoteVteps",
+ json_vtep_list);
+ }
+ if (json == NULL) {
+ vty_out(vty,
+ " Number of MACs (local and remote) known for this VNI: %u\n",
+ num_macs);
+ vty_out(vty,
+ " Number of ARPs (IPv4 and IPv6, local and remote) "
+ "known for this VNI: %u\n",
+ num_neigh);
+ vty_out(vty, " Advertise-gw-macip: %s\n",
+ zvni->advertise_gw_macip ? "Yes" : "No");
}
- num_macs = hashcount(zvni->mac_table);
- vty_out(vty,
- " Number of MACs (local and remote) known for this VNI: %u\n",
- num_macs);
- num_neigh = hashcount(zvni->neigh_table);
- vty_out(vty,
- " Number of ARPs (IPv4 and IPv6, local and remote) "
- "known for this VNI: %u",
- num_neigh);
}
/*
* Print a VNI hash entry - called for display of all VNIs.
*/
-static void zvni_print_hash(struct hash_backet *backet, void *ctxt)
+static void zvni_print_hash(struct hash_backet *backet, void *ctxt[])
{
struct vty *vty;
zebra_vni_t *zvni;
@@ -426,8 +659,14 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt)
u_int32_t num_vteps = 0;
u_int32_t num_macs = 0;
u_int32_t num_neigh = 0;
+ json_object *json = NULL;
+ json_object *json_vni = NULL;
+ json_object *json_ip_str = NULL;
+ json_object *json_vtep_list = NULL;
+
+ vty = ctxt[0];
+ json = ctxt[1];
- vty = (struct vty *)ctxt;
zvni = (zebra_vni_t *)backet->data;
if (!zvni)
return;
@@ -440,9 +679,36 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt)
num_macs = hashcount(zvni->mac_table);
num_neigh = hashcount(zvni->neigh_table);
- vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni,
- zvni->vxlan_if ? zvni->vxlan_if->name : "unknown",
- inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh, num_vteps);
+ if (json == NULL)
+ vty_out(vty, "%-10u %-21s %-15s %-8u %-8u %-15u\n", zvni->vni,
+ zvni->vxlan_if ? zvni->vxlan_if->name : "unknown",
+ inet_ntoa(zvni->local_vtep_ip), num_macs, num_neigh,
+ num_vteps);
+ else {
+ char vni_str[VNI_STR_LEN];
+ snprintf(vni_str, VNI_STR_LEN, "%u", zvni->vni);
+ json_vni = json_object_new_object();
+ json_object_string_add(json_vni, "vxlanIf",
+ zvni->vxlan_if ? zvni->vxlan_if->name
+ : "unknown");
+ json_object_string_add(json_vni, "vtepIp",
+ inet_ntoa(zvni->local_vtep_ip));
+ json_object_int_add(json_vni, "numMacs", num_macs);
+ json_object_int_add(json_vni, "numArpNd", num_neigh);
+ json_object_int_add(json_vni, "numRemoteVteps", num_vteps);
+ if (num_vteps) {
+ json_vtep_list = json_object_new_array();
+ for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next) {
+ json_ip_str = json_object_new_string(
+ inet_ntoa(zvtep->vtep_ip));
+ json_object_array_add(json_vtep_list,
+ json_ip_str);
+ }
+ json_object_object_add(json_vni, "remoteVteps",
+ json_vtep_list);
+ }
+ json_object_object_add(json, vni_str, json_vni);
+ }
}
/*
@@ -450,7 +716,7 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt)
*/
static int zvni_macip_send_msg_to_client(struct zebra_vrf *zvrf, vni_t vni,
struct ethaddr *macaddr,
- struct ipaddr *ip, u_char sticky,
+ struct ipaddr *ip, u_char flags,
u_int16_t cmd)
{
struct zserv *client;
@@ -483,19 +749,18 @@ static int zvni_macip_send_msg_to_client(struct zebra_vrf *zvrf, vni_t vni,
} else
stream_putl(s, 0); /* Just MAC. */
- stream_putc(s, sticky); /* Sticky MAC? */
+ stream_putc(s, flags); /* sticky mac/gateway mac */
/* Write packet size. */
stream_putw_at(s, 0, stream_get_endp(s));
if (IS_ZEBRA_DEBUG_VXLAN)
- zlog_debug("%u:Send MACIP %s %sMAC %s IP %s VNI %u to %s",
- zvrf_id(zvrf),
- (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
- sticky ? "sticky " : "",
- prefix_mac2str(macaddr, buf, sizeof(buf)),
- ipaddr2str(ip, buf2, sizeof(buf2)), vni,
- zebra_route_string(client->proto));
+ zlog_debug(
+ "%u:Send MACIP %s flags 0x%x MAC %s IP %s VNI %u to %s",
+ zvrf_id(zvrf), (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
+ flags, prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipaddr2str(ip, buf2, sizeof(buf2)), vni,
+ zebra_route_string(client->proto));
if (cmd == ZEBRA_MACIP_ADD)
client->macipadd_cnt++;
@@ -554,16 +819,26 @@ static void *zvni_neigh_alloc(void *p)
/*
* Add neighbor entry.
*/
-static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip)
+static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip,
+ struct ethaddr *mac)
{
zebra_neigh_t tmp_n;
zebra_neigh_t *n = NULL;
+ zebra_mac_t *zmac = NULL;
memset(&tmp_n, 0, sizeof(zebra_neigh_t));
memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
n = hash_get(zvni->neigh_table, &tmp_n, zvni_neigh_alloc);
assert(n);
+ memcpy(&n->emac, mac, ETH_ALEN);
+ n->state = ZEBRA_NEIGH_INACTIVE;
+
+ /* Associate the neigh to mac */
+ zmac = zvni_mac_lookup(zvni, mac);
+ if (zmac)
+ listnode_add_sort(zmac->neigh_list, n);
+
return n;
}
@@ -573,6 +848,11 @@ static zebra_neigh_t *zvni_neigh_add(zebra_vni_t *zvni, struct ipaddr *ip)
static int zvni_neigh_del(zebra_vni_t *zvni, zebra_neigh_t *n)
{
zebra_neigh_t *tmp_n;
+ zebra_mac_t *zmac = NULL;
+
+ zmac = zvni_mac_lookup(zvni, &n->emac);
+ if (zmac)
+ listnode_delete(zmac->neigh_list, n);
/* Free the VNI hash entry and allocated memory. */
tmp_n = hash_release(zvni->neigh_table, n);
@@ -597,8 +877,9 @@ static int zvni_neigh_del_hash_entry(struct hash_backet *backet, void *arg)
&& (n->flags & ZEBRA_NEIGH_REMOTE)
&& IPV4_ADDR_SAME(&n->r_vtep_ip, &wctx->r_vtep_ip))) {
if (wctx->upd_client && (n->flags & ZEBRA_NEIGH_LOCAL))
- zvni_neigh_send_del_to_client(
- wctx->zvrf, wctx->zvni->vni, &n->ip, &n->emac);
+ zvni_neigh_send_del_to_client(wctx->zvrf,
+ wctx->zvni->vni, &n->ip,
+ &n->emac, 0);
if (wctx->uninstall)
zvni_neigh_uninstall(wctx->zvni, n);
@@ -671,14 +952,154 @@ static zebra_neigh_t *zvni_neigh_lookup(zebra_vni_t *zvni, struct ipaddr *ip)
return n;
}
+/* Process all neigh associated to a mac upon local mac add event */
+static void zvni_process_neigh_on_local_mac_add(struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ /* MAC is learnt locally, program all inactive neigh
+ * pointing to this mac */
+ if (IS_ZEBRA_NEIGH_INACTIVE(n)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) on VNI %u is now ACTIVE",
+ zvrf_id(zvrf),
+ ipaddr2str(&n->ip, buf2,
+ sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni);
+
+ ZEBRA_NEIGH_SET_ACTIVE(n);
+ zvni_neigh_send_add_to_client(
+ zvrf, zvni->vni, &n->ip, &n->emac, 0);
+ } else {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) on VNI %u should NOT be ACTIVE",
+ zvrf_id(zvrf),
+ ipaddr2str(&n->ip, buf2,
+ sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni);
+ }
+ } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ /* TODO: assume the neigh has moved too ?? */
+ }
+ }
+}
+
+/* Process all neigh associated to a mac upon local mac del event */
+static void zvni_process_neigh_on_local_mac_del(struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) on VNI %u is now INACTIVE",
+ zvrf_id(zvrf),
+ ipaddr2str(&n->ip, buf2,
+ sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni);
+
+ ZEBRA_NEIGH_SET_INACTIVE(n);
+ zvni_neigh_send_del_to_client(
+ zvrf, zvni->vni, &n->ip, &n->emac, 0);
+ }
+ } else if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_err(
+ "%u: local MAC %s getting deleted on VNI %u has remote neigh %s",
+ zvrf_id(zvrf),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni,
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ }
+ }
+}
+
+/* process all neigh associated to a mac entry upon remote mac add */
+static void zvni_process_neigh_on_remote_mac_add(struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (IS_ZEBRA_NEIGH_ACTIVE(n)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) on VNI %u INACTIVE",
+ zvrf_id(zvrf),
+ ipaddr2str(&n->ip, buf2,
+ sizeof(buf2)),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni);
+
+ ZEBRA_NEIGH_SET_INACTIVE(n);
+ zvni_neigh_send_del_to_client(
+ zvrf, zvni->vni, &n->ip, &n->emac, 0);
+ }
+ }
+ }
+}
+
+/* process all neigh associated to mac entry upon remote mac del */
+static void zvni_process_neigh_on_remote_mac_del(struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni,
+ zebra_mac_t *zmac)
+{
+ zebra_neigh_t *n = NULL;
+ struct listnode *node = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ for (ALL_LIST_ELEMENTS_RO(zmac->neigh_list, node, n)) {
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_err(
+ "%u: remote MAC %s getting deleted on VNI %u has local neigh %s",
+ zvrf_id(zvrf),
+ prefix_mac2str(&n->emac, buf,
+ sizeof(buf)),
+ zvni->vni,
+ ipaddr2str(&n->ip, buf2, sizeof(buf2)));
+ }
+ }
+}
+
/*
* Inform BGP about local neighbor addition.
*/
static int zvni_neigh_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip,
- struct ethaddr *macaddr)
+ struct ethaddr *macaddr, u_char flags)
{
- return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, ip, 0,
+ return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, ip, flags,
ZEBRA_MACIP_ADD);
}
@@ -687,9 +1108,9 @@ static int zvni_neigh_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni,
*/
static int zvni_neigh_send_del_to_client(struct zebra_vrf *zvrf, vni_t vni,
struct ipaddr *ip,
- struct ethaddr *macaddr)
+ struct ethaddr *macaddr, u_char flags)
{
- return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, ip, 0,
+ return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, ip, flags,
ZEBRA_MACIP_DEL);
}
@@ -770,6 +1191,309 @@ static void zvni_install_neigh_hash(struct hash_backet *backet, void *ctxt)
zvni_neigh_install(wctx->zvni, n);
}
+/* Get the VRR interface for SVI if any */
+struct interface *zebra_get_vrr_intf_for_svi(struct interface *ifp)
+{
+ struct zebra_vrf *zvrf = NULL;
+ struct interface *tmp_if = NULL;
+ struct zebra_if *zif = NULL;
+ struct listnode *node;
+
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ for (ALL_LIST_ELEMENTS_RO(vrf_iflist(zvrf_id(zvrf)), node, tmp_if)) {
+ zif = tmp_if->info;
+ if (!zif)
+ continue;
+
+ if (!IS_ZEBRA_IF_MACVLAN(tmp_if))
+ continue;
+
+ if (zif->link == ifp)
+ return tmp_if;
+ }
+
+ return NULL;
+}
+
+static int zvni_del_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni)
+{
+ struct zebra_vrf *zvrf = NULL;
+ struct listnode *cnode = NULL, *cnnode = NULL;
+ struct connected *c = NULL;
+ struct ethaddr macaddr;
+
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ if (!zvrf)
+ return -1;
+
+ memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
+ struct ipaddr ip;
+
+ memset(&ip, 0, sizeof(struct ipaddr));
+ if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL))
+ continue;
+
+ if (c->address->family == AF_INET) {
+ ip.ipa_type = IPADDR_V4;
+ memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4),
+ sizeof(struct in_addr));
+ } else if (c->address->family == AF_INET6) {
+ ip.ipa_type = IPADDR_V6;
+ memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6),
+ sizeof(struct in6_addr));
+ } else {
+ continue;
+ }
+
+ zvni_gw_macip_del(ifp, zvni, &ip);
+ }
+
+ return 0;
+}
+
+static int zvni_add_macip_for_intf(struct interface *ifp, zebra_vni_t *zvni)
+{
+ struct zebra_vrf *zvrf = NULL;
+ struct listnode *cnode = NULL, *cnnode = NULL;
+ struct connected *c = NULL;
+ struct ethaddr macaddr;
+
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ if (!zvrf)
+ return -1;
+
+ memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, c)) {
+ struct ipaddr ip;
+
+ memset(&ip, 0, sizeof(struct ipaddr));
+ if (!CHECK_FLAG(c->conf, ZEBRA_IFC_REAL))
+ continue;
+
+ if (c->address->family == AF_INET) {
+ ip.ipa_type = IPADDR_V4;
+ memcpy(&(ip.ipaddr_v4), &(c->address->u.prefix4),
+ sizeof(struct in_addr));
+ } else if (c->address->family == AF_INET6) {
+ ip.ipa_type = IPADDR_V6;
+ memcpy(&(ip.ipaddr_v6), &(c->address->u.prefix6),
+ sizeof(struct in6_addr));
+ } else {
+ continue;
+ }
+
+ zvni_gw_macip_add(ifp, zvni, &macaddr, &ip);
+ }
+
+ return 0;
+}
+
+/*
+ * zvni_gw_macip_add_to_client
+ */
+static int zvni_gw_macip_add(struct interface *ifp, zebra_vni_t *zvni,
+ struct ethaddr *macaddr, struct ipaddr *ip)
+{
+ struct zebra_vrf *zvrf = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan *vxl = NULL;
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *mac = NULL;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ if (!zvrf)
+ return -1;
+
+ zif = zvni->vxlan_if->info;
+ if (!zif)
+ return -1;
+
+ vxl = &zif->l2info.vxl;
+
+ mac = zvni_mac_lookup(zvni, macaddr);
+ if (!mac) {
+ mac = zvni_mac_add(zvni, macaddr);
+ if (!mac) {
+ zlog_err("%u:Failed to add MAC %s intf %s(%u) VID %u",
+ ifp->vrf_id,
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ifp->name, ifp->ifindex, vxl->access_vlan);
+ return -1;
+ }
+ }
+
+ /* Set "local" forwarding info. */
+ SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+ mac->fwd_info.local.ifindex = ifp->ifindex;
+ mac->fwd_info.local.vid = vxl->access_vlan;
+
+ n = zvni_neigh_lookup(zvni, ip);
+ if (!n) {
+ n = zvni_neigh_add(zvni, ip, macaddr);
+ if (!n) {
+ zlog_err(
+ "%u:Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
+ ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)),
+ prefix_mac2str(macaddr, NULL,
+ ETHER_ADDR_STRLEN),
+ ifp->name, ifp->ifindex, zvni->vni);
+ return -1;
+ }
+ }
+
+ /* Set "local" forwarding info. */
+ SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
+ memcpy(&n->emac, macaddr, ETH_ALEN);
+ n->ifindex = ifp->ifindex;
+
+ /* We have a neigh associated to mac increment the refcnt*/
+ mac->neigh_refcnt++;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s add to BGP",
+ ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni,
+ prefix_mac2str(macaddr, NULL, ETHER_ADDR_STRLEN),
+ ipaddr2str(ip, buf2, sizeof(buf2)));
+
+ zvni_neigh_send_add_to_client(zvrf, zvni->vni, ip, macaddr,
+ ZEBRA_MAC_TYPE_GW);
+
+ return 0;
+}
+
+/*
+ * zvni_gw_macip_del_from_client
+ */
+static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
+ struct ipaddr *ip)
+{
+ struct zebra_vrf *zvrf = NULL;
+ zebra_neigh_t *n = NULL;
+ zebra_mac_t *mac = NULL;
+ char buf2[INET6_ADDRSTRLEN];
+
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ if (!zvrf)
+ return -1;
+
+ /* If the neigh entry is not present nothing to do*/
+ n = zvni_neigh_lookup(zvni, ip);
+ if (!n)
+ return 0;
+
+ /* mac entry should be present */
+ mac = zvni_mac_lookup(zvni, &n->emac);
+ if (!mac)
+ zlog_err("%u: MAC %s doesnt exsists for neigh %s on VNI %u",
+ ifp->vrf_id,
+ prefix_mac2str(&n->emac, NULL, ETHER_ADDR_STRLEN),
+ ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni);
+
+ /* If the entry is not local nothing to do*/
+ if (!CHECK_FLAG(n->flags, ZEBRA_NEIGH_LOCAL))
+ return -1;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP",
+ ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni,
+ prefix_mac2str(&(n->emac), NULL, ETHER_ADDR_STRLEN),
+ ipaddr2str(ip, buf2, sizeof(buf2)));
+
+ /* Remove neighbor from BGP. */
+ zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac,
+ ZEBRA_MAC_TYPE_GW);
+
+ /* Delete this neighbor entry. */
+ zvni_neigh_del(zvni, n);
+
+ /* see if the mac needs to be deleted as well*/
+ zvni_deref_ip2mac(zvni, mac, 0);
+
+ return 0;
+}
+
+static void zvni_gw_macip_del_for_vni_hash(struct hash_backet *backet,
+ void *zvrf)
+{
+ zebra_vni_t *zvni = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan zl2_info;
+ struct interface *vlan_if = NULL;
+ struct interface *vrr_if = NULL;
+
+ /* Add primary SVI MAC*/
+ zvni = (zebra_vni_t *)backet->data;
+ if (!zvni)
+ return;
+
+ zif = zvni->vxlan_if->info;
+ zl2_info = zif->l2info.vxl;
+
+ vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan,
+ zif->brslave_info.br_if);
+ if (!vlan_if)
+ return;
+
+ /* Del primary MAC-IP */
+ zvni_del_macip_for_intf(vlan_if, zvni);
+
+ /* Del VRR MAC-IP - if any*/
+ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
+ if (vrr_if)
+ zvni_del_macip_for_intf(vrr_if, zvni);
+
+ return;
+}
+
+static void zvni_gw_macip_add_for_vni_hash(struct hash_backet *backet,
+ void *zvrf)
+{
+ zebra_vni_t *zvni = NULL;
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan zl2_info;
+ struct interface *vlan_if = NULL;
+ struct interface *vrr_if = NULL;
+
+ zvni = (zebra_vni_t *)backet->data;
+ if (!zvni)
+ return;
+
+ if (!advertise_gw_macip_enabled(zvrf, zvni))
+ return;
+
+ zif = zvni->vxlan_if->info;
+ zl2_info = zif->l2info.vxl;
+
+ vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan,
+ zif->brslave_info.br_if);
+ if (!vlan_if)
+ return;
+
+ if (!advertise_gw_macip_enabled(zvrf, zvni))
+ return;
+
+ /* Add primary SVI MAC-IP */
+ zvni_add_macip_for_intf(vlan_if, zvni);
+
+ /* Add VRR MAC-IP - if any*/
+ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
+ if (vrr_if)
+ zvni_add_macip_for_intf(vrr_if, zvni);
+
+ return;
+}
+
/*
* Make hash key for MAC.
*/
@@ -827,6 +1551,9 @@ static zebra_mac_t *zvni_mac_add(zebra_vni_t *zvni, struct ethaddr *macaddr)
mac = hash_get(zvni->mac_table, &tmp_mac, zvni_mac_alloc);
assert(mac);
+ mac->neigh_list = list_new();
+ mac->neigh_list->cmp = (int (*)(void *, void *))neigh_cmp;
+
return mac;
}
@@ -837,6 +1564,8 @@ static int zvni_mac_del(zebra_vni_t *zvni, zebra_mac_t *mac)
{
zebra_mac_t *tmp_mac;
+ list_delete(mac->neigh_list);
+
/* Free the VNI hash entry and allocated memory. */
tmp_mac = hash_release(zvni->mac_table, mac);
if (tmp_mac)
@@ -864,8 +1593,9 @@ static int zvni_mac_del_hash_entry(struct hash_backet *backet, void *arg)
if (wctx->upd_client && (mac->flags & ZEBRA_MAC_LOCAL)) {
sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1
: 0;
- zvni_mac_send_del_to_client(wctx->zvrf, wctx->zvni->vni,
- &mac->macaddr, sticky);
+ zvni_mac_send_del_to_client(
+ wctx->zvrf, wctx->zvni->vni, &mac->macaddr,
+ (sticky ? ZEBRA_MAC_TYPE_STICKY : 0));
}
if (wctx->uninstall)
@@ -941,9 +1671,9 @@ static zebra_mac_t *zvni_mac_lookup(zebra_vni_t *zvni, struct ethaddr *mac)
* Inform BGP about local MAC addition.
*/
static int zvni_mac_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni,
- struct ethaddr *macaddr, u_char sticky)
+ struct ethaddr *macaddr, u_char flags)
{
- return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, NULL, sticky,
+ return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, NULL, flags,
ZEBRA_MACIP_ADD);
}
@@ -951,9 +1681,9 @@ static int zvni_mac_send_add_to_client(struct zebra_vrf *zvrf, vni_t vni,
* Inform BGP about local MAC deletion.
*/
static int zvni_mac_send_del_to_client(struct zebra_vrf *zvrf, vni_t vni,
- struct ethaddr *macaddr, u_char sticky)
+ struct ethaddr *macaddr, u_char flags)
{
- return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, NULL, sticky,
+ return zvni_macip_send_msg_to_client(zvrf, vni, macaddr, NULL, flags,
ZEBRA_MACIP_DEL);
}
@@ -1234,6 +1964,7 @@ static void zvni_read_mac_neigh(struct zebra_vrf *zvrf, zebra_vni_t *zvni,
struct zebra_if *zif;
struct interface *vlan_if;
struct zebra_l2info_vxlan *vxl;
+ struct interface *vrr_if;
zif = ifp->info;
vxl = &zif->l2info.vxl;
@@ -1247,8 +1978,20 @@ static void zvni_read_mac_neigh(struct zebra_vrf *zvrf, zebra_vni_t *zvni,
macfdb_read_for_bridge(zvrf->zns, ifp, zif->brslave_info.br_if);
vlan_if = zvni_map_to_svi(zvrf, vxl->access_vlan,
zif->brslave_info.br_if);
- if (vlan_if)
+ if (vlan_if) {
+
+ if (advertise_gw_macip_enabled(zvrf, zvni)) {
+ /* Add SVI MAC-IP */
+ zvni_add_macip_for_intf(vlan_if, zvni);
+
+ /* Add VRR MAC-IP - if any*/
+ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
+ if (vrr_if)
+ zvni_add_macip_for_intf(vrr_if, zvni);
+ }
+
neigh_read_for_vlan(zvrf->zns, vlan_if);
+ }
}
/*
@@ -1605,23 +2348,30 @@ static void zvni_cleanup_all(struct hash_backet *backet, void *zvrf)
* Display Neighbors for a VNI (VTY command handler).
*/
void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni)
+ vni_t vni, u_char use_json)
{
zebra_vni_t *zvni;
u_int32_t num_neigh;
struct neigh_walk_ctx wctx;
+ json_object *json = NULL;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup(zvrf, vni);
if (!zvni) {
- vty_out(vty, "%% VNI %u does not exist\n", vni);
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
num_neigh = hashcount(zvni->neigh_table);
if (!num_neigh)
return;
+ if (use_json)
+ json = json_object_new_object();
+
/* Since we have IPv6 addresses to deal with which can vary widely in
* size, we try to be a bit more elegant in display by first computing
* the maximum width.
@@ -1630,25 +2380,52 @@ void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
wctx.zvni = zvni;
wctx.vty = vty;
wctx.addr_width = 15;
+ wctx.json = json;
hash_iterate(zvni->neigh_table, zvni_find_neigh_addr_width, &wctx);
- vty_out(vty,
- "Number of ARPs (local and remote) known for this VNI: %u\n",
- num_neigh);
- vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP", "Type",
- "MAC", "Remote VTEP");
+ if (!use_json) {
+ vty_out(vty,
+ "Number of ARPs (local and remote) known for this VNI: %u\n",
+ num_neigh);
+ vty_out(vty, "%*s %-6s %-17s %-21s\n", -wctx.addr_width, "IP",
+ "Type", "MAC", "Remote VTEP");
+ } else
+ json_object_int_add(json, "numArpNd", num_neigh);
hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
* Display neighbors across all VNIs (VTY command handler).
*/
-void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf)
+void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
+ u_char use_json)
{
+ json_object *json = NULL;
+ void *args[2];
+
if (!EVPN_ENABLED(zvrf))
return;
- hash_iterate(zvrf->vni_table, zvni_print_neigh_hash_all_vni, vty);
+
+ if (use_json)
+ json = json_object_new_object();
+
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(zvrf->vni_table,
+ (void (*)(struct hash_backet *,
+ void *))zvni_print_neigh_hash_all_vni,
+ args);
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
@@ -1656,26 +2433,40 @@ void zebra_vxlan_print_neigh_all_vni(struct vty *vty, struct zebra_vrf *zvrf)
*/
void zebra_vxlan_print_specific_neigh_vni(struct vty *vty,
struct zebra_vrf *zvrf, vni_t vni,
- struct ipaddr *ip)
+ struct ipaddr *ip, u_char use_json)
{
zebra_vni_t *zvni;
zebra_neigh_t *n;
+ json_object *json = NULL;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup(zvrf, vni);
if (!zvni) {
- vty_out(vty, "%% VNI %u does not exist", vni);
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
n = zvni_neigh_lookup(zvni, ip);
if (!n) {
- vty_out(vty, "%% Requested neighbor does not exist in VNI %u\n",
- vni);
+ if (!use_json)
+ vty_out(vty,
+ "%% Requested neighbor does not exist in VNI %u\n",
+ vni);
return;
}
+ if (use_json)
+ json = json_object_new_object();
- zvni_print_neigh(n, vty);
+ zvni_print_neigh(n, vty, json);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
@@ -1683,17 +2474,22 @@ void zebra_vxlan_print_specific_neigh_vni(struct vty *vty,
* By definition, these are remote neighbors.
*/
void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni, struct in_addr vtep_ip)
+ vni_t vni, struct in_addr vtep_ip,
+ u_char use_json)
{
zebra_vni_t *zvni;
u_int32_t num_neigh;
struct neigh_walk_ctx wctx;
+ json_object *json = NULL;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup(zvrf, vni);
if (!zvni) {
- vty_out(vty, "%% VNI %u does not exist\n", vni);
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
num_neigh = hashcount(zvni->neigh_table);
@@ -1705,56 +2501,98 @@ void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
wctx.vty = vty;
wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
-
+ wctx.json = json;
hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
* Display MACs for a VNI (VTY command handler).
*/
void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni)
+ vni_t vni, u_char use_json)
{
zebra_vni_t *zvni;
u_int32_t num_macs;
struct mac_walk_ctx wctx;
+ json_object *json = NULL;
+ json_object *json_mac = NULL;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup(zvrf, vni);
if (!zvni) {
- vty_out(vty, "%% VNI %u does not exist\n", vni);
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
num_macs = hashcount(zvni->mac_table);
if (!num_macs)
return;
+ if (use_json) {
+ json = json_object_new_object();
+ json_mac = json_object_new_object();
+ }
+
memset(&wctx, 0, sizeof(struct mac_walk_ctx));
wctx.zvni = zvni;
wctx.vty = vty;
+ wctx.json = json_mac;
- vty_out(vty,
- "Number of MACs (local and remote) known for this VNI: %u\n",
- num_macs);
- vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type",
- "Intf/Remote VTEP", "VLAN");
+ if (!use_json) {
+ vty_out(vty,
+ "Number of MACs (local and remote) known for this VNI: %u\n",
+ num_macs);
+ vty_out(vty, "%-17s %-6s %-21s %-5s\n", "MAC", "Type",
+ "Intf/Remote VTEP", "VLAN");
+ } else
+ json_object_int_add(json, "numMacs", num_macs);
hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
+
+ if (use_json) {
+ json_object_object_add(json, "macs", json_mac);
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
* Display MACs for all VNIs (VTY command handler).
*/
-void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf)
+void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf,
+ u_char use_json)
{
struct mac_walk_ctx wctx;
+ json_object *json = NULL;
- if (!EVPN_ENABLED(zvrf))
+ if (!EVPN_ENABLED(zvrf)) {
+ if (use_json)
+ vty_out(vty, "{}\n");
return;
+ }
+ if (use_json)
+ json = json_object_new_object();
+
memset(&wctx, 0, sizeof(struct mac_walk_ctx));
wctx.vty = vty;
+ wctx.json = json;
hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
@@ -1762,17 +2600,30 @@ void zebra_vxlan_print_macs_all_vni(struct vty *vty, struct zebra_vrf *zvrf)
*/
void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty,
struct zebra_vrf *zvrf,
- struct in_addr vtep_ip)
+ struct in_addr vtep_ip,
+ u_char use_json)
{
struct mac_walk_ctx wctx;
+ json_object *json = NULL;
if (!EVPN_ENABLED(zvrf))
return;
+
+ if (use_json)
+ json = json_object_new_object();
+
memset(&wctx, 0, sizeof(struct mac_walk_ctx));
wctx.vty = vty;
wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
+ wctx.json = json;
hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
@@ -1805,64 +2656,128 @@ void zebra_vxlan_print_specific_mac_vni(struct vty *vty, struct zebra_vrf *zvrf,
* Display MACs for a VNI from specific VTEP (VTY command handler).
*/
void zebra_vxlan_print_macs_vni_vtep(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni, struct in_addr vtep_ip)
+ vni_t vni, struct in_addr vtep_ip,
+ u_char use_json)
{
zebra_vni_t *zvni;
u_int32_t num_macs;
struct mac_walk_ctx wctx;
+ json_object *json = NULL;
+ json_object *json_mac = NULL;
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup(zvrf, vni);
if (!zvni) {
- vty_out(vty, "%% VNI %u does not exist\n", vni);
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
num_macs = hashcount(zvni->mac_table);
if (!num_macs)
return;
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_mac = json_object_new_object();
+ }
+
memset(&wctx, 0, sizeof(struct mac_walk_ctx));
wctx.zvni = zvni;
wctx.vty = vty;
wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
wctx.r_vtep_ip = vtep_ip;
+ wctx.json = json_mac;
hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
+
+ if (use_json) {
+ json_object_int_add(json, "numMacs", wctx.count);
+ if (wctx.count)
+ json_object_object_add(json, "macs", json_mac);
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
* Display VNI information (VTY command handler).
*/
-void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni)
+void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf, vni_t vni,
+ u_char use_json)
{
zebra_vni_t *zvni;
+ json_object *json = NULL;
+ void *args[2];
if (!EVPN_ENABLED(zvrf))
return;
zvni = zvni_lookup(zvrf, vni);
if (!zvni) {
- vty_out(vty, "%% VNI %u does not exist\n", vni);
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% VNI %u does not exist\n", vni);
return;
}
- zvni_print(zvni, (void *)vty);
+ if (use_json)
+ json = json_object_new_object();
+ args[0] = vty;
+ args[1] = json;
+ zvni_print(zvni, (void *)args);
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
* Display VNI hash table (VTY command handler).
*/
-void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf)
+void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
+ u_char use_json)
{
u_int32_t num_vnis;
+ json_object *json = NULL;
+ void *args[2];
if (!EVPN_ENABLED(zvrf))
return;
num_vnis = hashcount(zvrf->vni_table);
- if (!num_vnis)
+ if (!num_vnis) {
+ if (use_json)
+ vty_out(vty, "{}\n");
return;
- vty_out(vty, "Number of VNIs: %u\n", num_vnis);
- vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI", "VxLAN IF",
- "VTEP IP", "# MACs", "# ARPs", "# Remote VTEPs");
- hash_iterate(zvrf->vni_table, zvni_print_hash, vty);
+ }
+ if (use_json) {
+ json = json_object_new_object();
+ json_object_string_add(json, "advertiseGatewayMacip",
+ zvrf->advertise_gw_macip ? "Yes" : "No");
+ json_object_int_add(json, "numVnis", num_vnis);
+ } else {
+ vty_out(vty, "Advertise gateway mac-ip: %s\n",
+ zvrf->advertise_gw_macip ? "Yes" : "No");
+ vty_out(vty, "Number of VNIs: %u\n", num_vnis);
+ vty_out(vty, "%-10s %-21s %-15s %-8s %-8s %-15s\n", "VNI",
+ "VxLAN IF", "VTEP IP", "# MACs", "# ARPs",
+ "# Remote VTEPs");
+ }
+ args[0] = vty;
+ args[1] = json;
+
+ hash_iterate(zvrf->vni_table,
+ (void (*)(struct hash_backet *, void *))zvni_print_hash,
+ args);
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ json_object_free(json);
+ }
}
/*
@@ -1878,6 +2793,8 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp,
zebra_neigh_t *n;
struct zebra_vrf *zvrf;
char buf[INET6_ADDRSTRLEN];
+ char buf2[ETHER_ADDR_STRLEN];
+ zebra_mac_t *zmac;
/* We are only interested in neighbors on an SVI that resides on top
* of a VxLAN bridge.
@@ -1902,6 +2819,18 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp,
if (!n)
return 0;
+ zmac = zvni_mac_lookup(zvni, &n->emac);
+ if (!zmac) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_err(
+ "%u: trying to del a neigh %s without a mac %s on VNI %u",
+ ifp->vrf_id, ipaddr2str(ip, buf, sizeof(buf)),
+ prefix_mac2str(&n->emac, buf2, sizeof(buf2)),
+ zvni->vni);
+
+ return 0;
+ }
+
/* If it is a remote entry, the kernel has aged this out or someone has
* deleted it, it needs to be re-installed as Quagga is the owner.
*/
@@ -1915,11 +2844,18 @@ int zebra_vxlan_local_neigh_del(struct interface *ifp,
assert(zvrf);
/* Remove neighbor from BGP. */
- zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac);
+ if (IS_ZEBRA_NEIGH_ACTIVE(n))
+ zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac,
+ 0);
/* Delete this neighbor entry. */
zvni_neigh_del(zvni, n);
+ /* see if the AUTO mac needs to be deleted */
+ if (CHECK_FLAG(zmac->flags, ZEBRA_MAC_AUTO)
+ || !listcount(zmac->neigh_list))
+ zvni_mac_del(zvni, zmac);
+
return 0;
}
@@ -1936,6 +2872,7 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp,
zebra_vni_t *zvni;
zebra_neigh_t *n;
struct zebra_vrf *zvrf;
+ zebra_mac_t *zmac;
char buf[ETHER_ADDR_STRLEN];
char buf2[INET6_ADDRSTRLEN];
int send_upd = 1, send_del = 0;
@@ -1960,6 +2897,30 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp,
ifp->ifindex, state, ext_learned ? "ext-learned " : "",
zvni->vni);
+ /* create a dummy MAC if the MAC is not already present */
+ zmac = zvni_mac_lookup(zvni, macaddr);
+ if (!zmac) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: AUTO MAC %s created for neigh %s on VNI %u",
+ ifp->vrf_id,
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ ipaddr2str(ip, buf2, sizeof(buf2)), zvni->vni);
+
+ zmac = zvni_mac_add(zvni, macaddr);
+ if (!zmac) {
+ zlog_warn("%u:Failed to add MAC %s VNI %u",
+ zvrf_id(zvrf),
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zvni->vni);
+ return -1;
+ }
+
+ memset(&zmac->fwd_info, 0, sizeof(zmac->fwd_info));
+ memset(&zmac->flags, 0, sizeof(u_int32_t));
+ SET_FLAG(zmac->flags, ZEBRA_MAC_AUTO);
+ }
+
/* If same entry already exists, it might be a change or it might be a
* move from remote to local.
*/
@@ -1995,7 +2956,7 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp,
n->r_vtep_ip.s_addr = 0;
}
} else {
- n = zvni_neigh_add(zvni, ip);
+ n = zvni_neigh_add(zvni, ip, macaddr);
if (!n) {
zlog_err(
"%u:Failed to add neighbor %s MAC %s intf %s(%u) -> VNI %u",
@@ -2008,18 +2969,39 @@ int zebra_vxlan_local_neigh_add_update(struct interface *ifp,
/* Issue delete for older info, if needed. */
if (send_del)
- zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip,
- &n->emac);
+ zvni_neigh_send_del_to_client(zvrf, zvni->vni, &n->ip, &n->emac,
+ 0);
/* Set "local" forwarding info. */
SET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
- memcpy(&n->emac, macaddr, ETH_ALEN);
n->ifindex = ifp->ifindex;
+ /* Before we program this in BGP, we need to check if MAC is locally
+ * learnt as well */
+ if (!CHECK_FLAG(zmac->flags, ZEBRA_MAC_LOCAL)) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: Skipping neigh %s add to client as MAC %s is not local on VNI %u",
+ ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)),
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zvni->vni);
+
+ return 0;
+ }
+
/* Inform BGP if required. */
- if (send_upd)
+ if (send_upd) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u: neigh %s (MAC %s) is now ACTIVE on VNI %u",
+ ifp->vrf_id, ipaddr2str(ip, buf2, sizeof(buf2)),
+ prefix_mac2str(macaddr, buf, sizeof(buf)),
+ zvni->vni);
+
+ ZEBRA_NEIGH_SET_ACTIVE(n);
return zvni_neigh_send_add_to_client(zvrf, zvni->vni, ip,
- macaddr);
+ macaddr, 0);
+ }
return 0;
}
@@ -2147,6 +3129,9 @@ int zebra_vxlan_remote_macip_del(struct zserv *client, int sock, u_short length,
}
} else {
if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+ zvni_process_neigh_on_remote_mac_del(zvrf, zvni,
+ mac);
+
if (!mac->neigh_refcnt) {
zvni_mac_uninstall(zvni, mac, 0);
zvni_mac_del(zvni, mac);
@@ -2303,6 +3288,8 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length,
else
UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
+ zvni_process_neigh_on_remote_mac_add(zvrf, zvni, mac);
+
/* Install the entry. */
zvni_mac_install(zvni, mac);
}
@@ -2326,7 +3313,7 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length,
if (update_neigh) {
if (!n) {
- n = zvni_neigh_add(zvni, &ip);
+ n = zvni_neigh_add(zvni, &ip, &macaddr);
if (!n) {
zlog_warn(
"%u:Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
@@ -2349,12 +3336,12 @@ int zebra_vxlan_remote_macip_add(struct zserv *client, int sock, u_short length,
if (old_mac)
zvni_deref_ip2mac(zvni, old_mac, 1);
mac->neigh_refcnt++;
+ memcpy(&n->emac, &macaddr, ETH_ALEN);
}
/* Set "remote" forwarding info. */
UNSET_FLAG(n->flags, ZEBRA_NEIGH_LOCAL);
/* TODO: Handle MAC change. */
- memcpy(&n->emac, &macaddr, ETH_ALEN);
n->r_vtep_ip = vtep_ip;
SET_FLAG(n->flags, ZEBRA_NEIGH_REMOTE);
@@ -2419,10 +3406,19 @@ int zebra_vxlan_check_del_local_mac(struct interface *ifp,
/* Remove MAC from BGP. */
sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1 : 0;
- zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr, sticky);
+ zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr,
+ (sticky ? ZEBRA_MAC_TYPE_STICKY : 0));
- /* Delete this MAC entry. */
- zvni_mac_del(zvni, mac);
+ /*
+ * If there are no neigh associated with the mac delete the mac
+ * else mark it as AUTO for forward reference
+ */
+ if (!listcount(mac->neigh_list)) {
+ zvni_mac_del(zvni, mac);
+ } else {
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ }
return 0;
}
@@ -2526,10 +3522,22 @@ int zebra_vxlan_local_mac_del(struct interface *ifp, struct interface *br_if,
/* Remove MAC from BGP. */
sticky = CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY) ? 1 : 0;
- zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr, sticky);
+ zvni_mac_send_del_to_client(zvrf, zvni->vni, macaddr,
+ (sticky ? ZEBRA_MAC_TYPE_STICKY : 0));
- /* Delete this MAC entry. */
- zvni_mac_del(zvni, mac);
+ /* Update all the neigh entries associated with this mac */
+ zvni_process_neigh_on_local_mac_del(zvrf, zvni, mac);
+
+ /*
+ * If there are no neigh associated with the mac delete the mac
+ * else mark it as AUTO for forward reference
+ */
+ if (!listcount(mac->neigh_list)) {
+ zvni_mac_del(zvni, mac);
+ } else {
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ SET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
+ }
return 0;
}
@@ -2584,6 +3592,13 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
? 1
: 0;
+
+ /*
+ * return if nothing has changed.
+ * inform bgp if sticky flag has changed
+ * update locally and do not inform bgp if local
+ * parameters like interface has changed
+ */
if (mac_sticky == sticky
&& mac->fwd_info.local.ifindex == ifp->ifindex
&& mac->fwd_info.local.vid == vid) {
@@ -2598,9 +3613,27 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
ifp->name, ifp->ifindex, vid,
zvni->vni);
return 0;
+ } else if (mac_sticky != sticky) {
+ add = 1;
+ } else {
+ add = 0; /* This is an update of local
+ interface. */
+ }
+ } else if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE)) {
+ /*
+ * If we have already learned the MAC as a remote sticky
+ * MAC,
+ * this is a operator error and we must log a warning
+ */
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_STICKY)) {
+ zlog_warn(
+ "MAC %s is already learnt as a remote sticky mac behind VTEP %s VNI %d",
+ prefix_mac2str(macaddr, buf,
+ sizeof(buf)),
+ inet_ntoa(mac->fwd_info.r_vtep_ip),
+ zvni->vni);
+ return 0;
}
-
- add = 0; /* This is an update of local interface. */
}
}
@@ -2621,8 +3654,9 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
/* Set "local" forwarding info. */
UNSET_FLAG(mac->flags, ZEBRA_MAC_REMOTE);
- memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
+ UNSET_FLAG(mac->flags, ZEBRA_MAC_AUTO);
SET_FLAG(mac->flags, ZEBRA_MAC_LOCAL);
+ memset(&mac->fwd_info, 0, sizeof(mac->fwd_info));
mac->fwd_info.local.ifindex = ifp->ifindex;
mac->fwd_info.local.vid = vid;
@@ -2632,9 +3666,11 @@ int zebra_vxlan_local_mac_add_update(struct interface *ifp,
UNSET_FLAG(mac->flags, ZEBRA_MAC_STICKY);
/* Inform BGP if required. */
- if (add)
+ if (add) {
+ zvni_process_neigh_on_local_mac_add(zvrf, zvni, mac);
return zvni_mac_send_add_to_client(zvrf, zvni->vni, macaddr,
sticky);
+ }
return 0;
}
@@ -2766,6 +3802,104 @@ int zebra_vxlan_remote_vtep_add(struct zserv *client, int sock, u_short length,
}
/*
+ * Add/Del gateway macip to evpn
+ * g/w can be:
+ * 1. SVI interface on a vlan aware bridge
+ * 2. SVI interface on a vlan unaware bridge
+ * 3. vrr interface (MACVLAN) associated to a SVI
+ * We advertise macip routes for an interface if it is associated to VxLan vlan
+ */
+int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
+ int add)
+{
+ struct ipaddr ip;
+ struct ethaddr macaddr;
+ zebra_vni_t *zvni = NULL;
+ struct zebra_vrf *zvrf = NULL;
+
+ memset(&ip, 0, sizeof(struct ipaddr));
+ memset(&macaddr, 0, sizeof(struct ethaddr));
+
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ if (!zvrf)
+ return -1;
+
+ if (!EVPN_ENABLED(zvrf))
+ return 0;
+
+ if (IS_ZEBRA_IF_MACVLAN(ifp)) {
+ struct interface *svi_if =
+ NULL; /* SVI corresponding to the MACVLAN */
+ struct zebra_if *ifp_zif =
+ NULL; /* Zebra daemon specific info for MACVLAN */
+ struct zebra_if *svi_if_zif =
+ NULL; /* Zebra daemon specific info for SVI*/
+
+ ifp_zif = ifp->info;
+ if (!ifp_zif)
+ return -1;
+
+ svi_if = ifp_zif->link;
+ if (!svi_if) {
+ zlog_err("%u:MACVLAN %s(%u) without link information",
+ ifp->vrf_id, ifp->name, ifp->ifindex);
+ return -1;
+ }
+
+ if (IS_ZEBRA_IF_VLAN(svi_if)) {
+ svi_if_zif = svi_if->info;
+ if (svi_if_zif)
+ zvni = zvni_map_svi(svi_if, svi_if_zif->link);
+ } else if (IS_ZEBRA_IF_BRIDGE(svi_if)) {
+ zvni = zvni_map_svi(svi_if, svi_if);
+ }
+ } else if (IS_ZEBRA_IF_VLAN(ifp)) {
+ struct zebra_if *svi_if_zif =
+ NULL; /* Zebra daemon specific info for SVI*/
+
+ svi_if_zif = ifp->info;
+ if (svi_if_zif)
+ zvni = zvni_map_svi(ifp, svi_if_zif->link);
+ } else if (IS_ZEBRA_IF_BRIDGE(ifp)) {
+ zvni = zvni_map_svi(ifp, ifp);
+ }
+
+ if (!zvni)
+ return 0;
+
+ if (!zvni->vxlan_if) {
+ zlog_err("VNI %u hash %p doesn't have intf upon MACVLAN up",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+
+ /* check if we are advertising gw macip routes */
+ if (!advertise_gw_macip_enabled(zvrf, zvni))
+ return 0;
+
+ memcpy(&macaddr.octet, ifp->hw_addr, ETH_ALEN);
+
+ if (p->family == AF_INET) {
+ ip.ipa_type = IPADDR_V4;
+ memcpy(&(ip.ipaddr_v4), &(p->u.prefix4),
+ sizeof(struct in_addr));
+ } else if (p->family == AF_INET6) {
+ ip.ipa_type = IPADDR_V6;
+ memcpy(&(ip.ipaddr_v6), &(p->u.prefix6),
+ sizeof(struct in6_addr));
+ }
+
+
+ if (add)
+ zvni_gw_macip_add(ifp, zvni, &macaddr, &ip);
+ else
+ zvni_gw_macip_del(ifp, zvni, &ip);
+
+ return 0;
+}
+
+/*
* Handle SVI interface going down. At this point, this is a NOP since
* the kernel deletes the neighbor entries on this SVI (if any).
*/
@@ -3124,6 +4258,98 @@ int zebra_vxlan_if_add(struct interface *ifp)
}
/*
+ * Handle message from client to enable/disable advertisement of g/w macip
+ * routes
+ */
+int zebra_vxlan_advertise_gw_macip(struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ struct stream *s;
+ int advertise;
+ vni_t vni = 0;
+ zebra_vni_t *zvni = NULL;
+
+ s = client->ibuf;
+ advertise = stream_getc(s);
+ vni = stream_get3(s);
+
+ if (!vni) {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug("%u:EVPN gateway macip Adv %s, currently %s",
+ zvrf_id(zvrf),
+ advertise ? "enabled" : "disabled",
+ advertise_gw_macip_enabled(zvrf, NULL)
+ ? "enabled"
+ : "disabled");
+
+ if (zvrf->advertise_gw_macip == advertise)
+ return 0;
+
+ zvrf->advertise_gw_macip = advertise;
+
+ if (advertise_gw_macip_enabled(zvrf, zvni))
+ hash_iterate(zvrf->vni_table,
+ zvni_gw_macip_add_for_vni_hash, zvrf);
+ else
+ hash_iterate(zvrf->vni_table,
+ zvni_gw_macip_del_for_vni_hash, zvrf);
+
+ } else {
+ struct zebra_if *zif = NULL;
+ struct zebra_l2info_vxlan zl2_info;
+ struct interface *vlan_if = NULL;
+ struct interface *vrr_if = NULL;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug(
+ "%u:EVPN gateway macip Adv %s on VNI %d , currently %s",
+ zvrf_id(zvrf),
+ advertise ? "enabled" : "disabled", vni,
+ advertise_gw_macip_enabled(zvrf, zvni)
+ ? "enabled"
+ : "disabled");
+
+ zvni = zvni_lookup(zvrf, vni);
+ if (!zvni)
+ return 0;
+
+ if (zvni->advertise_gw_macip == advertise)
+ return 0;
+
+ zvni->advertise_gw_macip = advertise;
+
+ zif = zvni->vxlan_if->info;
+ zl2_info = zif->l2info.vxl;
+
+ vlan_if = zvni_map_to_svi(zvrf, zl2_info.access_vlan,
+ zif->brslave_info.br_if);
+ if (!vlan_if)
+ return 0;
+
+ if (advertise_gw_macip_enabled(zvrf, zvni)) {
+ /* Add primary SVI MAC-IP */
+ zvni_add_macip_for_intf(vlan_if, zvni);
+
+ /* Add VRR MAC-IP - if any*/
+ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
+ if (vrr_if)
+ zvni_add_macip_for_intf(vrr_if, zvni);
+ } else {
+ /* Del primary MAC-IP */
+ zvni_del_macip_for_intf(vlan_if, zvni);
+
+ /* Del VRR MAC-IP - if any*/
+ vrr_if = zebra_get_vrr_intf_for_svi(vlan_if);
+ if (vrr_if)
+ zvni_del_macip_for_intf(vrr_if, zvni);
+ }
+ }
+
+ return 0;
+}
+
+
+/*
* Handle message from client to learn (or stop learning) about VNIs and MACs.
* 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
@@ -3151,6 +4377,10 @@ int zebra_vxlan_advertise_all_vni(struct zserv *client, int sock,
/* Build VNI hash table and inform BGP. */
zvni_build_hash_table(zvrf);
+ /* Add all SVI (L3 GW) MACs to BGP*/
+ hash_iterate(zvrf->vni_table, zvni_gw_macip_add_for_vni_hash,
+ zvrf);
+
/* Read the MAC FDB */
macfdb_read(zvrf->zns);
@@ -3182,4 +4412,5 @@ void zebra_vxlan_init_tables(struct zebra_vrf *zvrf)
void zebra_vxlan_close_tables(struct zebra_vrf *zvrf)
{
hash_iterate(zvrf->vni_table, zvni_cleanup_all, zvrf);
+ hash_free(zvrf->vni_table);
}
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
index f9ecd8333d..f7c1afc959 100644
--- a/zebra/zebra_vxlan.h
+++ b/zebra/zebra_vxlan.h
@@ -41,33 +41,44 @@
#define ZEBRA_VXLIF_MASTER_CHANGE 0x2
#define ZEBRA_VXLIF_VLAN_CHANGE 0x4
+#define VNI_STR_LEN 32
+
extern void zebra_vxlan_print_macs_vni(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni);
+ vni_t vni, u_char use_json);
extern void zebra_vxlan_print_macs_all_vni(struct vty *vty,
- struct zebra_vrf *zvrf);
+ struct zebra_vrf *zvrf,
+ u_char use_json);
extern void zebra_vxlan_print_macs_all_vni_vtep(struct vty *vty,
struct zebra_vrf *zvrf,
- struct in_addr vtep_ip);
+ struct in_addr vtep_ip,
+ u_char use_json);
extern void zebra_vxlan_print_specific_mac_vni(struct vty *vty,
struct zebra_vrf *zvrf,
vni_t vni, struct ethaddr *mac);
extern void zebra_vxlan_print_macs_vni_vtep(struct vty *vty,
struct zebra_vrf *zvrf, vni_t vni,
- struct in_addr vtep_ip);
+ struct in_addr vtep_ip,
+ u_char use_json);
extern void zebra_vxlan_print_neigh_vni(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni);
+ vni_t vni, u_char use_json);
extern void zebra_vxlan_print_neigh_all_vni(struct vty *vty,
- struct zebra_vrf *zvrf);
+ struct zebra_vrf *zvrf,
+ u_char use_json);
extern void zebra_vxlan_print_specific_neigh_vni(struct vty *vty,
struct zebra_vrf *zvrf,
- vni_t vni, struct ipaddr *ip);
+ vni_t vni, struct ipaddr *ip,
+ u_char use_json);
extern void zebra_vxlan_print_neigh_vni_vtep(struct vty *vty,
struct zebra_vrf *zvrf, vni_t vni,
- struct in_addr vtep_ip);
+ struct in_addr vtep_ip,
+ u_char use_json);
extern void zebra_vxlan_print_vni(struct vty *vty, struct zebra_vrf *zvrf,
- vni_t vni);
-extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf);
+ vni_t vni, u_char use_json);
+extern void zebra_vxlan_print_vnis(struct vty *vty, struct zebra_vrf *zvrf,
+ u_char use_json);
+extern int zebra_vxlan_add_del_gw_macip(struct interface *ifp, struct prefix *p,
+ int add);
extern int zebra_vxlan_svi_up(struct interface *ifp, struct interface *link_if);
extern int zebra_vxlan_svi_down(struct interface *ifp,
struct interface *link_if);
@@ -104,6 +115,9 @@ extern int zebra_vxlan_remote_vtep_add(struct zserv *client, int sock,
u_short length, struct zebra_vrf *zvrf);
extern int zebra_vxlan_remote_vtep_del(struct zserv *client, int sock,
u_short length, struct zebra_vrf *zvrf);
+extern int zebra_vxlan_advertise_gw_macip(struct zserv *client, int sock,
+ u_short length,
+ struct zebra_vrf *zvrf);
extern int zebra_vxlan_advertise_all_vni(struct zserv *client, int sock,
u_short length,
struct zebra_vrf *zvrf);
diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h
index fbde722927..8539311eab 100644
--- a/zebra/zebra_vxlan_private.h
+++ b/zebra/zebra_vxlan_private.h
@@ -63,6 +63,9 @@ struct zebra_vni_t_ {
/* VNI - key */
vni_t vni;
+ /* Flag for advertising gw macip */
+ u_int8_t advertise_gw_macip;
+
/* Corresponding VxLAN interface. */
struct interface *vxlan_if;
@@ -112,6 +115,9 @@ struct zebra_mac_t_ {
} fwd_info;
u_int32_t neigh_refcnt;
+
+ /* List of neigh associated with this mac */
+ struct list *neigh_list;
};
/*
@@ -132,10 +138,21 @@ struct mac_walk_ctx {
struct in_addr r_vtep_ip; /* To walk MACs from specific VTEP */
- struct vty *vty; /* Used by VTY handlers */
- u_int32_t count; /* Used by VTY handlers */
+ struct vty *vty; /* Used by VTY handlers */
+ u_int32_t count; /* Used by VTY handlers */
+ struct json_object *json; /* Used for JSON Output */
};
+enum zebra_neigh_state { ZEBRA_NEIGH_INACTIVE = 0, ZEBRA_NEIGH_ACTIVE = 1 };
+
+#define IS_ZEBRA_NEIGH_ACTIVE(n) n->state == ZEBRA_NEIGH_ACTIVE
+
+#define IS_ZEBRA_NEIGH_INACTIVE(n) n->state == ZEBRA_NEIGH_INACTIVE
+
+#define ZEBRA_NEIGH_SET_ACTIVE(n) n->state = ZEBRA_NEIGH_ACTIVE
+
+#define ZEBRA_NEIGH_SET_INACTIVE(n) n->state = ZEBRA_NEIGH_INACTIVE
+
/*
* Neighbor hash table.
*
@@ -158,8 +175,10 @@ struct zebra_neigh_t_ {
ifindex_t ifindex;
u_int32_t flags;
-#define ZEBRA_NEIGH_LOCAL 0x01
-#define ZEBRA_NEIGH_REMOTE 0x02
+#define ZEBRA_NEIGH_LOCAL 0x01
+#define ZEBRA_NEIGH_REMOTE 0x02
+
+ enum zebra_neigh_state state;
/* Remote VTEP IP - applicable only for remote neighbors. */
struct in_addr r_vtep_ip;
@@ -183,9 +202,10 @@ struct neigh_walk_ctx {
struct in_addr r_vtep_ip; /* To walk neighbors from specific VTEP */
- struct vty *vty; /* Used by VTY handlers */
- u_int32_t count; /* Used by VTY handlers */
- u_char addr_width; /* Used by VTY handlers */
+ struct vty *vty; /* Used by VTY handlers */
+ u_int32_t count; /* Used by VTY handlers */
+ u_char addr_width; /* Used by VTY handlers */
+ struct json_object *json; /* Used for JSON Output */
};
#endif /* _ZEBRA_VXLAN_PRIVATE_H */
diff --git a/zebra/zserv.c b/zebra/zserv.c
index a3a8c11c51..5ee6c6d1f3 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -2533,6 +2533,9 @@ static int zebra_client_read(struct thread *thread)
case ZEBRA_FEC_UNREGISTER:
zserv_fec_unregister(client, sock, length);
break;
+ case ZEBRA_ADVERTISE_DEFAULT_GW:
+ zebra_vxlan_advertise_gw_macip(client, sock, length, zvrf);
+ break;
case ZEBRA_ADVERTISE_ALL_VNI:
zebra_vxlan_advertise_all_vni(client, sock, length, zvrf);
break;