summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuss White <russ@riw.us>2017-07-14 15:36:31 +0200
committerGitHub <noreply@github.com>2017-07-14 15:36:31 +0200
commit805d1ca6b06158ce9f31f77867ef9d293744a95f (patch)
tree855a3dca8ef223561892a62f8ef3adef89ff84eb
parentMerge pull request #813 from opensourcerouting/newline-redux (diff)
parentbgpd, zebra: Cleanup warnings from new code (diff)
downloadfrr-805d1ca6b06158ce9f31f77867ef9d293744a95f.tar.xz
frr-805d1ca6b06158ce9f31f77867ef9d293744a95f.zip
Merge pull request #809 from donaldsharp/evpn_plus_struct_attr
Evpn plus struct attr
-rw-r--r--bgpd/Makefile.am4
-rw-r--r--bgpd/bgp_attr.c855
-rw-r--r--bgpd/bgp_attr.h80
-rw-r--r--bgpd/bgp_attr_evpn.c196
-rw-r--r--bgpd/bgp_attr_evpn.h42
-rw-r--r--bgpd/bgp_debug.c82
-rw-r--r--bgpd/bgp_debug.h3
-rw-r--r--bgpd/bgp_ecommunity.c339
-rw-r--r--bgpd/bgp_ecommunity.h51
-rw-r--r--bgpd/bgp_encap_tlv.c65
-rw-r--r--bgpd/bgp_encap_types.h13
-rw-r--r--bgpd/bgp_evpn.c2997
-rw-r--r--bgpd/bgp_evpn.h51
-rw-r--r--bgpd/bgp_evpn_private.h232
-rw-r--r--bgpd/bgp_evpn_vty.c1967
-rw-r--r--bgpd/bgp_evpn_vty.h3
-rw-r--r--bgpd/bgp_label.c6
-rw-r--r--bgpd/bgp_label.h3
-rw-r--r--bgpd/bgp_memory.c5
-rw-r--r--bgpd/bgp_memory.h5
-rw-r--r--bgpd/bgp_mpath.c62
-rw-r--r--bgpd/bgp_mplsvpn.c196
-rw-r--r--bgpd/bgp_mplsvpn.h46
-rw-r--r--bgpd/bgp_nexthop.c4
-rw-r--r--bgpd/bgp_nexthop.h2
-rw-r--r--bgpd/bgp_nht.c6
-rw-r--r--bgpd/bgp_packet.c7
-rw-r--r--bgpd/bgp_rd.c232
-rw-r--r--bgpd/bgp_rd.h74
-rw-r--r--bgpd/bgp_route.c786
-rw-r--r--bgpd/bgp_route.h45
-rw-r--r--bgpd/bgp_routemap.c103
-rw-r--r--bgpd/bgp_snmp.c10
-rw-r--r--bgpd/bgp_updgrp_adv.c13
-rw-r--r--bgpd/bgp_updgrp_packet.c62
-rw-r--r--bgpd/bgp_vty.c19
-rw-r--r--bgpd/bgp_vty.h3
-rw-r--r--bgpd/bgp_zebra.c160
-rw-r--r--bgpd/bgp_zebra.h2
-rw-r--r--bgpd/bgpd.c15
-rw-r--r--bgpd/bgpd.h15
-rw-r--r--bgpd/rfapi/rfapi.c47
-rw-r--r--bgpd/rfapi/rfapi_encap_tlv.c14
-rw-r--r--bgpd/rfapi/rfapi_import.c286
-rw-r--r--bgpd/rfapi/rfapi_rib.c8
-rw-r--r--bgpd/rfapi/rfapi_vty.c38
-rw-r--r--bgpd/rfapi/vnc_export_bgp.c62
-rw-r--r--bgpd/rfapi/vnc_import_bgp.c37
-rw-r--r--lib/command.c7
-rw-r--r--lib/command.h1
-rw-r--r--lib/log.c9
-rw-r--r--lib/vty.c1
-rw-r--r--lib/zclient.c16
-rw-r--r--lib/zclient.h13
-rw-r--r--vtysh/vtysh.c74
-rw-r--r--zebra/Makefile.am5
-rw-r--r--zebra/debug.c34
-rw-r--r--zebra/debug.h4
-rw-r--r--zebra/if_netlink.c373
-rw-r--r--zebra/interface.c139
-rw-r--r--zebra/interface.h67
-rw-r--r--zebra/kernel_netlink.c28
-rw-r--r--zebra/rt.h16
-rw-r--r--zebra/rt_netlink.c730
-rw-r--r--zebra/rt_netlink.h9
-rw-r--r--zebra/rt_socket.c39
-rw-r--r--zebra/rtread_getmsg.c20
-rw-r--r--zebra/rtread_netlink.c21
-rw-r--r--zebra/rtread_sysctl.c20
-rw-r--r--zebra/zebra_l2.c251
-rw-r--r--zebra/zebra_l2.h94
-rw-r--r--zebra/zebra_l2_null.c76
-rw-r--r--zebra/zebra_vrf.c5
-rw-r--r--zebra/zebra_vrf.h9
-rw-r--r--zebra/zebra_vty.c248
-rw-r--r--zebra/zebra_vxlan.c3380
-rw-r--r--zebra/zebra_vxlan.h108
-rw-r--r--zebra/zebra_vxlan_null.c197
-rw-r--r--zebra/zebra_vxlan_private.h199
-rw-r--r--zebra/zserv.c20
-rw-r--r--zebra/zserv.h9
81 files changed, 13436 insertions, 2139 deletions
diff --git a/bgpd/Makefile.am b/bgpd/Makefile.am
index f096f0ff1..0ec5a778b 100644
--- a/bgpd/Makefile.am
+++ b/bgpd/Makefile.am
@@ -83,7 +83,7 @@ libbgp_a_SOURCES = \
bgp_damp.c bgp_table.c bgp_advertise.c bgp_vty.c bgp_mpath.c \
bgp_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \
bgp_encap_tlv.c $(BGP_VNC_RFAPI_SRC) bgp_attr_evpn.c \
- bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c
+ bgp_evpn.c bgp_evpn_vty.c bgp_vpn.c bgp_label.c bgp_rd.c
noinst_HEADERS = \
bgp_memory.h \
@@ -95,7 +95,7 @@ noinst_HEADERS = \
bgp_advertise.h bgp_vty.h bgp_mpath.h bgp_nht.h \
bgp_updgrp.h bgp_bfd.h bgp_encap_tlv.h bgp_encap_types.h \
$(BGP_VNC_RFAPI_HD) bgp_attr_evpn.h bgp_evpn.h bgp_evpn_vty.h \
- bgp_vpn.h bgp_label.h
+ bgp_vpn.h bgp_label.h bgp_rd.h bgp_evpn_private.h
bgpd_SOURCES = bgp_main.c
bgpd_LDADD = libbgp.a $(BGP_VNC_RFP_LIB) ../lib/libfrr.la @LIBCAP@ @LIBM@
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
index 0114834c5..a22a7a5b4 100644
--- a/bgpd/bgp_attr.c
+++ b/bgpd/bgp_attr.c
@@ -270,17 +270,17 @@ encap_free(struct bgp_attr_encap_subtlv *p)
void
bgp_attr_flush_encap(struct attr *attr)
{
- if (!attr || !attr->extra)
+ if (!attr)
return;
- if (attr->extra->encap_subtlvs) {
- encap_free(attr->extra->encap_subtlvs);
- attr->extra->encap_subtlvs = NULL;
+ if (attr->encap_subtlvs) {
+ encap_free(attr->encap_subtlvs);
+ attr->encap_subtlvs = NULL;
}
#if ENABLE_BGP_VNC
- if (attr->extra->vnc_subtlvs) {
- encap_free(attr->extra->vnc_subtlvs);
- attr->extra->vnc_subtlvs = NULL;
+ if (attr->vnc_subtlvs) {
+ encap_free(attr->vnc_subtlvs);
+ attr->vnc_subtlvs = NULL;
}
#endif
}
@@ -424,15 +424,15 @@ encap_finish (void)
}
static bool
-overlay_index_same(const struct attr_extra *ae1, const struct attr_extra *ae2)
+overlay_index_same(const struct attr *a1, const struct attr *a2)
{
- if(!ae1 && ae2)
+ if(!a1 && a2)
return false;
- if(!ae2 && ae1)
+ if(!a2 && a1)
return false;
- if(!ae1 && !ae2)
+ if(!a1 && !a2)
return true;
- return !memcmp(&(ae1->evpn_overlay), &(ae2->evpn_overlay), sizeof(struct overlay_index));
+ return !memcmp(&(a1->evpn_overlay), &(a2->evpn_overlay), sizeof(struct overlay_index));
}
/* Unknown transit attribute. */
@@ -532,34 +532,6 @@ transit_finish (void)
/* Attribute hash routines. */
static struct hash *attrhash;
-static struct attr_extra *
-bgp_attr_extra_new (void)
-{
- struct attr_extra *extra;
- extra = XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra));
- extra->label_index = BGP_INVALID_LABEL_INDEX;
- extra->label = MPLS_INVALID_LABEL;
- return extra;
-}
-
-void
-bgp_attr_extra_free (struct attr *attr)
-{
- if (attr->extra)
- {
- XFREE (MTYPE_ATTR_EXTRA, attr->extra);
- attr->extra = NULL;
- }
-}
-
-struct attr_extra *
-bgp_attr_extra_get (struct attr *attr)
-{
- if (!attr->extra)
- attr->extra = bgp_attr_extra_new();
- return attr->extra;
-}
-
/* Shallow copy of an attribute
* Though, not so shallow that it doesn't copy the contents
* of the attr_extra pointed to by 'extra'
@@ -567,33 +539,7 @@ bgp_attr_extra_get (struct attr *attr)
void
bgp_attr_dup (struct attr *new, struct attr *orig)
{
- struct attr_extra *extra = new->extra;
-
*new = *orig;
- /* if caller provided attr_extra space, use it in any case.
- *
- * This is neccesary even if orig->extra equals NULL, because otherwise
- * memory may be later allocated on the heap by bgp_attr_extra_get.
- *
- * That memory would eventually be leaked, because the caller must not
- * call bgp_attr_extra_free if he provided attr_extra on the stack.
- */
- if (extra)
- {
- new->extra = extra;
- memset(new->extra, 0, sizeof(struct attr_extra));
- new->extra->label_index = BGP_INVALID_LABEL_INDEX;
- new->extra->label = MPLS_INVALID_LABEL;
-
- if (orig->extra) {
- *new->extra = *orig->extra;
- }
- }
- else if (orig->extra)
- {
- new->extra = bgp_attr_extra_new();
- *new->extra = *orig->extra;
- }
}
void
@@ -605,21 +551,18 @@ bgp_attr_deep_dup (struct attr *new, struct attr *orig)
if (orig->community)
new->community = community_dup(orig->community);
- if (orig->extra)
- {
- if (orig->extra->ecommunity)
- new->extra->ecommunity = ecommunity_dup(orig->extra->ecommunity);
- if (orig->extra->cluster)
- new->extra->cluster = cluster_dup(orig->extra->cluster);
- if (orig->extra->transit)
- new->extra->transit = transit_dup(orig->extra->transit);
- if (orig->extra->encap_subtlvs)
- new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs);
+ if (orig->ecommunity)
+ new->ecommunity = ecommunity_dup(orig->ecommunity);
+ if (orig->cluster)
+ new->cluster = cluster_dup(orig->cluster);
+ if (orig->transit)
+ new->transit = transit_dup(orig->transit);
+ if (orig->encap_subtlvs)
+ new->encap_subtlvs = encap_tlv_dup(orig->encap_subtlvs);
#if ENABLE_BGP_VNC
- if (orig->extra->vnc_subtlvs)
- new->extra->vnc_subtlvs = encap_tlv_dup(orig->extra->vnc_subtlvs);
+ if (orig->vnc_subtlvs)
+ new->vnc_subtlvs = encap_tlv_dup(orig->vnc_subtlvs);
#endif
- }
}
void
@@ -631,21 +574,18 @@ bgp_attr_deep_free (struct attr *attr)
if (attr->community)
community_free(attr->community);
- if (attr->extra)
- {
- if (attr->extra->ecommunity)
- ecommunity_free(&attr->extra->ecommunity);
- if (attr->extra->cluster)
- cluster_free(attr->extra->cluster);
- if (attr->extra->transit)
- transit_free(attr->extra->transit);
- if (attr->extra->encap_subtlvs)
- encap_free(attr->extra->encap_subtlvs);
+ if (attr->ecommunity)
+ ecommunity_free(&attr->ecommunity);
+ if (attr->cluster)
+ cluster_free(attr->cluster);
+ if (attr->transit)
+ transit_free(attr->transit);
+ if (attr->encap_subtlvs)
+ encap_free(attr->encap_subtlvs);
#if ENABLE_BGP_VNC
- if (attr->extra->vnc_subtlvs)
- encap_free(attr->extra->vnc_subtlvs);
+ if (attr->vnc_subtlvs)
+ encap_free(attr->vnc_subtlvs);
#endif
- }
}
unsigned long int
@@ -664,7 +604,6 @@ unsigned int
attrhash_key_make (void *p)
{
const struct attr *attr = (struct attr *) p;
- const struct attr_extra *extra = attr->extra;
uint32_t key = 0;
#define MIX(val) key = jhash_1word(val, key)
@@ -678,43 +617,37 @@ attrhash_key_make (void *p)
key += attr->med;
key += attr->local_pref;
- if (extra)
- {
- MIX(extra->aggregator_as);
- MIX(extra->aggregator_addr.s_addr);
- MIX(extra->weight);
- MIX(extra->mp_nexthop_global_in.s_addr);
- MIX(extra->originator_id.s_addr);
- MIX(extra->tag);
- MIX(extra->label);
- MIX(extra->label_index);
- }
-
+ MIX(attr->aggregator_as);
+ MIX(attr->aggregator_addr.s_addr);
+ MIX(attr->weight);
+ MIX(attr->mp_nexthop_global_in.s_addr);
+ MIX(attr->originator_id.s_addr);
+ MIX(attr->tag);
+ MIX(attr->label);
+ MIX(attr->label_index);
+
if (attr->aspath)
MIX(aspath_key_make (attr->aspath));
if (attr->community)
MIX(community_hash_make (attr->community));
-
- if (extra)
- {
- if (extra->lcommunity)
- MIX(lcommunity_hash_make (extra->lcommunity));
- if (extra->ecommunity)
- MIX(ecommunity_hash_make (extra->ecommunity));
- if (extra->cluster)
- MIX(cluster_hash_key_make (extra->cluster));
- if (extra->transit)
- MIX(transit_hash_key_make (extra->transit));
- if (extra->encap_subtlvs)
- MIX(encap_hash_key_make (extra->encap_subtlvs));
+
+ if (attr->lcommunity)
+ MIX(lcommunity_hash_make (attr->lcommunity));
+ if (attr->ecommunity)
+ MIX(ecommunity_hash_make (attr->ecommunity));
+ if (attr->cluster)
+ MIX(cluster_hash_key_make (attr->cluster));
+ if (attr->transit)
+ MIX(transit_hash_key_make (attr->transit));
+ if (attr->encap_subtlvs)
+ MIX(encap_hash_key_make (attr->encap_subtlvs));
#if ENABLE_BGP_VNC
- if (extra->vnc_subtlvs)
- MIX(encap_hash_key_make (extra->vnc_subtlvs));
+ if (attr->vnc_subtlvs)
+ MIX(encap_hash_key_make (attr->vnc_subtlvs));
#endif
- MIX(extra->mp_nexthop_len);
- key = jhash(extra->mp_nexthop_global.s6_addr, IPV6_MAX_BYTELEN, key);
- key = jhash(extra->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key);
- }
+ MIX(attr->mp_nexthop_len);
+ key = jhash(attr->mp_nexthop_global.s6_addr, IPV6_MAX_BYTELEN, key);
+ key = jhash(attr->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key);
return key;
}
@@ -734,38 +667,30 @@ attrhash_cmp (const void *p1, const void *p2)
&& attr1->local_pref == attr2->local_pref
&& attr1->rmap_change_flags == attr2->rmap_change_flags)
{
- const struct attr_extra *ae1 = attr1->extra;
- const struct attr_extra *ae2 = attr2->extra;
-
- if (ae1 && ae2
- && ae1->aggregator_as == ae2->aggregator_as
- && ae1->aggregator_addr.s_addr == ae2->aggregator_addr.s_addr
- && ae1->weight == ae2->weight
- && ae1->tag == ae2->tag
- && ae1->label_index == ae2->label_index
- && ae1->mp_nexthop_len == ae2->mp_nexthop_len
- && IPV6_ADDR_SAME (&ae1->mp_nexthop_global, &ae2->mp_nexthop_global)
- && IPV6_ADDR_SAME (&ae1->mp_nexthop_local, &ae2->mp_nexthop_local)
- && IPV4_ADDR_SAME (&ae1->mp_nexthop_global_in, &ae2->mp_nexthop_global_in)
- && ae1->ecommunity == ae2->ecommunity
- && ae1->lcommunity == ae2->lcommunity
- && ae1->cluster == ae2->cluster
- && ae1->transit == ae2->transit
- && (ae1->encap_tunneltype == ae2->encap_tunneltype)
- && encap_same(ae1->encap_subtlvs, ae2->encap_subtlvs)
+ if (attr1->aggregator_as == attr2->aggregator_as
+ && attr1->aggregator_addr.s_addr == attr2->aggregator_addr.s_addr
+ && attr1->weight == attr2->weight
+ && attr1->tag == attr2->tag
+ && attr1->label_index == attr2->label_index
+ && attr1->mp_nexthop_len == attr2->mp_nexthop_len
+ && IPV6_ADDR_SAME (&attr1->mp_nexthop_global, &attr2->mp_nexthop_global)
+ && IPV6_ADDR_SAME (&attr1->mp_nexthop_local, &attr2->mp_nexthop_local)
+ && IPV4_ADDR_SAME (&attr1->mp_nexthop_global_in, &attr2->mp_nexthop_global_in)
+ && attr1->ecommunity == attr2->ecommunity
+ && attr1->lcommunity == attr2->lcommunity
+ && attr1->cluster == attr2->cluster
+ && attr1->transit == attr2->transit
+ && (attr1->encap_tunneltype == attr2->encap_tunneltype)
+ && encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs)
#if ENABLE_BGP_VNC
- && encap_same(ae1->vnc_subtlvs, ae2->vnc_subtlvs)
+ && encap_same(attr1->vnc_subtlvs, attr2->vnc_subtlvs)
#endif
- && IPV4_ADDR_SAME (&ae1->originator_id, &ae2->originator_id)
- && overlay_index_same(ae1, ae2))
+ && IPV4_ADDR_SAME (&attr1->originator_id, &attr2->originator_id)
+ && overlay_index_same(attr1, attr2))
return 1;
- else if (ae1 || ae2)
- return 0;
- /* neither attribute has extra attributes, so they're same */
- return 1;
}
- else
- return 0;
+
+ return 0;
}
static void
@@ -780,7 +705,6 @@ attrhash_init (void)
static void
attr_vfree (void *attr)
{
- bgp_attr_extra_free ((struct attr *)attr);
XFREE (MTYPE_ATTR, attr);
}
@@ -813,24 +737,19 @@ attr_show_all (struct vty *vty)
static void *
bgp_attr_hash_alloc (void *p)
{
- const struct attr * val = (const struct attr *) p;
+ struct attr * val = (struct attr *) p;
struct attr *attr;
attr = XMALLOC (MTYPE_ATTR, sizeof (struct attr));
*attr = *val;
- if (val->extra)
- {
- attr->extra = bgp_attr_extra_new ();
- *attr->extra = *val->extra;
- if (val->extra->encap_subtlvs) {
- val->extra->encap_subtlvs = NULL;
- }
+ if (val->encap_subtlvs) {
+ val->encap_subtlvs = NULL;
+ }
#if ENABLE_BGP_VNC
- if (val->extra->vnc_subtlvs) {
- val->extra->vnc_subtlvs = NULL;
- }
+ if (val->vnc_subtlvs) {
+ val->vnc_subtlvs = NULL;
+ }
#endif
- }
attr->refcnt = 0;
return attr;
}
@@ -856,56 +775,51 @@ bgp_attr_intern (struct attr *attr)
else
attr->community->refcnt++;
}
- if (attr->extra)
+
+ if (attr->ecommunity)
{
- struct attr_extra *attre = attr->extra;
-
- if (attre->ecommunity)
- {
- if (! attre->ecommunity->refcnt)
- attre->ecommunity = ecommunity_intern (attre->ecommunity);
- else
- attre->ecommunity->refcnt++;
-
- }
- if (attre->lcommunity)
- {
- if (! attre->lcommunity->refcnt)
- attre->lcommunity = lcommunity_intern (attre->lcommunity);
- else
- attre->lcommunity->refcnt++;
- }
- if (attre->cluster)
- {
- if (! attre->cluster->refcnt)
- attre->cluster = cluster_intern (attre->cluster);
- else
- attre->cluster->refcnt++;
- }
- if (attre->transit)
- {
- if (! attre->transit->refcnt)
- attre->transit = transit_intern (attre->transit);
- else
- attre->transit->refcnt++;
- }
- if (attre->encap_subtlvs)
- {
- if (! attre->encap_subtlvs->refcnt)
- attre->encap_subtlvs = encap_intern (attre->encap_subtlvs, ENCAP_SUBTLV_TYPE);
- else
- attre->encap_subtlvs->refcnt++;
- }
+ if (! attr->ecommunity->refcnt)
+ attr->ecommunity = ecommunity_intern (attr->ecommunity);
+ else
+ attr->ecommunity->refcnt++;
+ }
+ if (attr->lcommunity)
+ {
+ if (! attr->lcommunity->refcnt)
+ attr->lcommunity = lcommunity_intern (attr->lcommunity);
+ else
+ attr->lcommunity->refcnt++;
+ }
+ if (attr->cluster)
+ {
+ if (! attr->cluster->refcnt)
+ attr->cluster = cluster_intern (attr->cluster);
+ else
+ attr->cluster->refcnt++;
+ }
+ if (attr->transit)
+ {
+ if (! attr->transit->refcnt)
+ attr->transit = transit_intern (attr->transit);
+ else
+ attr->transit->refcnt++;
+ }
+ if (attr->encap_subtlvs)
+ {
+ if (! attr->encap_subtlvs->refcnt)
+ attr->encap_subtlvs = encap_intern (attr->encap_subtlvs, ENCAP_SUBTLV_TYPE);
+ else
+ attr->encap_subtlvs->refcnt++;
+ }
#if ENABLE_BGP_VNC
- if (attre->vnc_subtlvs)
- {
- if (! attre->vnc_subtlvs->refcnt)
- attre->vnc_subtlvs = encap_intern (attre->vnc_subtlvs, VNC_SUBTLV_TYPE);
- else
- attre->vnc_subtlvs->refcnt++;
- }
-#endif
+ if (attr->vnc_subtlvs)
+ {
+ if (! attr->vnc_subtlvs->refcnt)
+ attr->vnc_subtlvs = encap_intern (attr->vnc_subtlvs, VNC_SUBTLV_TYPE);
+ else
+ attr->vnc_subtlvs->refcnt++;
}
+#endif
find = (struct attr *) hash_get (attrhash, attr, bgp_attr_hash_alloc);
find->refcnt++;
@@ -932,26 +846,23 @@ bgp_attr_refcount (struct attr *attr)
if (attr->community)
attr->community->refcnt++;
- if (attr->extra)
- {
- struct attr_extra *attre = attr->extra;
- if (attre->ecommunity)
- attre->ecommunity->refcnt++;
+ if (attr->ecommunity)
+ attr->ecommunity->refcnt++;
- if (attre->cluster)
- attre->cluster->refcnt++;
+ if (attr->cluster)
+ attr->cluster->refcnt++;
- if (attre->transit)
- attre->transit->refcnt++;
+ if (attr->transit)
+ attr->transit->refcnt++;
- if (attre->encap_subtlvs)
- attre->encap_subtlvs->refcnt++;
+ if (attr->encap_subtlvs)
+ attr->encap_subtlvs->refcnt++;
#if ENABLE_BGP_VNC
- if (attre->vnc_subtlvs)
- attre->vnc_subtlvs->refcnt++;
+ if (attr->vnc_subtlvs)
+ attr->vnc_subtlvs->refcnt++;
#endif
- }
+
attr->refcnt++;
return attr;
}
@@ -961,18 +872,17 @@ struct attr *
bgp_attr_default_set (struct attr *attr, u_char origin)
{
memset (attr, 0, sizeof (struct attr));
- bgp_attr_extra_get (attr);
attr->origin = origin;
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
attr->aspath = aspath_empty ();
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
- attr->extra->weight = BGP_ATTR_DEFAULT_WEIGHT;
- attr->extra->tag = 0;
- attr->extra->label_index = BGP_INVALID_LABEL_INDEX;
- attr->extra->label = MPLS_INVALID_LABEL;
+ attr->weight = BGP_ATTR_DEFAULT_WEIGHT;
+ attr->tag = 0;
+ attr->label_index = BGP_INVALID_LABEL_INDEX;
+ attr->label = MPLS_INVALID_LABEL;
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
- attr->extra->mp_nexthop_len = IPV6_MAX_BYTELEN;
+ attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
return attr;
}
@@ -986,11 +896,8 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin,
{
struct attr attr;
struct attr *new;
- struct attr_extra attre;
memset (&attr, 0, sizeof (struct attr));
- memset (&attre, 0, sizeof (struct attr_extra));
- attr.extra = &attre;
/* Origin attribute. */
attr.origin = origin;
@@ -1012,18 +919,20 @@ bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin,
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
}
- attre.label_index = BGP_INVALID_LABEL_INDEX;
- attre.label = MPLS_INVALID_LABEL;
- attre.weight = BGP_ATTR_DEFAULT_WEIGHT;
- attre.mp_nexthop_len = IPV6_MAX_BYTELEN;
+ attr.label_index = BGP_INVALID_LABEL_INDEX;
+ attr.label = MPLS_INVALID_LABEL;
+ attr.weight = BGP_ATTR_DEFAULT_WEIGHT;
+ attr.mp_nexthop_len = IPV6_MAX_BYTELEN;
if (! as_set || atomic_aggregate)
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION))
- attre.aggregator_as = bgp->confed_id;
+ attr.aggregator_as = bgp->confed_id;
else
- attre.aggregator_as = bgp->as;
- attre.aggregator_addr = bgp->router_id;
+ attr.aggregator_as = bgp->as;
+ attr.aggregator_addr = bgp->router_id;
+ attr.label_index = BGP_INVALID_LABEL_INDEX;
+ attr.label = MPLS_INVALID_LABEL;
new = bgp_attr_intern (&attr);
@@ -1044,31 +953,28 @@ bgp_attr_unintern_sub (struct attr *attr)
community_unintern (&attr->community);
UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES));
- if (attr->extra)
- {
- if (attr->extra->ecommunity)
- ecommunity_unintern (&attr->extra->ecommunity);
- UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES));
+ if (attr->ecommunity)
+ ecommunity_unintern (&attr->ecommunity);
+ UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES));
- if (attr->extra->lcommunity)
- lcommunity_unintern (&attr->extra->lcommunity);
- UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
+ if (attr->lcommunity)
+ lcommunity_unintern (&attr->lcommunity);
+ UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
- if (attr->extra->cluster)
- cluster_unintern (attr->extra->cluster);
- UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST));
-
- if (attr->extra->transit)
- transit_unintern (attr->extra->transit);
+ if (attr->cluster)
+ cluster_unintern (attr->cluster);
+ UNSET_FLAG(attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST));
- if (attr->extra->encap_subtlvs)
- encap_unintern (&attr->extra->encap_subtlvs, ENCAP_SUBTLV_TYPE);
+ if (attr->transit)
+ transit_unintern (attr->transit);
+
+ if (attr->encap_subtlvs)
+ encap_unintern (&attr->encap_subtlvs, ENCAP_SUBTLV_TYPE);
#if ENABLE_BGP_VNC
- if (attr->extra->vnc_subtlvs)
- encap_unintern (&attr->extra->vnc_subtlvs, VNC_SUBTLV_TYPE);
+ if (attr->vnc_subtlvs)
+ encap_unintern (&attr->vnc_subtlvs, VNC_SUBTLV_TYPE);
#endif
- }
}
/* Free bgp attribute and aspath. */
@@ -1078,25 +984,17 @@ bgp_attr_unintern (struct attr **pattr)
struct attr *attr = *pattr;
struct attr *ret;
struct attr tmp;
- struct attr_extra tmp_extra;
-
+
/* Decrement attribute reference. */
attr->refcnt--;
-
+
tmp = *attr;
- if (attr->extra)
- {
- tmp.extra = &tmp_extra;
- memcpy (tmp.extra, attr->extra, sizeof (struct attr_extra));
- }
-
/* If reference becomes zero then free attribute object. */
if (attr->refcnt == 0)
{
ret = hash_release (attrhash, attr);
assert (ret != NULL);
- bgp_attr_extra_free (attr);
XFREE (MTYPE_ATTR, attr);
*pattr = NULL;
}
@@ -1117,37 +1015,33 @@ bgp_attr_flush (struct attr *attr)
community_free (attr->community);
attr->community = NULL;
}
- if (attr->extra)
- {
- struct attr_extra *attre = attr->extra;
- if (attre->ecommunity && ! attre->ecommunity->refcnt)
- ecommunity_free (&attre->ecommunity);
- if (attre->lcommunity && ! attre->lcommunity->refcnt)
- lcommunity_free (&attre->lcommunity);
- if (attre->cluster && ! attre->cluster->refcnt)
- {
- cluster_free (attre->cluster);
- attre->cluster = NULL;
- }
- if (attre->transit && ! attre->transit->refcnt)
- {
- transit_free (attre->transit);
- attre->transit = NULL;
- }
- if (attre->encap_subtlvs && ! attre->encap_subtlvs->refcnt)
- {
- encap_free(attre->encap_subtlvs);
- attre->encap_subtlvs = NULL;
- }
+ if (attr->ecommunity && ! attr->ecommunity->refcnt)
+ ecommunity_free (&attr->ecommunity);
+ if (attr->lcommunity && ! attr->lcommunity->refcnt)
+ lcommunity_free (&attr->lcommunity);
+ if (attr->cluster && ! attr->cluster->refcnt)
+ {
+ cluster_free (attr->cluster);
+ attr->cluster = NULL;
+ }
+ if (attr->transit && ! attr->transit->refcnt)
+ {
+ transit_free (attr->transit);
+ attr->transit = NULL;
+ }
+ if (attr->encap_subtlvs && ! attr->encap_subtlvs->refcnt)
+ {
+ encap_free(attr->encap_subtlvs);
+ attr->encap_subtlvs = NULL;
+ }
#if ENABLE_BGP_VNC
- if (attre->vnc_subtlvs && ! attre->vnc_subtlvs->refcnt)
- {
- encap_free(attre->vnc_subtlvs);
- attre->vnc_subtlvs = NULL;
- }
-#endif
+ if (attr->vnc_subtlvs && ! attr->vnc_subtlvs->refcnt)
+ {
+ encap_free(attr->vnc_subtlvs);
+ attr->vnc_subtlvs = NULL;
}
+#endif
}
/* Implement draft-scudder-idr-optional-transitive behaviour and
@@ -1630,7 +1524,6 @@ bgp_attr_aggregator (struct bgp_attr_parser_args *args)
const bgp_size_t length = args->length;
int wantedlen = 6;
- struct attr_extra *attre = bgp_attr_extra_get (attr);
/* peer with AS4 will send 4 Byte AS, peer without will send 2 Byte */
if (CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV))
@@ -1645,10 +1538,10 @@ bgp_attr_aggregator (struct bgp_attr_parser_args *args)
}
if ( CHECK_FLAG (peer->cap, PEER_CAP_AS4_RCV ) )
- attre->aggregator_as = stream_getl (peer->ibuf);
+ attr->aggregator_as = stream_getl (peer->ibuf);
else
- attre->aggregator_as = stream_getw (peer->ibuf);
- attre->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf);
+ attr->aggregator_as = stream_getw (peer->ibuf);
+ attr->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf);
/* Set atomic aggregate flag. */
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
@@ -1692,7 +1585,6 @@ bgp_attr_munge_as4_attrs (struct peer *const peer,
{
int ignore_as4_path = 0;
struct aspath *newpath;
- struct attr_extra *attre = attr->extra;
if (!attr->aspath)
{
@@ -1732,8 +1624,6 @@ bgp_attr_munge_as4_attrs (struct peer *const peer,
{
if (attr->flag & (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR) ) )
{
- assert (attre);
-
/* received both.
* if the as_number in aggregator is not AS_TRANS,
* then AS4_AGGREGATOR and AS4_PATH shall be ignored
@@ -1746,7 +1636,7 @@ bgp_attr_munge_as4_attrs (struct peer *const peer,
* Aggregating node and the AS_PATH is to be
* constructed "as in all other cases"
*/
- if (attre->aggregator_as != BGP_AS_TRANS)
+ if (attr->aggregator_as != BGP_AS_TRANS)
{
/* ignore */
if ( BGP_DEBUG(as4, AS4))
@@ -1759,8 +1649,8 @@ bgp_attr_munge_as4_attrs (struct peer *const peer,
else
{
/* "New_aggregator shall be taken as aggregator" */
- attre->aggregator_as = as4_aggregator;
- attre->aggregator_addr.s_addr = as4_aggregator_addr->s_addr;
+ attr->aggregator_as = as4_aggregator;
+ attr->aggregator_addr.s_addr = as4_aggregator_addr->s_addr;
}
}
else
@@ -1774,7 +1664,7 @@ bgp_attr_munge_as4_attrs (struct peer *const peer,
zlog_debug ("[AS4] %s BGP not AS4 capable peer send"
" AS4_AGGREGATOR but no AGGREGATOR, will take"
" it as if AGGREGATOR with AS_TRANS had been there", peer->host);
- (attre = bgp_attr_extra_get (attr))->aggregator_as = as4_aggregator;
+ attr->aggregator_as = as4_aggregator;
/* sweep it under the carpet and simulate a "good" AGGREGATOR */
attr->flag |= (ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR));
}
@@ -1838,8 +1728,7 @@ bgp_attr_originator_id (struct bgp_attr_parser_args *args)
args->total);
}
- (bgp_attr_extra_get (attr))->originator_id.s_addr
- = stream_get_ipv4 (peer->ibuf);
+ attr->originator_id.s_addr = stream_get_ipv4 (peer->ibuf);
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
@@ -1863,8 +1752,7 @@ bgp_attr_cluster_list (struct bgp_attr_parser_args *args)
args->total);
}
- (bgp_attr_extra_get (attr))->cluster
- = cluster_parse ((struct in_addr *)stream_pnt (peer->ibuf), length);
+ attr->cluster = cluster_parse ((struct in_addr *)stream_pnt (peer->ibuf), length);
/* XXX: Fix cluster_parse to use stream API and then remove this */
stream_forward_getp (peer->ibuf, length);
@@ -1888,7 +1776,6 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
const bgp_size_t length = args->length;
- struct attr_extra *attre = bgp_attr_extra_get(attr);
/* Set end of packet. */
s = BGP_INPUT(peer);
@@ -1921,53 +1808,53 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
}
/* Get nexthop length. */
- attre->mp_nexthop_len = stream_getc (s);
+ attr->mp_nexthop_len = stream_getc (s);
- if (LEN_LEFT < attre->mp_nexthop_len)
+ if (LEN_LEFT < attr->mp_nexthop_len)
{
zlog_info ("%s: %s, MP nexthop length, %u, goes past end of attribute",
- __func__, peer->host, attre->mp_nexthop_len);
+ __func__, peer->host, attr->mp_nexthop_len);
return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
}
/* Nexthop length check. */
- switch (attre->mp_nexthop_len)
+ switch (attr->mp_nexthop_len)
{
case BGP_ATTR_NHLEN_IPV4:
- stream_get (&attre->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN);
+ stream_get (&attr->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN);
/* Probably needed for RFC 2283 */
if (attr->nexthop.s_addr == 0)
- memcpy(&attr->nexthop.s_addr, &attre->mp_nexthop_global_in, IPV4_MAX_BYTELEN);
+ memcpy(&attr->nexthop.s_addr, &attr->mp_nexthop_global_in, IPV4_MAX_BYTELEN);
break;
case BGP_ATTR_NHLEN_VPNV4:
stream_getl (s); /* RD high */
stream_getl (s); /* RD low */
- stream_get (&attre->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN);
+ stream_get (&attr->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN);
break;
case BGP_ATTR_NHLEN_IPV6_GLOBAL:
case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
- if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL)
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL)
{
stream_getl (s); /* RD high */
stream_getl (s); /* RD low */
}
- stream_get (&attre->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
+ stream_get (&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
break;
case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
case BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL:
- if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
{
stream_getl (s); /* RD high */
stream_getl (s); /* RD low */
}
- stream_get (&attre->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
- if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
+ stream_get (&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
{
stream_getl (s); /* RD high */
stream_getl (s); /* RD low */
}
- stream_get (&attre->mp_nexthop_local, s, IPV6_MAX_BYTELEN);
- if (! IN6_IS_ADDR_LINKLOCAL (&attre->mp_nexthop_local))
+ stream_get (&attr->mp_nexthop_local, s, IPV6_MAX_BYTELEN);
+ if (! IN6_IS_ADDR_LINKLOCAL (&attr->mp_nexthop_local))
{
char buf1[INET6_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
@@ -1975,17 +1862,17 @@ bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
if (bgp_debug_update(peer, NULL, NULL, 1))
zlog_debug ("%s rcvd nexthops %s, %s -- ignoring non-LL value",
peer->host,
- inet_ntop (AF_INET6, &attre->mp_nexthop_global,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global,
buf1, INET6_ADDRSTRLEN),
- inet_ntop (AF_INET6, &attre->mp_nexthop_local,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_local,
buf2, INET6_ADDRSTRLEN));
- attre->mp_nexthop_len = IPV6_MAX_BYTELEN;
+ attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
}
break;
default:
zlog_info ("%s: (%s) Wrong multiprotocol next hop length: %d",
- __func__, peer->host, attre->mp_nexthop_len);
+ __func__, peer->host, attr->mp_nexthop_len);
return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
}
@@ -2087,18 +1974,16 @@ bgp_attr_large_community (struct bgp_attr_parser_args *args)
*/
if (length == 0)
{
- if (attr->extra)
- attr->extra->lcommunity = NULL;
+ attr->lcommunity = NULL;
/* Empty extcomm doesn't seem to be invalid per se */
return BGP_ATTR_PARSE_PROCEED;
}
- (bgp_attr_extra_get (attr))->lcommunity =
- lcommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
+ attr->lcommunity = lcommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
/* XXX: fix ecommunity_parse to use stream API */
stream_forward_getp (peer->ibuf, length);
- if (attr->extra && !attr->extra->lcommunity)
+ if (!attr->lcommunity)
return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
args->total);
@@ -2115,27 +2000,30 @@ bgp_attr_ext_communities (struct bgp_attr_parser_args *args)
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
const bgp_size_t length = args->length;
+ u_char sticky = 0;
if (length == 0)
{
- if (attr->extra)
- attr->extra->ecommunity = NULL;
+ attr->ecommunity = NULL;
/* Empty extcomm doesn't seem to be invalid per se */
return BGP_ATTR_PARSE_PROCEED;
}
- (bgp_attr_extra_get (attr))->ecommunity =
- ecommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
+ attr->ecommunity = ecommunity_parse ((u_int8_t *)stream_pnt (peer->ibuf), length);
/* XXX: fix ecommunity_parse to use stream API */
stream_forward_getp (peer->ibuf, length);
- if (attr->extra && !attr->extra->ecommunity)
+ if (!attr->ecommunity)
return bgp_attr_malformed (args,
BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
args->total);
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
+ /* Extract MAC mobility sequence number, if any. */
+ attr->mm_seqnum = bgp_attr_mac_mobility_seqnum (attr, &sticky);
+ attr->sticky = sticky;
+
return BGP_ATTR_PARSE_PROCEED;
}
@@ -2150,7 +2038,6 @@ bgp_attr_encap(
u_char *startp)
{
bgp_size_t total;
- struct attr_extra *attre = NULL;
struct bgp_attr_encap_subtlv *stlv_last = NULL;
uint16_t tunneltype = 0;
@@ -2225,37 +2112,30 @@ bgp_attr_encap(
length -= sublength;
/* attach tlv to encap chain */
- if (!attre) {
- attre = bgp_attr_extra_get(attr);
- if (BGP_ATTR_ENCAP == type) {
- for (stlv_last = attre->encap_subtlvs; stlv_last && stlv_last->next;
- stlv_last = stlv_last->next);
- if (stlv_last) {
- stlv_last->next = tlv;
- } else {
- attre->encap_subtlvs = tlv;
- }
+ if (BGP_ATTR_ENCAP == type) {
+ for (stlv_last = attr->encap_subtlvs; stlv_last && stlv_last->next;
+ stlv_last = stlv_last->next);
+ if (stlv_last) {
+ stlv_last->next = tlv;
+ } else {
+ attr->encap_subtlvs = tlv;
+ }
#if ENABLE_BGP_VNC
- } else {
- for (stlv_last = attre->vnc_subtlvs; stlv_last && stlv_last->next;
- stlv_last = stlv_last->next);
- if (stlv_last) {
- stlv_last->next = tlv;
- } else {
- attre->vnc_subtlvs = tlv;
- }
-#endif
- }
} else {
- stlv_last->next = tlv;
+ for (stlv_last = attr->vnc_subtlvs; stlv_last && stlv_last->next;
+ stlv_last = stlv_last->next);
+ if (stlv_last) {
+ stlv_last->next = tlv;
+ } else {
+ attr->vnc_subtlvs = tlv;
+ }
+#endif
}
- stlv_last = tlv;
+ stlv_last->next = tlv;
}
if (BGP_ATTR_ENCAP == type) {
- if (!attre)
- attre = bgp_attr_extra_get(attr);
- attre->encap_tunneltype = tunneltype;
+ attr->encap_tunneltype = tunneltype;
}
if (length) {
@@ -2314,14 +2194,14 @@ bgp_attr_prefix_sid (struct bgp_attr_parser_args *args, struct bgp_nlri *mp_upda
args->total);
/* Store label index; subsequently, we'll check on address-family */
- (bgp_attr_extra_get (attr))->label_index = label_index;
+ attr->label_index = label_index;
/*
* Ignore the Label index attribute unless received for labeled-unicast
* SAFI.
*/
if (!mp_update->length || mp_update->safi != SAFI_LABELED_UNICAST)
- attr->extra->label_index = BGP_INVALID_LABEL_INDEX;
+ attr->label_index = BGP_INVALID_LABEL_INDEX;
}
/* Placeholder code for the IPv6 SID type */
@@ -2377,7 +2257,6 @@ bgp_attr_unknown (struct bgp_attr_parser_args *args)
{
bgp_size_t total = args->total;
struct transit *transit;
- struct attr_extra *attre;
struct peer *const peer = args->peer;
struct attr *const attr = args->attr;
u_char *const startp = args->startp;
@@ -2415,10 +2294,10 @@ bgp_attr_unknown (struct bgp_attr_parser_args *args)
SET_FLAG (*startp, BGP_ATTR_FLAG_PARTIAL);
/* Store transitive attribute to the end of attr->transit. */
- if (! ((attre = bgp_attr_extra_get(attr))->transit) )
- attre->transit = XCALLOC (MTYPE_TRANSIT, sizeof (struct transit));
+ if (!attr->transit)
+ attr->transit = XCALLOC (MTYPE_TRANSIT, sizeof (struct transit));
- transit = attre->transit;
+ transit = attr->transit;
if (transit->val)
transit->val = XREALLOC (MTYPE_TRANSIT_VAL, transit->val,
@@ -2800,18 +2679,15 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
if (ret != BGP_ATTR_PARSE_PROCEED)
return ret;
}
- if (attr->extra)
- {
- /* Finally intern unknown attribute. */
- if (attr->extra->transit)
- attr->extra->transit = transit_intern (attr->extra->transit);
- if (attr->extra->encap_subtlvs)
- attr->extra->encap_subtlvs = encap_intern (attr->extra->encap_subtlvs, ENCAP_SUBTLV_TYPE);
+ /* Finally intern unknown attribute. */
+ if (attr->transit)
+ attr->transit = transit_intern (attr->transit);
+ if (attr->encap_subtlvs)
+ attr->encap_subtlvs = encap_intern (attr->encap_subtlvs, ENCAP_SUBTLV_TYPE);
#if ENABLE_BGP_VNC
- if (attr->extra->vnc_subtlvs)
- attr->extra->vnc_subtlvs = encap_intern (attr->extra->vnc_subtlvs, VNC_SUBTLV_TYPE);
+ if (attr->vnc_subtlvs)
+ attr->vnc_subtlvs = encap_intern (attr->vnc_subtlvs, VNC_SUBTLV_TYPE);
#endif
- }
return BGP_ATTR_PARSE_PROCEED;
}
@@ -2841,16 +2717,14 @@ bgp_packet_mpattr_start (struct stream *s, struct peer *peer,
stream_putc (s, pkt_safi); /* SAFI */
/* Nexthop AFI */
- if (peer_cap_enhe(peer, afi, safi)) {
- nh_afi = AFI_IP6;
- } else {
- if (afi == AFI_L2VPN)
- nh_afi = AFI_L2VPN;
- else if (safi == SAFI_LABELED_UNICAST)
- nh_afi = afi;
- else
- nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->extra->mp_nexthop_len);
- }
+ if (afi == AFI_IP && safi == SAFI_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);
/* Nexthop */
bpacket_attr_vec_arr_set_vec (vecarr, BGP_ATTR_VEC_NH, s, attr);
@@ -2869,11 +2743,12 @@ bgp_packet_mpattr_start (struct stream *s, struct peer *peer,
stream_putc (s, 12);
stream_putl (s, 0); /* RD = 0, per RFC */
stream_putl (s, 0);
- stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
+ stream_put (s, &attr->mp_nexthop_global_in, 4);
break;
case SAFI_ENCAP:
+ case SAFI_EVPN:
stream_putc (s, 4);
- stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
+ stream_put (s, &attr->mp_nexthop_global_in, 4);
break;
default:
break;
@@ -2885,85 +2760,47 @@ bgp_packet_mpattr_start (struct stream *s, struct peer *peer,
case SAFI_UNICAST:
case SAFI_MULTICAST:
case SAFI_LABELED_UNICAST:
+ case SAFI_EVPN:
{
- struct attr_extra *attre = attr->extra;
-
- assert (attr->extra);
-
- if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
stream_putc (s, BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL);
- stream_put (s, &attre->mp_nexthop_global, IPV6_MAX_BYTELEN);
- stream_put (s, &attre->mp_nexthop_local, IPV6_MAX_BYTELEN);
+ stream_put (s, &attr->mp_nexthop_global, IPV6_MAX_BYTELEN);
+ stream_put (s, &attr->mp_nexthop_local, IPV6_MAX_BYTELEN);
} else {
stream_putc (s, IPV6_MAX_BYTELEN);
- stream_put (s, &attre->mp_nexthop_global, IPV6_MAX_BYTELEN);
+ stream_put (s, &attr->mp_nexthop_global, IPV6_MAX_BYTELEN);
}
}
break;
case SAFI_MPLS_VPN:
{
- struct attr_extra *attre = attr->extra;
-
- assert (attr->extra);
- if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL) {
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL) {
stream_putc (s, 24);
stream_putl (s, 0); /* RD = 0, per RFC */
stream_putl (s, 0);
- stream_put (s, &attre->mp_nexthop_global, IPV6_MAX_BYTELEN);
- } else if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ stream_put (s, &attr->mp_nexthop_global, IPV6_MAX_BYTELEN);
+ } else if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
stream_putc (s, 48);
stream_putl (s, 0); /* RD = 0, per RFC */
stream_putl (s, 0);
- stream_put (s, &attre->mp_nexthop_global, IPV6_MAX_BYTELEN);
+ stream_put (s, &attr->mp_nexthop_global, IPV6_MAX_BYTELEN);
stream_putl (s, 0); /* RD = 0, per RFC */
stream_putl (s, 0);
- stream_put (s, &attre->mp_nexthop_local, IPV6_MAX_BYTELEN);
+ stream_put (s, &attr->mp_nexthop_local, IPV6_MAX_BYTELEN);
}
}
break;
case SAFI_ENCAP:
- assert (attr->extra);
stream_putc (s, IPV6_MAX_BYTELEN);
- stream_put (s, &attr->extra->mp_nexthop_global, IPV6_MAX_BYTELEN);
+ stream_put (s, &attr->mp_nexthop_global, IPV6_MAX_BYTELEN);
break;
default:
break;
}
break;
- case AFI_L2VPN:
- switch (safi)
- {
- case SAFI_EVPN:
- if (attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV4)
- {
- stream_putc (s, 12);
- stream_putl (s, 0); /* RD = 0, per RFC */
- stream_putl (s, 0);
- stream_put (s, &attr->extra->mp_nexthop_global_in, 4);
- }
- else if (attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL)
- {
- stream_putc (s, 24);
- stream_putl (s, 0); /* RD = 0, per RFC */
- stream_putl (s, 0);
- stream_put (s, &attr->extra->mp_nexthop_global, IPV6_MAX_BYTELEN);
- }
- else if (attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
- {
- stream_putc (s, 48);
- stream_putl (s, 0); /* RD = 0, per RFC */
- stream_putl (s, 0);
- stream_put (s, &attr->extra->mp_nexthop_global, IPV6_MAX_BYTELEN);
- stream_putl (s, 0); /* RD = 0, per RFC */
- stream_putl (s, 0);
- stream_put (s, &attr->extra->mp_nexthop_local, IPV6_MAX_BYTELEN);
- }
- break;
- break;
- default:
- break;
- }
default:
+ zlog_err ("Bad nexthop when sening to %s, AFI %u SAFI %u nhlen %d",
+ peer->host, afi, safi, attr->mp_nexthop_len);
break;
}
@@ -2988,9 +2825,11 @@ bgp_packet_mpattr_prefix (struct stream *s, afi_t afi, safi_t safi,
stream_put (s, prd->val, 8);
stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
}
- else if (safi == SAFI_EVPN)
+ else if (afi == AFI_L2VPN && safi == SAFI_EVPN)
{
- bgp_packet_mpattr_route_type_5(s, p, prd, label, attr);
+ /* EVPN prefix - contents depend on type */
+ bgp_evpn_encode_prefix (s, p, prd, label, attr,
+ addpath_encode, addpath_tx_id);
}
else if (safi == SAFI_LABELED_UNICAST)
{
@@ -3007,6 +2846,8 @@ bgp_packet_mpattr_prefix_size (afi_t afi, safi_t safi, struct prefix *p)
int size = PSIZE (p->prefixlen);
if (safi == SAFI_MPLS_VPN)
size += 88;
+ else if (afi == AFI_L2VPN && safi == SAFI_EVPN)
+ size += 232; // TODO: Maximum possible for type-2, type-3 and type-5
return size;
}
@@ -3029,16 +2870,16 @@ bgp_packet_mpattr_tea(
struct bgp_attr_encap_subtlv *st;
const char *attrname;
- if (!attr || !attr->extra ||
+ if (!attr ||
(attrtype == BGP_ATTR_ENCAP &&
- (!attr->extra->encap_tunneltype ||
- attr->extra->encap_tunneltype == BGP_ENCAP_TYPE_MPLS)))
+ (!attr->encap_tunneltype ||
+ attr->encap_tunneltype == BGP_ENCAP_TYPE_MPLS)))
return;
switch (attrtype) {
case BGP_ATTR_ENCAP:
attrname = "Tunnel Encap";
- subtlvs = attr->extra->encap_subtlvs;
+ subtlvs = attr->encap_subtlvs;
if (subtlvs == NULL) /* nothing to do */
return;
/*
@@ -3054,7 +2895,7 @@ bgp_packet_mpattr_tea(
#if ENABLE_BGP_VNC
case BGP_ATTR_VNC:
attrname = "VNC";
- subtlvs = attr->extra->vnc_subtlvs;
+ subtlvs = attr->vnc_subtlvs;
if (subtlvs == NULL) /* nothing to do */
return;
attrlenfield = 0; /* no outer T + L */
@@ -3093,7 +2934,7 @@ bgp_packet_mpattr_tea(
if (attrtype == BGP_ATTR_ENCAP) {
/* write outer T+L */
- stream_putw(s, attr->extra->encap_tunneltype);
+ stream_putw(s, attr->encap_tunneltype);
stream_putw(s, attrlenfield - 4);
}
@@ -3282,8 +3123,6 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
/* Aggregator. */
if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
{
- assert (attr->extra);
-
/* Common to BGP_ATTR_AGGREGATOR, regardless of ASN size */
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_AGGREGATOR);
@@ -3292,7 +3131,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
{
/* AS4 capable peer */
stream_putc (s, 8);
- stream_putl (s, attr->extra->aggregator_as);
+ stream_putl (s, attr->aggregator_as);
}
else
{
@@ -3300,7 +3139,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
stream_putc (s, 6);
/* Is ASN representable in 2-bytes? Or must AS_TRANS be used? */
- if ( attr->extra->aggregator_as > 65535 )
+ if ( attr->aggregator_as > 65535 )
{
stream_putw (s, BGP_AS_TRANS);
@@ -3311,9 +3150,9 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
send_as4_aggregator = 1;
}
else
- stream_putw (s, (u_int16_t) attr->extra->aggregator_as);
+ stream_putw (s, (u_int16_t) attr->aggregator_as);
}
- stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
+ stream_put_ipv4 (s, attr->aggregator_addr.s_addr);
}
/* Community attribute. */
@@ -3338,23 +3177,22 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
/*
* Large Community attribute.
*/
- if (attr->extra &&
- CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)
+ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_LARGE_COMMUNITY)
&& (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES)))
{
- if (attr->extra->lcommunity->size * 12 > 255)
+ 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_LARGE_COMMUNITIES);
- stream_putw (s, attr->extra->lcommunity->size * 12);
+ stream_putw (s, attr->lcommunity->size * 12);
}
else
{
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
- stream_putc (s, attr->extra->lcommunity->size * 12);
+ stream_putc (s, attr->lcommunity->size * 12);
}
- stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12);
+ stream_put (s, attr->lcommunity->val, attr->lcommunity->size * 12);
}
/* Route Reflector. */
@@ -3368,7 +3206,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
stream_putc (s, 4);
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
- stream_put_in_addr (s, &attr->extra->originator_id);
+ stream_put_in_addr (s, &attr->originator_id);
else
stream_put_in_addr (s, &from->remote_id);
@@ -3376,16 +3214,16 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
stream_putc (s, BGP_ATTR_CLUSTER_LIST);
- if (attr->extra && attr->extra->cluster)
+ if (attr->cluster)
{
- stream_putc (s, attr->extra->cluster->length + 4);
+ stream_putc (s, attr->cluster->length + 4);
/* If this peer configuration's parent BGP has cluster_id. */
if (bgp->config & BGP_CONFIG_CLUSTER_ID)
stream_put_in_addr (s, &bgp->cluster_id);
else
stream_put_in_addr (s, &bgp->router_id);
- stream_put (s, attr->extra->cluster->list,
- attr->extra->cluster->length);
+ stream_put (s, attr->cluster->list,
+ attr->cluster->length);
}
else
{
@@ -3402,26 +3240,22 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
&& (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
{
- struct attr_extra *attre = attr->extra;
-
- assert (attre);
-
if (peer->sort == BGP_PEER_IBGP
|| peer->sort == BGP_PEER_CONFED)
{
- if (attre->ecommunity->size * 8 > 255)
+ 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_EXT_COMMUNITIES);
- stream_putw (s, attre->ecommunity->size * 8);
+ stream_putw (s, attr->ecommunity->size * 8);
}
else
{
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
- stream_putc (s, attre->ecommunity->size * 8);
+ stream_putc (s, attr->ecommunity->size * 8);
}
- stream_put (s, attre->ecommunity->val, attre->ecommunity->size * 8);
+ stream_put (s, attr->ecommunity->val, attr->ecommunity->size * 8);
}
else
{
@@ -3430,9 +3264,9 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
int ecom_tr_size = 0;
int i;
- for (i = 0; i < attre->ecommunity->size; i++)
+ for (i = 0; i < attr->ecommunity->size; i++)
{
- pnt = attre->ecommunity->val + (i * 8);
+ pnt = attr->ecommunity->val + (i * 8);
tbit = *pnt;
if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE))
@@ -3456,9 +3290,9 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
stream_putc (s, ecom_tr_size * 8);
}
- for (i = 0; i < attre->ecommunity->size; i++)
+ for (i = 0; i < attr->ecommunity->size; i++)
{
- pnt = attre->ecommunity->val + (i * 8);
+ pnt = attr->ecommunity->val + (i * 8);
tbit = *pnt;
if (CHECK_FLAG (tbit, ECOMMUNITY_FLAG_NON_TRANSITIVE))
@@ -3477,8 +3311,7 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
{
u_int32_t label_index;
- assert (attr->extra);
- label_index = attr->extra->label_index;
+ label_index = attr->label_index;
if (label_index != BGP_INVALID_LABEL_INDEX)
{
@@ -3522,8 +3355,6 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
if ( send_as4_aggregator )
{
- assert (attr->extra);
-
/* send AS4_AGGREGATOR, at this place */
/* this section of code moved here in order to ensure the correct
* *ascending* order of attributes
@@ -3531,8 +3362,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_AS4_AGGREGATOR);
stream_putc (s, 8);
- stream_putl (s, attr->extra->aggregator_as);
- stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
+ stream_putl (s, attr->aggregator_as);
+ stream_put_ipv4 (s, attr->aggregator_addr.s_addr);
}
if (((afi == AFI_IP || afi == AFI_IP6) &&
@@ -3549,8 +3380,8 @@ bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
}
/* Unknown transit attribute. */
- if (attr->extra && attr->extra->transit)
- stream_put (s, attr->extra->transit->val, attr->extra->transit->length);
+ if (attr->transit)
+ stream_put (s, attr->transit->val, attr->transit->length);
/* Return total size of attribute. */
return stream_get_endp (s) - cp;
@@ -3700,12 +3531,11 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
/* Aggregator. */
if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
{
- assert (attr->extra);
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_AGGREGATOR);
stream_putc (s, 8);
- stream_putl (s, attr->extra->aggregator_as);
- stream_put_ipv4 (s, attr->extra->aggregator_addr.s_addr);
+ stream_putl (s, attr->aggregator_as);
+ stream_put_ipv4 (s, attr->aggregator_addr.s_addr);
}
/* Community attribute. */
@@ -3727,32 +3557,31 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
}
/* Large Community attribute. */
- if (attr->extra && attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES))
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES))
{
- if (attr->extra->lcommunity->size * 12 > 255)
+ 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_LARGE_COMMUNITIES);
- stream_putw (s, attr->extra->lcommunity->size * 12);
+ stream_putw (s, attr->lcommunity->size * 12);
}
else
{
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_LARGE_COMMUNITIES);
- stream_putc (s, attr->extra->lcommunity->size * 12);
+ stream_putc (s, attr->lcommunity->size * 12);
}
- stream_put (s, attr->extra->lcommunity->val, attr->extra->lcommunity->size * 12);
+ stream_put (s, attr->lcommunity->val, attr->lcommunity->size * 12);
}
/* Add a MP_NLRI attribute to dump the IPv6 next hop */
- if (prefix != NULL && prefix->family == AF_INET6 && attr->extra &&
- (attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL ||
- attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) )
+ if (prefix != NULL && prefix->family == AF_INET6 &&
+ (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL ||
+ attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) )
{
int sizep;
- struct attr_extra *attre = attr->extra;
-
+
stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
stream_putc(s, BGP_ATTR_MP_REACH_NLRI);
sizep = stream_get_endp (s);
@@ -3763,10 +3592,10 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
stream_putc(s, SAFI_UNICAST); /* SAFI */
/* Next hop */
- stream_putc(s, attre->mp_nexthop_len);
- stream_put(s, &attre->mp_nexthop_global, IPV6_MAX_BYTELEN);
- if (attre->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
- stream_put(s, &attre->mp_nexthop_local, IPV6_MAX_BYTELEN);
+ stream_putc(s, attr->mp_nexthop_len);
+ stream_put(s, &attr->mp_nexthop_global, IPV6_MAX_BYTELEN);
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ stream_put(s, &attr->mp_nexthop_local, IPV6_MAX_BYTELEN);
/* SNPA */
stream_putc(s, 0);
@@ -3781,9 +3610,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
/* Prefix SID */
if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_PREFIX_SID))
{
- assert (attr->extra);
-
- if (attr->extra->label_index != BGP_INVALID_LABEL_INDEX)
+ if (attr->label_index != BGP_INVALID_LABEL_INDEX)
{
stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
stream_putc (s, BGP_ATTR_PREFIX_SID);
@@ -3792,7 +3619,7 @@ bgp_dump_routes_attr (struct stream *s, struct attr *attr,
stream_putc (s, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
stream_putc (s, 0); // reserved
stream_putw (s, 0); // flags
- stream_putl (s, attr->extra->label_index);
+ stream_putl (s, attr->label_index);
}
}
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
index f3c1b5e3c..99c6a6b79 100644
--- a/bgpd/bgp_attr.h
+++ b/bgpd/bgp_attr.h
@@ -98,13 +98,35 @@ struct overlay_index
union gw_addr gw_ip;
};
-/* Additional/uncommon BGP attributes.
- * lazily allocated as and when a struct attr
- * requires it.
- */
-struct attr_extra
+/* BGP core attribute structure. */
+struct attr
{
- /* Multi-Protocol Nexthop, AFI IPv6 */
+ /* AS Path structure */
+ struct aspath *aspath;
+
+ /* Community structure */
+ struct community *community;
+
+ /* Reference count of this attribute. */
+ unsigned long refcnt;
+
+ /* Flag of attribute is set or not. */
+ uint64_t flag;
+
+ /* Apart from in6_addr, the remaining static attributes */
+ struct in_addr nexthop;
+ u_int32_t med;
+ u_int32_t local_pref;
+ ifindex_t nh_ifindex;
+
+ /* Path origin attribute */
+ u_char origin;
+
+ /* has the route-map changed any attribute?
+ Used on the peer outbound side. */
+ u_int32_t rmap_change_flags;
+
+ /* Multi-Protocol Nexthop, AFI IPv6 */
struct in6_addr mp_nexthop_global;
struct in6_addr mp_nexthop_local;
@@ -140,6 +162,9 @@ struct attr_extra
/* MP Nexthop preference */
u_char mp_nexthop_prefer_global;
+ /* Static MAC for EVPN */
+ u_char sticky;
+
/* route tag */
route_tag_t tag;
@@ -157,38 +182,9 @@ struct attr_extra
#endif
/* EVPN */
struct overlay_index evpn_overlay;
-};
-/* BGP core attribute structure. */
-struct attr
-{
- /* AS Path structure */
- struct aspath *aspath;
-
- /* Community structure */
- struct community *community;
-
- /* Lazily allocated pointer to extra attributes */
- struct attr_extra *extra;
-
- /* Reference count of this attribute. */
- unsigned long refcnt;
-
- /* Flag of attribute is set or not. */
- uint64_t flag;
-
- /* Apart from in6_addr, the remaining static attributes */
- struct in_addr nexthop;
- u_int32_t med;
- u_int32_t local_pref;
- ifindex_t nh_ifindex;
-
- /* Path origin attribute */
- u_char origin;
-
- /* has the route-map changed any attribute?
- Used on the peer outbound side. */
- u_int32_t rmap_change_flags;
+ /* EVPN MAC Mobility sequence number, if any. */
+ u_int32_t mm_seqnum;
};
/* rmap_change_flags definition */
@@ -220,7 +216,7 @@ struct transit
#define BGP_CLUSTER_LIST_LENGTH(attr) \
(((attr)->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) ? \
- (attr)->extra->cluster->length : 0)
+ (attr)->cluster->length : 0)
typedef enum {
BGP_ATTR_PARSE_PROCEED = 0,
@@ -239,8 +235,6 @@ extern void bgp_attr_finish (void);
extern bgp_attr_parse_ret_t bgp_attr_parse (struct peer *, struct attr *,
bgp_size_t, struct bgp_nlri *,
struct bgp_nlri *);
-extern struct attr_extra *bgp_attr_extra_get (struct attr *);
-extern void bgp_attr_extra_free (struct attr *);
extern void bgp_attr_dup (struct attr *, struct attr *);
extern void bgp_attr_deep_dup (struct attr *, struct attr *);
extern void bgp_attr_deep_free (struct attr *);
@@ -333,4 +327,10 @@ bgp_rmap_nhop_changed(u_int32_t out_rmap_flags, u_int32_t in_rmap_flags)
CHECK_FLAG(in_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED)) ? 1 : 0);
}
+static inline u_int32_t
+mac_mobility_seqnum (struct attr *attr)
+{
+ return (attr) ? attr->mm_seqnum : 0;
+}
+
#endif /* _QUAGGA_BGP_ATTR_H */
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c
index 6970d5a67..aa175bcf5 100644
--- a/bgpd/bgp_attr_evpn.c
+++ b/bgpd/bgp_attr_evpn.c
@@ -33,21 +33,20 @@
#include "bgpd/bgp_attr_evpn.h"
#include "bgpd/bgp_ecommunity.h"
#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac)
{
- struct ecommunity_val routermac_ecom;
-
- if (attr->extra) {
- memset(&routermac_ecom, 0, sizeof(struct ecommunity_val));
- routermac_ecom.val[0] = ECOMMUNITY_ENCODE_EVPN;
- routermac_ecom.val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC;
- memcpy(&routermac_ecom.val[2], routermac->octet, ETHER_ADDR_LEN);
- if (!attr->extra->ecommunity)
- attr->extra->ecommunity = ecommunity_new();
- ecommunity_add_val(attr->extra->ecommunity, &routermac_ecom);
- ecommunity_str (attr->extra->ecommunity);
- }
+ struct ecommunity_val routermac_ecom;
+
+ memset(&routermac_ecom, 0, sizeof(struct ecommunity_val));
+ routermac_ecom.val[0] = ECOMMUNITY_ENCODE_EVPN;
+ routermac_ecom.val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC;
+ memcpy(&routermac_ecom.val[2], routermac->octet, ETHER_ADDR_LEN);
+ if (!attr->ecommunity)
+ attr->ecommunity = ecommunity_new();
+ ecommunity_add_val(attr->ecommunity, &routermac_ecom);
+ ecommunity_str (attr->ecommunity);
}
/* converts to an esi
@@ -57,85 +56,138 @@ void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac)
*/
int str2esi(const char *str, struct eth_segment_id *id)
{
- unsigned int a[ESI_LEN];
- int i;
-
- if (!str)
- return 0;
- if (sscanf (str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x",
- a + 0, a + 1, a + 2, a + 3, a + 4, a + 5,
- a + 6, a + 7, a + 8, a + 9) != ESI_LEN)
- {
- /* error in incoming str length */
- return 0;
- }
- /* valid mac address */
- if (!id)
- return 1;
- for (i = 0; i < ESI_LEN; ++i)
- id->val[i] = a[i] & 0xff;
- return 1;
+ unsigned int a[ESI_LEN];
+ int i;
+
+ if (!str)
+ return 0;
+ if (sscanf (str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x",
+ a + 0, a + 1, a + 2, a + 3, a + 4, a + 5,
+ a + 6, a + 7, a + 8, a + 9) != ESI_LEN)
+ {
+ /* error in incoming str length */
+ return 0;
+ }
+ /* valid mac address */
+ if (!id)
+ return 1;
+ for (i = 0; i < ESI_LEN; ++i)
+ id->val[i] = a[i] & 0xff;
+ return 1;
}
char *esi2str(struct eth_segment_id *id)
{
- char *ptr;
- u_char *val;
+ char *ptr;
+ u_char *val;
- if (!id)
- return NULL;
+ if (!id)
+ return NULL;
- val = id->val;
- ptr = (char *)XMALLOC(MTYPE_TMP, (ESI_LEN * 2 + ESI_LEN - 1 + 1) * sizeof(char));
+ val = id->val;
+ ptr = (char *)XMALLOC(MTYPE_TMP, (ESI_LEN * 2 + ESI_LEN - 1 + 1) * sizeof(char));
- snprintf(ptr, (ESI_LEN * 2 + ESI_LEN - 1 + 1),
- "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
- val[0], val[1], val[2], val[3], val[4],
- val[5], val[6], val[7], val[8], val[9]);
+ snprintf(ptr, (ESI_LEN * 2 + ESI_LEN - 1 + 1),
+ "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ val[0], val[1], val[2], val[3], val[4],
+ val[5], val[6], val[7], val[8], val[9]);
- return ptr;
+ return ptr;
}
char *ecom_mac2str(char *ecom_mac)
{
- char *en;
+ char *en;
- en = ecom_mac;
- en += 2;
- return prefix_mac2str((struct ethaddr *)en, NULL, 0);
+ en = ecom_mac;
+ en += 2;
+
+ return prefix_mac2str((struct ethaddr *)en, NULL, 0);
+}
+
+/*
+ * Fetch and return the sequence number from MAC Mobility extended
+ * community, if present, else 0.
+ */
+u_int32_t
+bgp_attr_mac_mobility_seqnum (struct attr *attr, u_char *sticky)
+{
+ struct ecommunity *ecom;
+ int i;
+ u_char flags = 0;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return 0;
+
+ /* If there is a MAC Mobility extended community, return its
+ * sequence number.
+ * TODO: RFC is silent on handling of multiple MAC mobility extended
+ * communities for the same route. We will bail out upon the first
+ * one.
+ */
+ for (i = 0; i < ecom->size; i++)
+ {
+ u_char *pnt;
+ u_char type, sub_type;
+ u_int32_t seq_num;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (!(type == ECOMMUNITY_ENCODE_EVPN &&
+ sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY))
+ continue;
+ flags = *pnt++;
+
+ if (flags & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
+ *sticky = 1;
+ else
+ *sticky = 0;
+
+ pnt++;
+ seq_num = (*pnt++ << 24);
+ seq_num |= (*pnt++ << 16);
+ seq_num |= (*pnt++ << 8);
+ seq_num |= (*pnt++);
+
+ return seq_num;
+ }
+
+ return 0;
}
/* dst prefix must be AF_INET or AF_INET6 prefix, to forge EVPN prefix */
extern int
bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag, struct prefix *dst)
{
- struct evpn_addr *p_evpn_p;
- struct prefix p2;
- struct prefix *src = &p2;
-
- if (!dst || dst->family == 0)
- return -1;
- /* store initial prefix in src */
- prefix_copy(src, dst);
- memset(dst, 0, sizeof(struct prefix));
- p_evpn_p = &(dst->u.prefix_evpn);
- dst->family = AF_ETHERNET;
- p_evpn_p->route_type = evpn_type;
- if (evpn_type == EVPN_IP_PREFIX) {
- p_evpn_p->eth_tag = eth_tag;
- p_evpn_p->ip_prefix_length = p2.prefixlen;
- if (src->family == AF_INET) {
+ struct evpn_addr *p_evpn_p;
+ struct prefix p2;
+ struct prefix *src = &p2;
+
+ if (!dst || dst->family == 0)
+ return -1;
+ /* store initial prefix in src */
+ prefix_copy(src, dst);
+ memset(dst, 0, sizeof(struct prefix));
+ p_evpn_p = &(dst->u.prefix_evpn);
+ dst->family = AF_ETHERNET;
+ p_evpn_p->route_type = evpn_type;
+ if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+ p_evpn_p->eth_tag = eth_tag;
+ p_evpn_p->ip_prefix_length = p2.prefixlen;
+ if (src->family == AF_INET) {
SET_IPADDR_V4 (&p_evpn_p->ip);
- memcpy(&p_evpn_p->ip.ipaddr_v4, &src->u.prefix4,
- sizeof(struct in_addr));
- dst->prefixlen = (u_char) PREFIX_LEN_ROUTE_TYPE_5_IPV4;
- } else {
+ memcpy(&p_evpn_p->ip.ipaddr_v4, &src->u.prefix4,
+ sizeof(struct in_addr));
+ dst->prefixlen = (u_char) PREFIX_LEN_ROUTE_TYPE_5_IPV4;
+ } else {
SET_IPADDR_V6 (&p_evpn_p->ip);
- memcpy(&p_evpn_p->ip.ipaddr_v6, &src->u.prefix6,
- sizeof(struct in6_addr));
- dst->prefixlen = (u_char) PREFIX_LEN_ROUTE_TYPE_5_IPV6;
- }
- } else
- return -1;
- return 0;
+ memcpy(&p_evpn_p->ip.ipaddr_v6, &src->u.prefix6,
+ sizeof(struct in6_addr));
+ dst->prefixlen = (u_char) PREFIX_LEN_ROUTE_TYPE_5_IPV6;
+ }
+ } else
+ return -1;
+ return 0;
}
diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h
index 3a93f6ae6..26650ef8b 100644
--- a/bgpd/bgp_attr_evpn.h
+++ b/bgpd/bgp_attr_evpn.h
@@ -22,31 +22,37 @@
#define _QUAGGA_BGP_ATTR_EVPN_H
/* value of first byte of ESI */
-#define ESI_TYPE_ARBITRARY 0 /* */
-#define ESI_TYPE_LACP 1 /* <> */
-#define ESI_TYPE_BRIDGE 2 /* <Root bridge Mac-6B>:<Root Br Priority-2B>:00 */
-#define ESI_TYPE_MAC 3 /* <Syst Mac Add-6B>:<Local Discriminator Value-3B> */
-#define ESI_TYPE_ROUTER 4 /* <RouterId-4B>:<Local Discriminator Value-4B> */
-#define ESI_TYPE_AS 5 /* <AS-4B>:<Local Discriminator Value-4B> */
+#define ESI_TYPE_ARBITRARY 0 /* */
+#define ESI_TYPE_LACP 1 /* <> */
+#define ESI_TYPE_BRIDGE 2 /* <Root bridge Mac-6B>:<Root Br Priority-2B>:00 */
+#define ESI_TYPE_MAC 3 /* <Syst Mac Add-6B>:<Local Discriminator Value-3B> */
+#define ESI_TYPE_ROUTER 4 /* <RouterId-4B>:<Local Discriminator Value-4B> */
+#define ESI_TYPE_AS 5 /* <AS-4B>:<Local Discriminator Value-4B> */
+
+
#define MAX_ESI {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}
#define ESI_LEN 10
#define MAX_ET 0xffffffff
+
u_long eth_tag_id;
struct attr;
-struct eth_segment_id {
- u_char val[ESI_LEN];
+struct eth_segment_id
+{
+ u_char val[ESI_LEN];
};
-union gw_addr {
- struct in_addr ipv4;
- struct in6_addr ipv6;
+union gw_addr
+{
+ struct in_addr ipv4;
+ struct in6_addr ipv6;
};
-struct bgp_route_evpn {
- struct eth_segment_id eth_s_id;
- union gw_addr gw_ip;
+struct bgp_route_evpn
+{
+ struct eth_segment_id eth_s_id;
+ union gw_addr gw_ip;
};
extern int str2esi(const char *str, struct eth_segment_id *id);
@@ -55,5 +61,9 @@ extern char *ecom_mac2str(char *ecom_mac);
extern void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac);
extern int bgp_build_evpn_prefix(int type, uint32_t eth_tag,
- struct prefix *dst);
-#endif /* _QUAGGA_BGP_ATTR_EVPN_H */
+ struct prefix *dst);
+
+extern u_int32_t
+bgp_attr_mac_mobility_seqnum (struct attr *attr, u_char *sticky);
+
+#endif /* _QUAGGA_BGP_ATTR_EVPN_H */
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
index e45b5ab4e..f9e67d96c 100644
--- a/bgpd/bgp_debug.c
+++ b/bgpd/bgp_debug.c
@@ -39,6 +39,9 @@
#include "bgpd/bgp_community.h"
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_evpn.h"
unsigned long conf_bgp_debug_as4;
unsigned long conf_bgp_debug_neighbor_events;
@@ -384,6 +387,8 @@ bgp_debug_peer_updout_enabled(char *host)
int
bgp_dump_attr (struct attr *attr, char *buf, size_t size)
{
+ char addrbuf[BUFSIZ];
+
if (! attr)
return 0;
@@ -394,66 +399,68 @@ bgp_dump_attr (struct attr *attr, char *buf, size_t size)
snprintf (buf + strlen (buf), size - strlen (buf), ", origin %s",
bgp_origin_str[attr->origin]);
- if (attr->extra)
- {
- char addrbuf[BUFSIZ];
+ /* Add MP case. */
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL
+ || attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ snprintf (buf + strlen (buf), size - strlen (buf), ", mp_nexthop %s",
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global,
+ addrbuf, BUFSIZ));
- /* Add MP case. */
- if (attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL
- || attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
- snprintf (buf + strlen (buf), size - strlen (buf), ", mp_nexthop %s",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
- addrbuf, BUFSIZ));
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ snprintf (buf + strlen (buf), size - strlen (buf), "(%s)",
+ inet_ntop (AF_INET6, &attr->mp_nexthop_local,
+ addrbuf, BUFSIZ));
- if (attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
- snprintf (buf + strlen (buf), size - strlen (buf), "(%s)",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
- addrbuf, BUFSIZ));
- }
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4)
+ snprintf (buf, size, "nexthop %s", inet_ntoa (attr->nexthop));
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
snprintf (buf + strlen (buf), size - strlen (buf), ", localpref %u",
attr->local_pref);
- if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)))
+ if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC)))
snprintf (buf + strlen (buf), size - strlen (buf), ", metric %u",
attr->med);
- if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)))
+ if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)))
snprintf (buf + strlen (buf), size - strlen (buf), ", community %s",
community_str (attr->community));
+ if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
+ snprintf (buf + strlen (buf), size - strlen (buf), ", extcommunity %s",
+ ecommunity_str (attr->ecommunity));
+
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE)))
snprintf (buf + strlen (buf), size - strlen (buf), ", atomic-aggregate");
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR)))
snprintf (buf + strlen (buf), size - strlen (buf), ", aggregated by %u %s",
- attr->extra->aggregator_as,
- inet_ntoa (attr->extra->aggregator_addr));
+ attr->aggregator_as,
+ inet_ntoa (attr->aggregator_addr));
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)))
snprintf (buf + strlen (buf), size - strlen (buf), ", originator %s",
- inet_ntoa (attr->extra->originator_id));
+ inet_ntoa (attr->originator_id));
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST)))
{
int i;
snprintf (buf + strlen (buf), size - strlen (buf), ", clusterlist");
- for (i = 0; i < attr->extra->cluster->length / 4; i++)
+ for (i = 0; i < attr->cluster->length / 4; i++)
snprintf (buf + strlen (buf), size - strlen (buf), " %s",
- inet_ntoa (attr->extra->cluster->list[i]));
+ inet_ntoa (attr->cluster->list[i]));
}
- if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)))
+ if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)))
snprintf (buf + strlen (buf), size - strlen (buf), ", path %s",
aspath_print (attr->aspath));
if (CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_PREFIX_SID)))
{
- if (attr->extra->label_index != BGP_INVALID_LABEL_INDEX)
+ if (attr->label_index != BGP_INVALID_LABEL_INDEX)
snprintf (buf + strlen (buf), size - strlen (buf), ", label-index %u",
- attr->extra->label_index);
+ attr->label_index);
}
if (strlen (buf) > 1)
@@ -2142,12 +2149,14 @@ bgp_debug_zebra (struct prefix *p)
}
const char *
-bgp_debug_rdpfxpath2str (struct prefix_rd *prd, union prefixconstptr pu,
- int addpath_valid, u_int32_t addpath_id,
- char *str, int size)
+bgp_debug_rdpfxpath2str (afi_t afi, safi_t safi,
+ struct prefix_rd *prd, union prefixconstptr pu,
+ mpls_label_t *label, int addpath_valid,
+ u_int32_t addpath_id, char *str, int size)
{
char rd_buf[RD_ADDRSTRLEN];
char pfx_buf[PREFIX_STRLEN];
+ char tag_buf[30];
/* ' with addpath ID ' 17
* max strlen of uint32 + 10
* +/- (just in case) + 1
@@ -2163,13 +2172,24 @@ bgp_debug_rdpfxpath2str (struct prefix_rd *prd, union prefixconstptr pu,
if (addpath_valid)
snprintf(pathid_buf, sizeof(pathid_buf), " with addpath ID %u", addpath_id);
+ tag_buf[0] = '\0';
+ if (bgp_labeled_safi (safi) && label)
+ {
+ u_int32_t label_value;
+
+ label_value = decode_label (label);
+ sprintf (tag_buf, " label %u", label_value);
+ }
+
if (prd)
- snprintf (str, size, "RD %s %s%s",
+ snprintf (str, size, "RD %s %s%s%s",
prefix_rd2str(prd, rd_buf, sizeof (rd_buf)),
- prefix2str (pu, pfx_buf, sizeof (pfx_buf)), pathid_buf);
+ prefix2str (pu, pfx_buf, sizeof (pfx_buf)),
+ tag_buf, pathid_buf);
else
- snprintf (str, size, "%s%s",
- prefix2str (pu, pfx_buf, sizeof (pfx_buf)), pathid_buf);
+ snprintf (str, size, "%s%s%s",
+ prefix2str (pu, pfx_buf, sizeof (pfx_buf)),
+ tag_buf, pathid_buf);
return str;
}
diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h
index 5ec2eaa7c..11ab4dbd6 100644
--- a/bgpd/bgp_debug.h
+++ b/bgpd/bgp_debug.h
@@ -153,7 +153,8 @@ extern int bgp_debug_bestpath(struct prefix *p);
extern int bgp_debug_zebra(struct prefix *p);
extern int bgp_debug_count(void);
-extern const char *bgp_debug_rdpfxpath2str (struct prefix_rd *, union prefixconstptr,
+extern const char *bgp_debug_rdpfxpath2str (afi_t, safi_t, struct prefix_rd *,
+ union prefixconstptr, mpls_label_t *,
int, u_int32_t, char *, int);
const char *bgp_notify_admin_message(char *buf, size_t bufsz, u_char *data, size_t datalen);
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
index 0555d1bbe..934921434 100644
--- a/bgpd/bgp_ecommunity.c
+++ b/bgpd/bgp_ecommunity.c
@@ -304,6 +304,61 @@ enum ecommunity_token
ecommunity_token_val,
};
+/*
+ * Encode BGP extended community from passed values. Supports types
+ * defined in RFC 4360 and well-known sub-types.
+ */
+static int
+ecommunity_encode (u_char type, u_char sub_type, int trans,
+ as_t as, struct in_addr ip, u_int32_t val,
+ struct ecommunity_val *eval)
+{
+ assert (eval);
+ if (type == ECOMMUNITY_ENCODE_AS)
+ {
+ if (as > BGP_AS_MAX)
+ return -1;
+ }
+ else if (type == ECOMMUNITY_ENCODE_IP
+ || type == ECOMMUNITY_ENCODE_AS4)
+ {
+ if (val > UINT16_MAX)
+ return -1;
+ }
+
+ /* Fill in the values. */
+ eval->val[0] = type;
+ if (!trans)
+ eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
+ eval->val[1] = sub_type;
+ if (type == ECOMMUNITY_ENCODE_AS)
+ {
+ eval->val[2] = (as >> 8) & 0xff;
+ eval->val[3] = as & 0xff;
+ eval->val[4] = (val >> 24) & 0xff;
+ eval->val[5] = (val >> 16) & 0xff;
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+ }
+ else if (type == ECOMMUNITY_ENCODE_IP)
+ {
+ memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+ }
+ else
+ {
+ eval->val[2] = (as >> 24) & 0xff;
+ eval->val[3] = (as >> 16) & 0xff;
+ eval->val[4] = (as >> 8) & 0xff;
+ eval->val[5] = as & 0xff;
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+ }
+
+ return 0;
+}
+
/* Get next Extended Communities token from the string. */
static const char *
ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
@@ -318,6 +373,7 @@ ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
struct in_addr ip;
as_t as = 0;
u_int32_t val = 0;
+ u_char ecomm_type;
char buf[INET_ADDRSTRLEN + 1];
/* Skip white space. */
@@ -452,44 +508,15 @@ ecommunity_gettoken (const char *str, struct ecommunity_val *eval,
if (!digit || !separator)
goto error;
- /* Encode result into routing distinguisher. */
+ /* Encode result into extended community. */
if (dot)
- {
- if (val > UINT16_MAX)
- goto error;
-
- eval->val[0] = ECOMMUNITY_ENCODE_IP;
- eval->val[1] = 0;
- memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
- eval->val[6] = (val >> 8) & 0xff;
- eval->val[7] = val & 0xff;
- }
+ ecomm_type = ECOMMUNITY_ENCODE_IP;
else if (as > BGP_AS_MAX)
- {
- if (val > UINT16_MAX)
- goto error;
-
- eval->val[0] = ECOMMUNITY_ENCODE_AS4;
- eval->val[1] = 0;
- eval->val[2] = (as >>24) & 0xff;
- eval->val[3] = (as >>16) & 0xff;
- eval->val[4] = (as >>8) & 0xff;
- eval->val[5] = as & 0xff;
- eval->val[6] = (val >> 8) & 0xff;
- eval->val[7] = val & 0xff;
- }
+ ecomm_type = ECOMMUNITY_ENCODE_AS4;
else
- {
- eval->val[0] = ECOMMUNITY_ENCODE_AS;
- eval->val[1] = 0;
-
- eval->val[2] = (as >>8) & 0xff;
- eval->val[3] = as & 0xff;
- eval->val[4] = (val >>24) & 0xff;
- eval->val[5] = (val >>16) & 0xff;
- eval->val[6] = (val >>8) & 0xff;
- eval->val[7] = val & 0xff;
- }
+ ecomm_type = ECOMMUNITY_ENCODE_AS;
+ if (ecommunity_encode (ecomm_type, 0, 1, as, ip, val, eval))
+ goto error;
*token = ecommunity_token_val;
return p;
@@ -581,6 +608,81 @@ ecommunity_str2com (const char *str, int type, int keyword_included)
return ecom;
}
+static int
+ecommunity_rt_soo_str (char *buf, u_int8_t *pnt, int type,
+ int sub_type, int format)
+{
+ int len = 0;
+ const char *prefix;
+
+ /* For parse Extended Community attribute tupple. */
+ struct ecommunity_as
+ {
+ as_t as;
+ u_int32_t val;
+ } eas;
+
+ struct ecommunity_ip
+ {
+ struct in_addr ip;
+ u_int16_t val;
+ } eip;
+
+
+ /* Determine prefix for string, if any. */
+ switch (format)
+ {
+ case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
+ prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
+ break;
+ case ECOMMUNITY_FORMAT_DISPLAY:
+ prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
+ break;
+ case ECOMMUNITY_FORMAT_ROUTE_MAP:
+ prefix = "";
+ break;
+ default:
+ prefix = "";
+ break;
+ }
+
+ /* Put string into buffer. */
+ if (type == ECOMMUNITY_ENCODE_AS4)
+ {
+ eas.as = (*pnt++ << 24);
+ eas.as |= (*pnt++ << 16);
+ eas.as |= (*pnt++ << 8);
+ eas.as |= (*pnt++);
+ eas.val = (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ len = sprintf (buf, "%s%u:%u", prefix, eas.as, eas.val);
+ }
+ else if (type == ECOMMUNITY_ENCODE_AS)
+ {
+ eas.as = (*pnt++ << 8);
+ eas.as |= (*pnt++);
+
+ eas.val = (*pnt++ << 24);
+ eas.val |= (*pnt++ << 16);
+ eas.val |= (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ len = sprintf (buf, "%s%u:%u", prefix, eas.as, eas.val);
+ }
+ else if (type == ECOMMUNITY_ENCODE_IP)
+ {
+ memcpy (&eip.ip, pnt, 4);
+ pnt += 4;
+ eip.val = (*pnt++ << 8);
+ eip.val |= (*pnt++);
+
+ len = sprintf (buf, "%s%s:%u", prefix, inet_ntoa (eip.ip), eip.val);
+ }
+
+ return len;
+}
+
/* Convert extended community attribute to string.
Due to historical reason of industry standard implementation, there
@@ -610,29 +712,15 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
{
int i;
u_int8_t *pnt;
- int encode = 0;
int type = 0;
+ int sub_type = 0;
#define ECOMMUNITY_STR_DEFAULT_LEN 27
int str_size;
int str_pnt;
char *str_buf;
- const char *prefix;
int len = 0;
int first = 1;
- /* For parse Extended Community attribute tupple. */
- struct ecommunity_as
- {
- as_t as;
- u_int32_t val;
- } eas;
-
- struct ecommunity_ip
- {
- struct in_addr ip;
- u_int16_t val;
- } eip;
-
if (ecom->size == 0)
{
str_buf = XMALLOC (MTYPE_ECOMMUNITY_STR, 1);
@@ -648,6 +736,8 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
for (i = 0; i < ecom->size; i++)
{
+ int unk_ecom = 0;
+
/* Make it sure size is enough. */
while (str_pnt + ECOMMUNITY_STR_DEFAULT_LEN >= str_size)
{
@@ -662,39 +752,39 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
pnt = ecom->val + (i * 8);
/* High-order octet of type. */
- encode = *pnt++;
+ type = *pnt++;
- switch (encode)
+ if (type == ECOMMUNITY_ENCODE_AS ||
+ type == ECOMMUNITY_ENCODE_IP ||
+ type == ECOMMUNITY_ENCODE_AS4)
{
- case ECOMMUNITY_ENCODE_AS:
- case ECOMMUNITY_ENCODE_IP:
- case ECOMMUNITY_ENCODE_AS4:
- break;
-
- case ECOMMUNITY_ENCODE_OPAQUE:
- if(filter == ECOMMUNITY_ROUTE_TARGET)
- {
- continue;
- }
+ /* Low-order octet of type. */
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET &&
+ sub_type != ECOMMUNITY_SITE_ORIGIN)
+ unk_ecom = 1;
+ else
+ len = ecommunity_rt_soo_str (str_buf + str_pnt, pnt, type,
+ sub_type, format);
+ }
+ else if (type == ECOMMUNITY_ENCODE_OPAQUE)
+ {
+ if (filter == ECOMMUNITY_ROUTE_TARGET)
+ continue;
if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP)
{
uint16_t tunneltype;
memcpy (&tunneltype, pnt + 5, 2);
tunneltype = ntohs(tunneltype);
len = sprintf (str_buf + str_pnt, "ET:%d", tunneltype);
- str_pnt += len;
- first = 0;
- continue;
- }
- len = sprintf (str_buf + str_pnt, "?");
- str_pnt += len;
- first = 0;
- continue;
- case ECOMMUNITY_ENCODE_EVPN:
- if(filter == ECOMMUNITY_ROUTE_TARGET)
- {
- continue;
}
+ else
+ unk_ecom = 1;
+ }
+ else if (type == ECOMMUNITY_ENCODE_EVPN)
+ {
+ if (filter == ECOMMUNITY_ROUTE_TARGET)
+ continue;
if (*pnt == ECOMMUNITY_SITE_ORIGIN)
{
char macaddr[6];
@@ -703,91 +793,32 @@ ecommunity_ecom2str (struct ecommunity *ecom, int format, int filter)
len = sprintf(str_buf + str_pnt, "EVPN:%02x:%02x:%02x:%02x:%02x:%02x",
macaddr[0], macaddr[1], macaddr[2],
macaddr[3], macaddr[4], macaddr[5]);
- str_pnt += len;
- first = 0;
- continue;
}
- len = sprintf (str_buf + str_pnt, "?");
- str_pnt += len;
- first = 0;
- continue;
- default:
- len = sprintf (str_buf + str_pnt, "?");
- str_pnt += len;
- first = 0;
- continue;
+ else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY)
+ {
+ u_int32_t seqnum;
+ u_char flags = *++pnt;
+
+ memcpy (&seqnum, pnt + 2, 4);
+ seqnum = ntohl(seqnum);
+ if (flags & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
+ len = sprintf (str_buf + str_pnt, "MM:%u, sticky MAC", seqnum);
+ else
+ len = sprintf (str_buf + str_pnt, "MM:%u", seqnum);
+ }
+ else
+ unk_ecom = 1;
}
+ else
+ unk_ecom = 1;
- /* Low-order octet of type. */
- type = *pnt++;
- if (type != ECOMMUNITY_ROUTE_TARGET && type != ECOMMUNITY_SITE_ORIGIN)
- {
- len = sprintf (str_buf + str_pnt, "?");
- str_pnt += len;
- first = 0;
- continue;
- }
-
- switch (format)
- {
- case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
- prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
- break;
- case ECOMMUNITY_FORMAT_DISPLAY:
- prefix = (type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
- break;
- case ECOMMUNITY_FORMAT_ROUTE_MAP:
- prefix = "";
- break;
- default:
- prefix = "";
- break;
- }
+ if (unk_ecom)
+ len = sprintf (str_buf + str_pnt, "?");
- /* Put string into buffer. */
- if (encode == ECOMMUNITY_ENCODE_AS4)
- {
- eas.as = (*pnt++ << 24);
- eas.as |= (*pnt++ << 16);
- eas.as |= (*pnt++ << 8);
- eas.as |= (*pnt++);
-
- eas.val = (*pnt++ << 8);
- eas.val |= (*pnt++);
-
- len = sprintf( str_buf + str_pnt, "%s%u:%u", prefix,
- eas.as, eas.val );
- str_pnt += len;
- first = 0;
- }
- if (encode == ECOMMUNITY_ENCODE_AS)
- {
- eas.as = (*pnt++ << 8);
- eas.as |= (*pnt++);
-
- eas.val = (*pnt++ << 24);
- eas.val |= (*pnt++ << 16);
- eas.val |= (*pnt++ << 8);
- eas.val |= (*pnt++);
-
- len = sprintf (str_buf + str_pnt, "%s%u:%u", prefix,
- eas.as, eas.val);
- str_pnt += len;
- first = 0;
- }
- else if (encode == ECOMMUNITY_ENCODE_IP)
- {
- memcpy (&eip.ip, pnt, 4);
- pnt += 4;
- eip.val = (*pnt++ << 8);
- eip.val |= (*pnt++);
-
- len = sprintf (str_buf + str_pnt, "%s%s:%u", prefix,
- inet_ntoa (eip.ip), eip.val);
- str_pnt += len;
- first = 0;
- }
+ str_pnt += len;
+ first = 0;
}
+
return str_buf;
}
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
index d6006e81d..9281c0d99 100644
--- a/bgpd/bgp_ecommunity.h
+++ b/bgpd/bgp_ecommunity.h
@@ -32,12 +32,15 @@
#define ECOMMUNITY_ROUTE_TARGET 0x02
#define ECOMMUNITY_SITE_ORIGIN 0x03
+/* Low-order octet of the Extended Communities type field for EVPN types */
#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00
#define ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL 0x01
#define ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT 0x02
#define ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC 0x03
#define ECOMMUNITY_EVPN_SUBTYPE_DEF_GW 0x0d
+#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY 0x01
+
/* Low-order octet of the Extended Communities type field for OPAQUE types */
#define ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP 0x0c
@@ -76,6 +79,54 @@ struct ecommunity_val
#define ecom_length(X) ((X)->size * ECOMMUNITY_SIZE)
+/*
+ * Encode BGP Route Target AS:nn.
+ */
+static inline void
+encode_route_target_as (as_t as, u_int32_t val,
+ struct ecommunity_val *eval)
+{
+ eval->val[0] = ECOMMUNITY_ENCODE_AS;
+ eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
+ eval->val[2] = (as >> 8) & 0xff;
+ eval->val[3] = as & 0xff;
+ eval->val[4] = (val >> 24) & 0xff;
+ eval->val[5] = (val >> 16) & 0xff;
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+}
+
+/*
+ * Encode BGP Route Target IP:nn.
+ */
+static inline void
+encode_route_target_ip (struct in_addr ip, u_int16_t val,
+ struct ecommunity_val *eval)
+{
+ eval->val[0] = ECOMMUNITY_ENCODE_IP;
+ eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
+ memcpy (&eval->val[2], &ip, sizeof (struct in_addr));
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+}
+
+/*
+ * Encode BGP Route Target AS4:nn.
+ */
+static inline void
+encode_route_target_as4 (as_t as, u_int16_t val,
+ struct ecommunity_val *eval)
+{
+ eval->val[0] = ECOMMUNITY_ENCODE_AS4;
+ eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
+ eval->val[2] = (as >> 24) & 0xff;
+ eval->val[3] = (as >> 16) & 0xff;
+ eval->val[4] = (as >> 8) & 0xff;
+ eval->val[5] = as & 0xff;
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+}
+
extern void ecommunity_init (void);
extern void ecommunity_finish (void);
extern void ecommunity_free (struct ecommunity **);
diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c
index eee2cb72c..89194980d 100644
--- a/bgpd/bgp_encap_tlv.c
+++ b/bgpd/bgp_encap_tlv.c
@@ -238,7 +238,7 @@ subtlv_encode_remote_endpoint(
if (last) {\
last->next = new;\
} else {\
- extra->encap_subtlvs = new;\
+ attr->encap_subtlvs = new;\
}\
last = new;\
}\
@@ -249,13 +249,12 @@ bgp_encap_type_l2tpv3overip_to_tlv(
struct bgp_encap_type_l2tpv3_over_ip *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
struct bgp_attr_encap_subtlv *last;
/* advance to last subtlv */
- for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_L2TPV3_OVER_IP;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_L2TPV3_OVER_IP;
assert(CHECK_FLAG(bet->valid_subtlvs, BGP_TEA_SUBTLV_ENCAP));
@@ -270,13 +269,12 @@ bgp_encap_type_gre_to_tlv(
struct bgp_encap_type_gre *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
struct bgp_attr_encap_subtlv *last;
/* advance to last subtlv */
- for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_GRE;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_GRE;
ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_gre, st_encap);
ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type, st_proto);
@@ -289,13 +287,12 @@ bgp_encap_type_ip_in_ip_to_tlv(
struct bgp_encap_type_ip_in_ip *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
struct bgp_attr_encap_subtlv *last;
/* advance to last subtlv */
- for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_IP_IN_IP;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_IP_IN_IP;
ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type, st_proto);
ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color);
@@ -307,13 +304,12 @@ bgp_encap_type_transmit_tunnel_endpoint(
struct bgp_encap_type_transmit_tunnel_endpoint *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
struct bgp_attr_encap_subtlv *last;
/* advance to last subtlv */
- for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT;
/* no subtlvs for this type */
}
@@ -323,13 +319,12 @@ bgp_encap_type_ipsec_in_tunnel_mode_to_tlv(
struct bgp_encap_type_ipsec_in_tunnel_mode *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
struct bgp_attr_encap_subtlv *last;
/* advance to last subtlv */
- for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE;
ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta);
}
@@ -339,13 +334,12 @@ bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
struct bgp_attr_encap_subtlv *last;
/* advance to last subtlv */
- for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE;
ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta);
}
@@ -355,13 +349,12 @@ bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
struct bgp_attr_encap_subtlv *last;
/* advance to last subtlv */
- for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE;
ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta);
}
@@ -371,13 +364,12 @@ bgp_encap_type_pbb_to_tlv(
struct bgp_encap_type_pbb *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
struct bgp_attr_encap_subtlv *last;
/* advance to last subtlv */
- for (last = extra->encap_subtlvs; last && last->next; last = last->next);
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_PBB;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_PBB;
assert(CHECK_FLAG(bet->valid_subtlvs, BGP_TEA_SUBTLV_ENCAP));
ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_pbb, st_encap);
@@ -388,16 +380,15 @@ bgp_encap_type_vxlan_to_tlv(
struct bgp_encap_type_vxlan *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
struct bgp_attr_encap_subtlv *tlv;
uint32_t vnid;
- extra->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN;
if(bet == NULL ||!bet->vnid)
return;
- if(extra->encap_subtlvs)
- XFREE(MTYPE_ENCAP_TLV, extra->encap_subtlvs);
+ if(attr->encap_subtlvs)
+ XFREE(MTYPE_ENCAP_TLV, attr->encap_subtlvs);
tlv = XCALLOC (MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv)-1+12);
tlv->type = 1; /* encapsulation type */
tlv->length = 12;
@@ -411,7 +402,7 @@ bgp_encap_type_vxlan_to_tlv(
char *ptr = (char *)&tlv->value + 4;
memcpy( ptr, bet->mac_address, 6);
}
- extra->encap_subtlvs = tlv;
+ attr->encap_subtlvs = tlv;
return;
}
@@ -420,9 +411,7 @@ bgp_encap_type_nvgre_to_tlv(
struct bgp_encap_type_nvgre *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
-
- extra->encap_tunneltype = BGP_ENCAP_TYPE_NVGRE;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_NVGRE;
}
void
@@ -438,9 +427,7 @@ bgp_encap_type_mpls_in_gre_to_tlv(
struct bgp_encap_type_mpls_in_gre *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
-
- extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_GRE;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_GRE;
}
void
@@ -448,9 +435,8 @@ bgp_encap_type_vxlan_gpe_to_tlv(
struct bgp_encap_type_vxlan_gpe *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN_GPE;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN_GPE;
}
void
@@ -458,9 +444,8 @@ bgp_encap_type_mpls_in_udp_to_tlv(
struct bgp_encap_type_mpls_in_udp *bet, /* input structure */
struct attr *attr)
{
- struct attr_extra *extra = bgp_attr_extra_get(attr);
- extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_UDP;
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_UDP;
}
diff --git a/bgpd/bgp_encap_types.h b/bgpd/bgp_encap_types.h
index ffeb2f61e..03664fe7a 100644
--- a/bgpd/bgp_encap_types.h
+++ b/bgpd/bgp_encap_types.h
@@ -19,6 +19,8 @@
#ifndef _QUAGGA_BGP_ENCAP_TYPES_H
#define _QUAGGA_BGP_ENCAP_TYPES_H
+#include "bgpd/bgp_ecommunity.h"
+
/* from http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#tunnel-types */
typedef enum {
BGP_ENCAP_TYPE_RESERVED=0,
@@ -213,4 +215,15 @@ struct bgp_encap_type_pbb {
struct bgp_tea_subtlv_encap_pbb st_encap;
};
+static inline void
+encode_encap_extcomm (bgp_encap_types tnl_type,
+ struct ecommunity_val *eval)
+{
+ memset (eval, 0, sizeof (*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_OPAQUE;
+ eval->val[1] = ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP;
+ eval->val[6] = ((tnl_type) >> 8) & 0xff;
+ eval->val[7] = (tnl_type) & 0xff;
+}
+
#endif /* _QUAGGA_BGP_ENCAP_TYPES_H */
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index 34a3315c0..46e97cc50 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -1,7 +1,8 @@
/* Ethernet-VPN Packet and vty Processing File
* Copyright (C) 2016 6WIND
+ * Copyright (C) 2017 Cumulus Networks, Inc.
*
- * This file is part of FRRouting.
+ * This file is part of FRR.
*
* FRRouting is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -26,6 +27,10 @@
#include "log.h"
#include "memory.h"
#include "stream.h"
+#include "hash.h"
+#include "jhash.h"
+#include "bitfield.h"
+#include "zclient.h"
#include "bgpd/bgp_attr_evpn.h"
#include "bgpd/bgpd.h"
@@ -35,193 +40,2817 @@
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_label.h"
#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_encap_types.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_aspath.h"
+
+/*
+ * Definitions and external declarations.
+ */
+extern struct zclient *zclient;
+
+DEFINE_QOBJ_TYPE(bgpevpn)
+
+
+/*
+ * Static function declarations
+ */
+static void
+delete_evpn_route_entry (struct bgp *bgp, struct bgpevpn *vpn,
+ afi_t afi, safi_t safi, struct bgp_node *rn,
+ struct bgp_info **ri);
+static int
+delete_all_vni_routes (struct bgp *bgp, struct bgpevpn *vpn);
+
+/*
+ * Private functions.
+ */
+
+/*
+ * Make vni hash key.
+ */
+static unsigned int
+vni_hash_key_make(void *p)
+{
+ struct bgpevpn *vpn = p;
+ return (jhash_1word(vpn->vni, 0));
+}
+
+/*
+ * Comparison function for vni hash
+ */
+static int
+vni_hash_cmp (const void *p1, const void *p2)
+{
+ const struct bgpevpn *vpn1 = p1;
+ const struct bgpevpn *vpn2 = p2;
+
+ if (!vpn1 && !vpn2)
+ return 1;
+ if (!vpn1 || !vpn2)
+ return 0;
+ return(vpn1->vni == vpn2->vni);
+}
+
+/*
+ * Make import route target hash key.
+ */
+static unsigned int
+import_rt_hash_key_make (void *p)
+{
+ struct irt_node *irt = p;
+ char *pnt = irt->rt.val;
+ unsigned int key = 0;
+ int c=0;
+
+ key += pnt[c];
+ key += pnt[c + 1];
+ key += pnt[c + 2];
+ key += pnt[c + 3];
+ key += pnt[c + 4];
+ key += pnt[c + 5];
+ key += pnt[c + 6];
+ key += pnt[c + 7];
+
+ return (key);
+}
+
+/*
+ * Comparison function for import rt hash
+ */
+static int
+import_rt_hash_cmp (const void *p1, const void *p2)
+{
+ const struct irt_node *irt1 = p1;
+ const struct irt_node *irt2 = p2;
+
+ if (irt1 == NULL && irt2 == NULL)
+ return 1;
+
+ if (irt1 == NULL || irt2 == NULL)
+ return 0;
+
+ return(memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0);
+}
+
+/*
+ * Create a new import_rt
+ */
+static struct irt_node *
+import_rt_new (struct bgp *bgp, struct ecommunity_val *rt)
+{
+ struct irt_node *irt;
+
+ if (!bgp)
+ return NULL;
+
+ irt = XCALLOC (MTYPE_BGP_EVPN_IMPORT_RT, sizeof (struct irt_node));
+ if (!irt)
+ return NULL;
+
+ irt->rt = *rt;
+ irt->vnis = list_new ();
+
+ /* Add to hash */
+ if (!hash_get(bgp->import_rt_hash, irt, hash_alloc_intern))
+ {
+ XFREE(MTYPE_BGP_EVPN_IMPORT_RT, irt);
+ return NULL;
+ }
+
+ return irt;
+}
+
+/*
+ * Free the import rt node
+ */
+static void
+import_rt_free (struct bgp *bgp, struct irt_node *irt)
+{
+ hash_release(bgp->import_rt_hash, irt);
+ XFREE(MTYPE_BGP_EVPN_IMPORT_RT, irt);
+}
+
+/*
+ * Function to lookup Import RT node - used to map a RT to set of
+ * VNIs importing routes with that RT.
+ */
+static struct irt_node *
+lookup_import_rt (struct bgp *bgp, struct ecommunity_val *rt)
+{
+ struct irt_node *irt;
+ struct irt_node tmp;
+
+ memset(&tmp, 0, sizeof(struct irt_node));
+ memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE);
+ irt = hash_lookup(bgp->import_rt_hash, &tmp);
+ return irt;
+}
+
+/*
+ * Is specified VNI present on the RT's list of "importing" VNIs?
+ */
+static int
+is_vni_present_in_irt_vnis (struct list *vnis, struct bgpevpn *vpn)
+{
+ struct listnode *node, *nnode;
+ struct bgpevpn *tmp_vpn;
+
+ for (ALL_LIST_ELEMENTS (vnis, node, nnode, tmp_vpn))
+ {
+ if (tmp_vpn == vpn)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Compare Route Targets.
+ */
+static int
+evpn_route_target_cmp (struct ecommunity *ecom1, struct ecommunity *ecom2)
+{
+ if (ecom1 && !ecom2)
+ return -1;
+
+ if (!ecom1 && ecom2)
+ return 1;
+
+ if (!ecom1 && !ecom2)
+ return 0;
+
+ if (ecom1->str && !ecom2->str)
+ return -1;
+
+ if (!ecom1->str && ecom2->str)
+ return 1;
+
+ if (!ecom1->str && !ecom2->str)
+ return 0;
+
+ return strcmp(ecom1->str, ecom2->str);
+}
+
+/*
+ * Mask off global-admin field of specified extended community (RT),
+ * just retain the local-admin field.
+ */
+static inline void
+mask_ecom_global_admin (struct ecommunity_val *dst,
+ struct ecommunity_val *src)
+{
+ u_char type;
+
+ type = src->val[0];
+ dst->val[0] = 0;
+ if (type == ECOMMUNITY_ENCODE_AS)
+ {
+ dst->val[2] = dst->val[3] = 0;
+ }
+ else if (type == ECOMMUNITY_ENCODE_AS4 ||
+ type == ECOMMUNITY_ENCODE_IP)
+ {
+ dst->val[2] = dst->val[3] = 0;
+ dst->val[4] = dst->val[5] = 0;
+ }
+}
+
+/*
+ * Map one RT to specified VNI.
+ */
+static void
+map_vni_to_rt (struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity_val *eval)
+{
+ struct irt_node *irt;
+ struct ecommunity_val eval_tmp;
+
+ /* If using "automatic" RT, we only care about the local-admin sub-field.
+ * This is to facilitate using VNI as the RT for EBGP peering too.
+ */
+ memcpy (&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!is_import_rt_configured (vpn))
+ mask_ecom_global_admin (&eval_tmp, eval);
+
+ irt = lookup_import_rt (bgp, &eval_tmp);
+ if (irt && irt->vnis)
+ if (is_vni_present_in_irt_vnis (irt->vnis, vpn))
+ /* Already mapped. */
+ return;
+
+ if (!irt)
+ {
+ irt = import_rt_new (bgp, &eval_tmp);
+ assert (irt);
+ }
+
+ /* Add VNI to the hash list for this RT. */
+ listnode_add (irt->vnis, vpn);
+}
+
+/*
+ * Unmap specified VNI from specified RT. If there are no other
+ * VNIs for this RT, then the RT hash is deleted.
+ */
+static void
+unmap_vni_from_rt (struct bgp *bgp, struct bgpevpn *vpn,
+ struct irt_node *irt)
+{
+ /* Delete VNI from hash list for this RT. */
+ listnode_delete (irt->vnis, vpn);
+ if (!listnode_head (irt->vnis))
+ {
+ list_free (irt->vnis);
+ import_rt_free (bgp, irt);
+ }
+}
+
+/*
+ * Create RT extended community automatically from passed information:
+ * of the form AS:VNI.
+ * NOTE: We use only the lower 16 bits of the AS. This is sufficient as
+ * the need is to get a RT value that will be unique across different
+ * VNIs but the same across routers (in the same AS) for a particular
+ * VNI.
+ */
+static void
+form_auto_rt (struct bgp *bgp, struct bgpevpn *vpn,
+ struct list *rtl)
+{
+ struct ecommunity_val eval;
+ struct ecommunity *ecomadd;
+
+ encode_route_target_as ((bgp->as & 0xFFFF), vpn->vni, &eval);
+
+ ecomadd = ecommunity_new ();
+ ecommunity_add_val (ecomadd, &eval);
+ listnode_add_sort (rtl, ecomadd);
+}
+
+/*
+ * Derive RD and RT for a VNI automatically. Invoked at the time of
+ * creation of a VNI.
+ */
+static void
+derive_rd_rt_for_vni (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ bgp_evpn_derive_auto_rd (bgp, vpn);
+ bgp_evpn_derive_auto_rt_import (bgp, vpn);
+ bgp_evpn_derive_auto_rt_export (bgp, vpn);
+}
+
+/*
+ * Add (update) or delete MACIP from zebra.
+ */
+static int
+bgp_zebra_send_remote_macip (struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p,
+ struct in_addr remote_vtep_ip,
+ int add, u_char sticky)
+{
+ struct stream *s;
+ int ipa_len;
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ char buf3[INET6_ADDRSTRLEN];
+
+ /* 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, add ? ZEBRA_REMOTE_MACIP_ADD : ZEBRA_REMOTE_MACIP_DEL,
+ bgp->vrf_id);
+ stream_putl(s, vpn->vni);
+ stream_put (s, &p->prefix.mac.octet, ETHER_ADDR_LEN); /* Mac Addr */
+ /* IP address length and IP address, if any. */
+ if (IS_EVPN_PREFIX_IPADDR_NONE(p))
+ stream_putl(s, 0);
+ else
+ {
+ ipa_len = IS_EVPN_PREFIX_IPADDR_V4(p) ?
+ IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN;
+ stream_putl(s, ipa_len);
+ stream_put (s, &p->prefix.ip.ip.addr, ipa_len);
+ }
+ stream_put_in_addr(s, &remote_vtep_ip);
+
+ /* TX MAC sticky status */
+ if (add)
+ stream_putc (s, sticky);
+
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ if (bgp_debug_zebra (NULL))
+ zlog_debug("Tx %s MACIP, VNI %u %sMAC %s IP %s remote VTEP %s",
+ add ? "ADD" : "DEL", vpn->vni,
+ sticky ? "sticky " : "",
+ prefix_mac2str (&p->prefix.mac, buf1, sizeof(buf1)),
+ ipaddr2str (&p->prefix.ip, buf3, sizeof(buf3)),
+ inet_ntop(AF_INET, &remote_vtep_ip, buf2, sizeof(buf2)));
+
+ return zclient_send_message(zclient);
+}
+
+/*
+ * Add (update) or delete remote VTEP from zebra.
+ */
+static int
+bgp_zebra_send_remote_vtep (struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p, int add)
+{
+ struct stream *s;
+
+ /* 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, 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);
+ else if (IS_EVPN_PREFIX_IPADDR_V6(p))
+ {
+ zlog_err ("Bad remote IP when trying to %s remote VTEP for VNI %u",
+ add ? "ADD" : "DEL", vpn->vni);
+ return -1;
+ }
+
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ if (bgp_debug_zebra (NULL))
+ zlog_debug("Tx %s Remote VTEP, VNI %u remote VTEP %s",
+ add ? "ADD" : "DEL", vpn->vni,
+ inet_ntoa (p->prefix.ip.ipaddr_v4));
+
+ return zclient_send_message(zclient);
+}
+
+/*
+ * Build extended communities for EVPN route. RT and ENCAP are
+ * applicable to all routes.
+ */
+static void
+build_evpn_route_extcomm (struct bgpevpn *vpn, struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity ecom_sticky;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_sticky;
+ bgp_encap_types tnl_type;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+ u_int32_t seqnum;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset (&ecom_encap, 0, sizeof (ecom_encap));
+ encode_encap_extcomm (tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.val = (u_int8_t *)eval.val;
+
+ /* Add Encap */
+ attr->ecommunity = ecommunity_dup (&ecom_encap);
+
+ /* Add the export RTs */
+ for (ALL_LIST_ELEMENTS (vpn->export_rtl, node, nnode, ecom))
+ attr->ecommunity = ecommunity_merge (attr->ecommunity, ecom);
+
+ if (attr->sticky)
+ {
+ seqnum = 0;
+ memset (&ecom_sticky, 0, sizeof (ecom_sticky));
+ encode_mac_mobility_extcomm(1, seqnum, &eval_sticky);
+ ecom_sticky.size = 1;
+ ecom_sticky.val = (u_int8_t *)eval_sticky.val;
+ attr->ecommunity = ecommunity_merge (attr->ecommunity, &ecom_sticky);
+ }
+
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
+}
+
+/*
+ * Add MAC mobility extended community to attribute.
+ */
+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;
+ int i;
+ u_int8_t *pnt;
+ int type = 0;
+ int sub_type = 0;
+
+ /* Build MM */
+ encode_mac_mobility_extcomm (0, seq_num, &eval);
+
+ /* Find current MM ecommunity */
+ ecom_mm = NULL;
+
+ if (attr->ecommunity)
+ {
+ for (i = 0; i < attr->ecommunity->size; i++)
+ {
+ pnt = attr->ecommunity->val + (i * 8);
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if (type == ECOMMUNITY_ENCODE_EVPN && sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY)
+ {
+ ecom_mm = (struct ecommunity*) attr->ecommunity->val + (i * 8);
+ break;
+ }
+ }
+ }
+
+ /* Update the existing MM ecommunity */
+ if (ecom_mm)
+ {
+ memcpy(ecom_mm->val, eval.val, sizeof(char) * ECOMMUNITY_SIZE);
+ }
+ /* Add MM to existing */
+ else
+ {
+ memset (&ecom_tmp, 0, sizeof (ecom_tmp));
+ ecom_tmp.size = 1;
+ ecom_tmp.val = (u_int8_t *)eval.val;
+
+ attr->ecommunity = ecommunity_merge (attr->ecommunity, &ecom_tmp);
+ }
+}
+
+/* Install EVPN route into zebra. */
+static int
+evpn_zebra_install (struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p, struct in_addr remote_vtep_ip,
+ u_char sticky)
+{
+ int ret;
+
+ if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ ret = bgp_zebra_send_remote_macip (bgp, vpn, p, remote_vtep_ip, 1, sticky);
+ else
+ ret = bgp_zebra_send_remote_vtep (bgp, vpn, p, 1);
+
+ return ret;
+}
+
+/* Uninstall EVPN route from zebra. */
+static int
+evpn_zebra_uninstall (struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p, struct in_addr remote_vtep_ip)
+{
+ int ret;
+
+ if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ ret = bgp_zebra_send_remote_macip (bgp, vpn, p, remote_vtep_ip, 0, 0);
+ else
+ ret = bgp_zebra_send_remote_vtep (bgp, vpn, p, 0);
+
+ return ret;
+}
+
+/*
+ * Due to MAC mobility, the prior "local" best route has been supplanted
+ * by a "remote" best route. The prior route has to be deleted and withdrawn
+ * from peers.
+ */
+static void
+evpn_delete_old_local_route (struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_node *rn, struct bgp_info *old_local)
+{
+ struct bgp_node *global_rn;
+ struct bgp_info *ri;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ /* Locate route node in the global EVPN routing table. Note that
+ * this table is a 2-level tree (RD-level + Prefix-level) similar to
+ * L3VPN routes.
+ */
+ global_rn = bgp_afi_node_lookup (bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)&rn->p, &vpn->prd);
+ if (global_rn)
+ {
+ /* Delete route entry in the global EVPN table. */
+ delete_evpn_route_entry (bgp, vpn, afi, safi, global_rn, &ri);
+
+ /* Schedule for processing - withdraws to peers happen from
+ * this table.
+ */
+ if (ri)
+ bgp_process (bgp, global_rn, afi, safi);
+ bgp_unlock_node (global_rn);
+ }
+
+ /* Delete route entry in the VNI route table, caller to remove. */
+ bgp_info_delete (rn, old_local);
+}
+
+/*
+ * Calculate the best path for an EVPN route. Install/update best path in zebra,
+ * if appropriate.
+ */
+static int
+evpn_route_select_install (struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_node *rn)
+{
+ struct bgp_info *old_select, *new_select;
+ struct bgp_info_pair old_and_new;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ int ret = 0;
+
+ /* Compute the best path. */
+ bgp_best_selection (bgp, rn, &bgp->maxpaths[afi][safi],
+ &old_and_new, afi, safi);
+ old_select = old_and_new.old;
+ new_select = old_and_new.new;
+
+ /* If the best path hasn't changed - see if there is still something to update
+ * to zebra RIB.
+ */
+ if (old_select && old_select == new_select &&
+ old_select->type == ZEBRA_ROUTE_BGP &&
+ old_select->sub_type == BGP_ROUTE_NORMAL &&
+ !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR) &&
+ !CHECK_FLAG(old_select->flags, BGP_INFO_ATTR_CHANGED) &&
+ !bgp->addpath_tx_used[afi][safi])
+ {
+ if (bgp_zebra_has_route_changed (rn, old_select))
+ ret = evpn_zebra_install (bgp, vpn, (struct prefix_evpn *)&rn->p,
+ old_select->attr->nexthop,
+ old_select->attr->sticky);
+ UNSET_FLAG (old_select->flags, BGP_INFO_MULTIPATH_CHG);
+ bgp_zebra_clear_route_change_flags (rn);
+ return ret;
+ }
+
+ /* If the user did a "clear" this flag will be set */
+ UNSET_FLAG(rn->flags, BGP_NODE_USER_CLEAR);
+
+ /* bestpath has changed; update relevant fields and install or uninstall
+ * into the zebra RIB.
+ */
+ if (old_select || new_select)
+ bgp_bump_version(rn);
+
+ if (old_select)
+ bgp_info_unset_flag (rn, old_select, BGP_INFO_SELECTED);
+ if (new_select)
+ {
+ bgp_info_set_flag (rn, new_select, BGP_INFO_SELECTED);
+ bgp_info_unset_flag (rn, new_select, BGP_INFO_ATTR_CHANGED);
+ UNSET_FLAG (new_select->flags, BGP_INFO_MULTIPATH_CHG);
+ }
+
+ if (new_select
+ && new_select->type == ZEBRA_ROUTE_BGP
+ && new_select->sub_type == BGP_ROUTE_NORMAL)
+ {
+ ret = evpn_zebra_install (bgp, vpn, (struct prefix_evpn *) &rn->p,
+ new_select->attr->nexthop,
+ new_select->attr->sticky);
+ /* If an old best existed and it was a "local" route, the only reason
+ * it would be supplanted is due to MAC mobility procedures. So, we
+ * need to do an implicit delete and withdraw that route from peers.
+ */
+ if (old_select
+ && old_select->peer == bgp->peer_self
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_STATIC)
+ evpn_delete_old_local_route (bgp, vpn, rn, old_select);
+ }
+ else
+ {
+ if (old_select
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_NORMAL)
+ ret = evpn_zebra_uninstall (bgp, vpn, (struct prefix_evpn *) &rn->p,
+ old_select->attr->nexthop);
+ }
+
+ /* Clear any route change flags. */
+ bgp_zebra_clear_route_change_flags (rn);
+
+ /* Reap old select bgp_info, if it has been removed */
+ if (old_select && CHECK_FLAG (old_select->flags, BGP_INFO_REMOVED))
+ bgp_info_reap (rn, old_select);
+
+ return ret;
+}
+
+
+/*
+ * Return true if the local ri for this rn has sticky set
+ */
+static int
+evpn_route_is_sticky (struct bgp *bgp, struct bgp_node *rn)
+{
+ struct bgp_info *tmp_ri;
+ struct bgp_info *local_ri;
+
+ local_ri = NULL;
+ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
+ {
+ if (tmp_ri->peer == bgp->peer_self
+ && tmp_ri->type == ZEBRA_ROUTE_BGP
+ && tmp_ri->sub_type == BGP_ROUTE_STATIC)
+ local_ri = tmp_ri;
+ }
+
+ if (!local_ri)
+ return 0;
+
+ return local_ri->attr->sticky;
+}
+
+/*
+ * Create or update EVPN route entry. This could be in the VNI route table
+ * or the global route table.
+ */
+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 *tmp_ri;
+ struct bgp_info *local_ri, *remote_ri;
+ struct attr *attr_new;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+ int route_change = 1;
+ u_char sticky = 0;
+
+ *ri = NULL;
+
+ /* See if this is an update of an existing route, or a new add. Also,
+ * identify if already known from remote, and if so, the one with the
+ * highest sequence number; this is only when adding to the VNI routing
+ * table.
+ */
+ local_ri = remote_ri = NULL;
+ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
+ {
+ if (tmp_ri->peer == bgp->peer_self
+ && tmp_ri->type == ZEBRA_ROUTE_BGP
+ && tmp_ri->sub_type == BGP_ROUTE_STATIC)
+ local_ri = tmp_ri;
+ if (vni_table)
+ {
+ if (tmp_ri->type == ZEBRA_ROUTE_BGP
+ && tmp_ri->sub_type == BGP_ROUTE_NORMAL
+ && CHECK_FLAG (tmp_ri->flags, BGP_INFO_VALID))
+ {
+ if (!remote_ri)
+ remote_ri = tmp_ri;
+ else if (mac_mobility_seqnum (tmp_ri->attr) >
+ mac_mobility_seqnum (remote_ri->attr))
+ remote_ri = tmp_ri;
+ }
+ }
+ }
+
+ /* If route doesn't exist already, create a new one, if told to.
+ * Otherwise act based on whether the attributes of the route have
+ * changed or not.
+ */
+ if (!local_ri && !add)
+ return 0;
+
+ if (!local_ri)
+ {
+ /* When learnt locally for the first time but already known from
+ * remote, we have to initiate appropriate MAC mobility steps. This
+ * is applicable when updating the VNI routing table.
+ */
+ if (remote_ri)
+ {
+ u_int32_t cur_seqnum;
+
+ /* Add MM extended community to route. */
+ cur_seqnum = mac_mobility_seqnum (remote_ri->attr);
+ add_mac_mobility_to_attr (cur_seqnum + 1, attr);
+ }
+
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern (attr);
+
+ /* Extract MAC mobility sequence number, if any. */
+ attr_new->mm_seqnum = bgp_attr_mac_mobility_seqnum (attr_new, &sticky);
+ attr_new->sticky = sticky;
+
+ /* Create new route with its attribute. */
+ tmp_ri = info_make (ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0,
+ bgp->peer_self, attr_new, rn);
+ SET_FLAG (tmp_ri->flags, BGP_INFO_VALID);
+ bgp_info_extra_get(tmp_ri);
+
+ /* The VNI goes into the 'label' field of the route */
+ vni2label (vpn->vni, &label);
+
+ memcpy (&tmp_ri->extra->label, &label, BGP_LABEL_BYTES);
+ bgp_info_add (rn, tmp_ri);
+ }
+ else
+ {
+ tmp_ri = local_ri;
+ if (attrhash_cmp (tmp_ri->attr, attr) &&
+ !CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
+ route_change = 0;
+ else
+ {
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern (attr);
+ bgp_info_set_flag (rn, tmp_ri, BGP_INFO_ATTR_CHANGED);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(tmp_ri->flags, BGP_INFO_REMOVED))
+ bgp_info_restore(rn, tmp_ri);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern (&tmp_ri->attr);
+ tmp_ri->attr = attr_new;
+ tmp_ri->uptime = bgp_clock ();
+ }
+ }
+
+ /* Return back the route entry. */
+ *ri = tmp_ri;
+ return route_change;
+}
+
+/*
+ * Create or update EVPN route (of type based on prefix) for specified VNI
+ * and schedule for processing.
+ */
+static int
+update_evpn_route (struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p, u_char sticky)
+{
+ struct bgp_node *rn;
+ struct attr attr;
+ struct attr *attr_new;
+ struct bgp_info *ri;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ int route_change;
+
+ memset (&attr, 0, sizeof (struct attr));
+
+ /* Build path-attribute for this route. */
+ bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
+ attr.nexthop = vpn->originator_ip;
+ attr.mp_nexthop_global_in = vpn->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr.sticky = sticky;
+
+ /* Set up RT and ENCAP extended community. */
+ build_evpn_route_extcomm (vpn, &attr);
+
+ /* First, create (or fetch) route node within the VNI. */
+ /* NOTE: There is no RD here. */
+ rn = bgp_node_get (vpn->route_table, (struct prefix *)p);
+
+ /* Create or update route entry. */
+ route_change = update_evpn_route_entry (bgp, vpn, afi, safi,
+ rn, &attr, 1, 1, &ri);
+ assert (ri);
+ attr_new = ri->attr;
+
+ /* Perform route selection; this is just to set the flags correctly
+ * as local route in the VNI always wins.
+ */
+ evpn_route_select_install (bgp, vpn, rn);
+ bgp_unlock_node (rn);
+
+ /* If this is a new route or some attribute has changed, export the
+ * route to the global table. The route will be advertised to peers
+ * from there. Note that this table is a 2-level tree (RD-level +
+ * Prefix-level) similar to L3VPN routes.
+ */
+ if (route_change)
+ {
+ struct bgp_info *global_ri;
+
+ 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);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process (bgp, rn, afi, safi);
+ bgp_unlock_node (rn);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern (&attr.aspath);
+
+ return 0;
+}
+
+/*
+ * Delete EVPN route entry. This could be in the VNI route table
+ * or the global route table.
+ */
+static void
+delete_evpn_route_entry (struct bgp *bgp, struct bgpevpn *vpn,
+ afi_t afi, safi_t safi, struct bgp_node *rn,
+ struct bgp_info **ri)
+{
+ struct bgp_info *tmp_ri;
+
+ *ri = NULL;
+
+ /* Now, find matching route. */
+ for (tmp_ri = rn->info; tmp_ri; tmp_ri = tmp_ri->next)
+ if (tmp_ri->peer == bgp->peer_self
+ && tmp_ri->type == ZEBRA_ROUTE_BGP
+ && tmp_ri->sub_type == BGP_ROUTE_STATIC)
+ break;
+
+ *ri = tmp_ri;
+
+ /* Mark route for delete. */
+ if (tmp_ri)
+ bgp_info_delete (rn, tmp_ri);
+}
+
+/*
+ * Delete EVPN route (of type based on prefix) for specified VNI and
+ * schedule for processing.
+ */
+static int
+delete_evpn_route (struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p)
+{
+ struct bgp_node *rn, *global_rn;
+ struct bgp_info *ri;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ /* First, locate the route node within the VNI. If it doesn't exist, there
+ * is nothing further to do.
+ */
+ /* NOTE: There is no RD here. */
+ rn = bgp_node_lookup (vpn->route_table, (struct prefix *)p);
+ if (!rn)
+ return 0;
+
+ /* Next, locate route node in the global EVPN routing table. Note that
+ * this table is a 2-level tree (RD-level + Prefix-level) similar to
+ * L3VPN routes.
+ */
+ global_rn = bgp_afi_node_lookup (bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)p, &vpn->prd);
+ if (global_rn)
+ {
+ /* Delete route entry in the global EVPN table. */
+ delete_evpn_route_entry (bgp, vpn, afi, safi, global_rn, &ri);
+
+ /* Schedule for processing - withdraws to peers happen from
+ * this table.
+ */
+ if (ri)
+ bgp_process (bgp, global_rn, afi, safi);
+ bgp_unlock_node (global_rn);
+ }
+
+ /* Delete route entry in the VNI route table. This can just be removed. */
+ delete_evpn_route_entry (bgp, vpn, afi, safi, rn, &ri);
+ if (ri)
+ bgp_info_reap (rn, ri);
+ bgp_unlock_node (rn);
+
+ return 0;
+}
+
+/*
+ * Update all type-2 (MACIP) local routes for this VNI - these should also
+ * be scheduled for advertise to peers.
+ */
+static int
+update_all_type2_routes (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ struct attr attr;
+ struct attr attr_sticky;
+ struct attr *attr_new;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+ memset (&attr, 0, sizeof (struct attr));
+ memset (&attr_sticky, 0, sizeof (struct attr));
+
+ /* Build path-attribute - all type-2 routes for this VNI will share the
+ * same path attribute.
+ */
+ bgp_attr_default_set (&attr, BGP_ORIGIN_IGP);
+ bgp_attr_default_set (&attr_sticky, BGP_ORIGIN_IGP);
+ attr.nexthop = vpn->originator_ip;
+ attr.mp_nexthop_global_in = vpn->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr_sticky.nexthop = vpn->originator_ip;
+ attr_sticky.mp_nexthop_global_in = vpn->originator_ip;
+ attr_sticky.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr_sticky.sticky = 1;
+
+ /* Set up RT, ENCAP and sticky MAC extended community. */
+ build_evpn_route_extcomm (vpn, &attr);
+ build_evpn_route_extcomm (vpn, &attr_sticky);
+
+ /* Walk this VNI's route table and update local type-2 routes. For any
+ * routes updated, update corresponding entry in the global table too.
+ */
+ for (rn = bgp_table_top (vpn->route_table); rn; rn = bgp_route_next (rn))
+ {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+ struct bgp_node *rd_rn;
+ struct bgp_info *global_ri;
+
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ continue;
+
+ if (evpn_route_is_sticky (bgp, rn))
+ update_evpn_route_entry (bgp, vpn, afi, safi, rn, &attr_sticky, 0, 1, &ri);
+ else
+ update_evpn_route_entry (bgp, vpn, afi, safi, rn, &attr, 0, 1, &ri);
+
+ /* If a local route exists for this prefix, we need to update
+ * the global routing table too.
+ */
+ if (!ri)
+ continue;
+
+ /* Perform route selection; this is just to set the flags correctly
+ * as local route in the VNI always wins.
+ */
+ evpn_route_select_install (bgp, vpn, rn);
+
+ attr_new = ri->attr;
+
+ /* Update route in global routing table. */
+ rd_rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)evp, &vpn->prd);
+ assert (rd_rn);
+ update_evpn_route_entry (bgp, vpn, afi, safi, rd_rn,
+ attr_new, 0, 0, &global_ri);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process (bgp, rd_rn, afi, safi);
+ bgp_unlock_node (rd_rn);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern (&attr.aspath);
+ aspath_unintern (&attr_sticky.aspath);
+
+ return 0;
+}
+
+/*
+ * Delete all type-2 (MACIP) local routes for this VNI - only from the
+ * global routing table. These are also scheduled for withdraw from peers.
+ */
+static int
+delete_global_type2_routes (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_node *rdrn, *rn;
+ struct bgp_table *table;
+ struct bgp_info *ri;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ rdrn = bgp_node_lookup (bgp->rib[afi][safi], (struct prefix *) &vpn->prd);
+ if (rdrn && rdrn->info)
+ {
+ table = (struct bgp_table *)rdrn->info;
+ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ continue;
+
+ delete_evpn_route_entry (bgp, vpn, afi, safi, rn, &ri);
+ if (ri)
+ bgp_process (bgp, rn, afi, safi);
+ }
+ }
+
+ /* Unlock RD node. */
+ if (rdrn)
+ bgp_unlock_node (rdrn);
+
+ return 0;
+}
+
+/*
+ * Delete all type-2 (MACIP) local routes for this VNI - from the global
+ * table as well as the per-VNI route table.
+ */
+static int
+delete_all_type2_routes (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* First, walk the global route table for this VNI's type-2 local routes.
+ * EVPN routes are a 2-level table, first get the RD table.
+ */
+ delete_global_type2_routes (bgp, vpn);
+
+ /* Next, walk this VNI's route table and delete local type-2 routes. */
+ for (rn = bgp_table_top (vpn->route_table); rn; rn = bgp_route_next (rn))
+ {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ continue;
+
+ delete_evpn_route_entry (bgp, vpn, afi, safi, rn, &ri);
+
+ /* Route entry in local table gets deleted immediately. */
+ if (ri)
+ bgp_info_reap (rn, ri);
+ }
+
+ return 0;
+}
+
+/*
+ * Delete all routes in the per-VNI route table.
+ */
+static int
+delete_all_vni_routes (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri, *nextri;
+
+ /* Walk this VNI's route table and delete all routes. */
+ for (rn = bgp_table_top (vpn->route_table); rn; rn = bgp_route_next (rn))
+ {
+ for (ri = rn->info; (ri != NULL) && (nextri = ri->next, 1); ri = nextri)
+ {
+ bgp_info_delete (rn, ri);
+ bgp_info_reap (rn, ri);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Update (and advertise) local routes for a VNI. Invoked upon the VNI
+ * export RT getting modified or change to tunnel IP. Note that these
+ * situations need the route in the per-VNI table as well as the global
+ * table to be updated (as attributes change).
+ */
+static int
+update_routes_for_vni (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int ret;
+ struct prefix_evpn p;
+
+ /* Update and advertise the type-3 route (only one) followed by the
+ * locally learnt type-2 routes (MACIP) - for this VNI.
+ */
+ build_evpn_type3_prefix (&p, vpn->originator_ip);
+ ret = update_evpn_route (bgp, vpn, &p, 0);
+ if (ret)
+ return ret;
+
+ return update_all_type2_routes (bgp, vpn);
+}
+
+/*
+ * Delete (and withdraw) local routes for specified VNI from the global
+ * table and per-VNI table. After this, remove all other routes from
+ * the per-VNI table. Invoked upon the VNI being deleted or EVPN
+ * (advertise-all-vni) being disabled.
+ */
+static int
+delete_routes_for_vni (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int ret;
+ struct prefix_evpn p;
+
+ /* Delete and withdraw locally learnt type-2 routes (MACIP)
+ * followed by type-3 routes (only one) - for this VNI.
+ */
+ ret = delete_all_type2_routes (bgp, vpn);
+ if (ret)
+ return ret;
+
+ build_evpn_type3_prefix (&p, vpn->originator_ip);
+ ret = delete_evpn_route (bgp, vpn, &p);
+ if (ret)
+ return ret;
+
+ /* Delete all routes from the per-VNI table. */
+ return delete_all_vni_routes (bgp, vpn);
+}
+
+/*
+ * There is a tunnel endpoint IP address change for this VNI,
+ * need to re-advertise routes with the new nexthop.
+ */
+static int
+handle_tunnel_ip_change (struct bgp *bgp, struct bgpevpn *vpn,
+ struct in_addr originator_ip)
+{
+ struct prefix_evpn p;
+
+ /* Need to withdraw type-3 route as the originator IP is part
+ * of the key.
+ */
+ build_evpn_type3_prefix (&p, vpn->originator_ip);
+ delete_evpn_route (bgp, vpn, &p);
+
+ /* Update the tunnel IP and re-advertise all routes for this VNI. */
+ vpn->originator_ip = originator_ip;
+ return update_routes_for_vni (bgp, vpn);
+}
+
+/*
+ * Install route entry into the VNI routing table and invoke route selection.
+ */
+static int
+install_evpn_route_entry (struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p, struct bgp_info *parent_ri)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ struct attr *attr_new;
+ int ret;
+
+ /* Create (or fetch) route within the VNI. */
+ /* NOTE: There is no RD here. */
+ rn = bgp_node_get (vpn->route_table, (struct prefix *)p);
+
+ /* Check if route entry is already present. */
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->extra &&
+ (struct bgp_info *)ri->extra->parent == parent_ri)
+ break;
+
+ if (!ri)
+ {
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern (parent_ri->attr);
+
+ /* Create new route with its attribute. */
+ ri = info_make (parent_ri->type, parent_ri->sub_type, 0,
+ parent_ri->peer, attr_new, rn);
+ SET_FLAG (ri->flags, BGP_INFO_VALID);
+ bgp_info_extra_get(ri);
+ ri->extra->parent = parent_ri;
+ if (parent_ri->extra)
+ memcpy (&ri->extra->label, &parent_ri->extra->label, BGP_LABEL_BYTES);
+ bgp_info_add (rn, ri);
+ }
+ else
+ {
+ if (attrhash_cmp (ri->attr, parent_ri->attr) &&
+ !CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+ {
+ bgp_unlock_node (rn);
+ return 0;
+ }
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern (parent_ri->attr);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
+ bgp_info_restore(rn, ri);
+
+ /* Mark if nexthop has changed. */
+ if (!IPV4_ADDR_SAME (&ri->attr->nexthop, &attr_new->nexthop))
+ SET_FLAG(ri->flags, BGP_INFO_IGP_CHANGED);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern (&ri->attr);
+ ri->attr = attr_new;
+ ri->uptime = bgp_clock ();
+ }
+
+ /* Perform route selection and update zebra, if required. */
+ ret = evpn_route_select_install (bgp, vpn, rn);
+
+ return ret;
+}
+
+/*
+ * Uninstall route entry from the VNI routing table and send message
+ * to zebra, if appropriate.
+ */
+static int
+uninstall_evpn_route_entry (struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p, struct bgp_info *parent_ri)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int ret;
+
+ /* Locate route within the VNI. */
+ /* NOTE: There is no RD here. */
+ rn = bgp_node_lookup (vpn->route_table, (struct prefix *)p);
+ if (!rn)
+ return 0;
+
+ /* Find matching route entry. */
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->extra &&
+ (struct bgp_info *)ri->extra->parent == parent_ri)
+ break;
+
+ if (!ri)
+ return 0;
+
+ /* Mark entry for deletion */
+ bgp_info_delete (rn, ri);
+
+ /* Perform route selection and update zebra, if required. */
+ ret = evpn_route_select_install (bgp, vpn, rn);
+
+ /* Unlock route node. */
+ bgp_unlock_node (rn);
+
+ return ret;
+}
+
+/*
+ * Given a route entry and a VNI, see if this route entry should be
+ * imported into the VNI i.e., RTs match.
+ */
+static int
+is_route_matching_for_vni (struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_info *ri)
+{
+ struct attr *attr = ri->attr;
+ struct ecommunity *ecom;
+ int i;
+
+ assert (attr);
+ /* Route should have valid RT to be even considered. */
+ if (!(attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
+ return 0;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return 0;
+
+ /* For each extended community RT, see if it matches this VNI. If any RT
+ * matches, we're done.
+ */
+ for (i = 0; i < ecom->size; i++)
+ {
+ u_char *pnt;
+ u_char type, sub_type;
+ struct ecommunity_val *eval;
+ struct ecommunity_val eval_tmp;
+ struct irt_node *irt;
+
+ /* Only deal with RTs */
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ continue;
+
+ /* See if this RT matches specified VNIs import RTs */
+ irt = lookup_import_rt (bgp, eval);
+ if (irt && irt->vnis)
+ if (is_vni_present_in_irt_vnis (irt->vnis, vpn))
+ return 1;
+
+ /* Also check for non-exact match. In this, we mask out the AS and
+ * only check on the local-admin sub-field. This is to facilitate using
+ * VNI as the RT for EBGP peering too.
+ */
+ irt = NULL;
+ if (type == ECOMMUNITY_ENCODE_AS ||
+ type == ECOMMUNITY_ENCODE_AS4 ||
+ type == ECOMMUNITY_ENCODE_IP)
+ {
+ memcpy (&eval_tmp, eval, ECOMMUNITY_SIZE);
+ mask_ecom_global_admin (&eval_tmp, eval);
+ irt = lookup_import_rt (bgp, &eval_tmp);
+ }
+ if (irt && irt->vnis)
+ if (is_vni_present_in_irt_vnis (irt->vnis, vpn))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Install or uninstall routes of specified type that are appropriate for this
+ * particular VNI.
+ */
+static int
+install_uninstall_routes_for_vni (struct bgp *bgp, struct bgpevpn *vpn,
+ bgp_evpn_route_type rtype, int install)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_node *rd_rn, *rn;
+ struct bgp_table *table;
+ struct bgp_info *ri;
+ int ret;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Walk entire global routing table and evaluate routes which could be
+ * imported into this VPN. Note that we cannot just look at the routes for
+ * the VNI's RD - remote routes applicable for this VNI could have any RD.
+ */
+ /* EVPN routes are a 2-level table. */
+ for (rd_rn = bgp_table_top(bgp->rib[afi][safi]); rd_rn; rd_rn = bgp_route_next (rd_rn))
+ {
+ table = (struct bgp_table *)(rd_rn->info);
+ if (!table)
+ continue;
+
+ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ if (evp->prefix.route_type != rtype)
+ continue;
+
+ for (ri = rn->info; ri; ri = ri->next)
+ {
+ /* Consider "valid" remote routes applicable for this VNI. */
+ if (!(CHECK_FLAG (ri->flags, BGP_INFO_VALID)
+ && ri->type == ZEBRA_ROUTE_BGP
+ && ri->sub_type == BGP_ROUTE_NORMAL))
+ continue;
+
+ if (is_route_matching_for_vni (bgp, vpn, ri))
+ {
+ if (install)
+ ret = install_evpn_route_entry (bgp, vpn, evp, ri);
+ else
+ ret = uninstall_evpn_route_entry (bgp, vpn, evp, ri);
+
+ if (ret)
+ {
+ zlog_err ("%u: Failed to %s EVPN %s route in VNI %u",
+ bgp->vrf_id, install ? "install" : "uninstall",
+ rtype == BGP_EVPN_MAC_IP_ROUTE ? \
+ "MACIP" : "IMET", vpn->vni);
+ return ret;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Install any existing remote routes applicable for this VNI into its
+ * routing table. This is invoked when a VNI becomes "live" or its Import
+ * RT is changed.
+ */
+static int
+install_routes_for_vni (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int ret;
+
+ /* Install type-3 routes followed by type-2 routes - the ones applicable
+ * for this VNI.
+ */
+ ret = install_uninstall_routes_for_vni (bgp, vpn, BGP_EVPN_IMET_ROUTE, 1);
+ if (ret)
+ return ret;
+
+ return install_uninstall_routes_for_vni (bgp, vpn, BGP_EVPN_MAC_IP_ROUTE, 1);
+}
+
+/*
+ * Uninstall any existing remote routes for this VNI. One scenario in which
+ * this is invoked is upon an import RT change.
+ */
+static int
+uninstall_routes_for_vni (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int ret;
+
+ /* Uninstall type-2 routes followed by type-3 routes - the ones applicable
+ * for this VNI.
+ */
+ ret = install_uninstall_routes_for_vni (bgp, vpn, BGP_EVPN_MAC_IP_ROUTE, 0);
+ if (ret)
+ return ret;
+
+ return install_uninstall_routes_for_vni (bgp, vpn, BGP_EVPN_IMET_ROUTE, 0);
+}
+
+/*
+ * Install or uninstall route in matching VNIs (list).
+ */
+static int
+install_uninstall_route_in_vnis (struct bgp *bgp, afi_t afi, safi_t safi,
+ struct prefix_evpn *evp, struct bgp_info *ri,
+ struct list *vnis, int install)
+{
+ struct bgpevpn *vpn;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS (vnis, node, nnode, vpn))
+ {
+ int ret;
+
+ if (!is_vni_live (vpn))
+ continue;
+
+ if (install)
+ ret = install_evpn_route_entry (bgp, vpn, evp, ri);
+ else
+ ret = uninstall_evpn_route_entry (bgp, vpn, evp, ri);
+
+ if (ret)
+ {
+ zlog_err ("%u: Failed to %s EVPN %s route in VNI %u",
+ bgp->vrf_id, install ? "install" : "uninstall",
+ evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ? \
+ "MACIP" : "IMET", vpn->vni);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Install or uninstall route for appropriate VNIs.
+ */
+static int
+install_uninstall_evpn_route (struct bgp *bgp, afi_t afi, safi_t safi,
+ struct prefix *p, struct bgp_info *ri,
+ int import)
+{
+ struct prefix_evpn *evp = (struct prefix_evpn *)p;
+ struct attr *attr = ri->attr;
+ struct ecommunity *ecom;
+ int i;
+
+ assert (attr);
+
+ /* Only type-2 and type-3 routes go into a L2 VNI. */
+ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_IMET_ROUTE))
+ return 0;
+
+ /* If we don't have Route Target, nothing much to do. */
+ if (!(attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
+ return 0;
+
+ ecom = attr->ecommunity;
+ if (!ecom || !ecom->size)
+ return -1;
+
+ /* For each extended community RT, see which VNIs match and import
+ * the route into matching VNIs.
+ */
+ for (i = 0; i < ecom->size; i++)
+ {
+ u_char *pnt;
+ u_char type, sub_type;
+ struct ecommunity_val *eval;
+ struct ecommunity_val eval_tmp;
+ struct irt_node *irt;
+
+ /* Only deal with RTs */
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ continue;
+
+ /* Are we interested in this RT? */
+ irt = lookup_import_rt (bgp, eval);
+ if (irt && irt->vnis)
+ install_uninstall_route_in_vnis (bgp, afi, safi, evp,
+ ri, irt->vnis, import);
+
+ /* Also check for non-exact match. In this, we mask out the AS and
+ * only check on the local-admin sub-field. This is to facilitate using
+ * VNI as the RT for EBGP peering too.
+ */
+ irt = NULL;
+ if (type == ECOMMUNITY_ENCODE_AS ||
+ type == ECOMMUNITY_ENCODE_AS4 ||
+ type == ECOMMUNITY_ENCODE_IP)
+ {
+ memcpy (&eval_tmp, eval, ECOMMUNITY_SIZE);
+ mask_ecom_global_admin (&eval_tmp, eval);
+ irt = lookup_import_rt (bgp, &eval_tmp);
+ }
+ if (irt && irt->vnis)
+ install_uninstall_route_in_vnis (bgp, afi, safi, evp,
+ ri, irt->vnis, import);
+ }
+
+ return 0;
+}
+
+/*
+ * Update and advertise local routes for a VNI. Invoked upon router-id
+ * change. Note that the processing is done only on the global route table
+ * using routes that already exist in the per-VNI table.
+ */
+static int
+update_advertise_vni_routes (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ struct prefix_evpn p;
+ struct bgp_node *rn, *global_rn;
+ struct bgp_info *ri, *global_ri;
+ struct attr *attr;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ /* Locate type-3 route for VNI in the per-VNI table and use its
+ * attributes to create and advertise the type-3 route for this VNI
+ * in the global table.
+ */
+ build_evpn_type3_prefix (&p, vpn->originator_ip);
+ rn = bgp_node_lookup (vpn->route_table, (struct prefix *)&p);
+ if (!rn) /* unexpected */
+ return 0;
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->peer == bgp->peer_self
+ && ri->type == ZEBRA_ROUTE_BGP
+ && ri->sub_type == BGP_ROUTE_STATIC)
+ break;
+ if (!ri) /* unexpected */
+ return 0;
+ attr = ri->attr;
+
+ 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);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process (bgp, global_rn, afi, safi);
+ bgp_unlock_node (global_rn);
+
+ /* Now, walk this VNI's route table and use the route and its attribute
+ * to create and schedule route in global table.
+ */
+ for (rn = bgp_table_top (vpn->route_table); rn; rn = bgp_route_next (rn))
+ {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ /* Identify MAC-IP local routes. */
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ continue;
+
+ for (ri = rn->info; ri; ri = ri->next)
+ if (ri->peer == bgp->peer_self
+ && ri->type == ZEBRA_ROUTE_BGP
+ && ri->sub_type == BGP_ROUTE_STATIC)
+ break;
+ if (!ri)
+ continue;
+
+ /* Create route in global routing table using this route entry's
+ * attribute.
+ */
+ attr = ri->attr;
+ global_rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)evp, &vpn->prd);
+ assert (global_rn);
+ update_evpn_route_entry (bgp, vpn, afi, safi, global_rn,
+ attr, 1, 0, &global_ri);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process (bgp, global_rn, afi, safi);
+ bgp_unlock_node (global_rn);
+ }
+
+ return 0;
+}
+
+/*
+ * Delete (and withdraw) local routes for a VNI - only from the global
+ * table. Invoked upon router-id change.
+ */
+static int
+delete_withdraw_vni_routes (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int ret;
+ struct prefix_evpn p;
+ struct bgp_node *global_rn;
+ struct bgp_info *ri;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ /* Delete and withdraw locally learnt type-2 routes (MACIP)
+ * for this VNI - from the global table.
+ */
+ ret = delete_global_type2_routes (bgp, vpn);
+ if (ret)
+ return ret;
+
+ /* Remove type-3 route for this VNI from global table. */
+ build_evpn_type3_prefix (&p, vpn->originator_ip);
+ global_rn = bgp_afi_node_lookup (bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)&p, &vpn->prd);
+ if (global_rn)
+ {
+ /* Delete route entry in the global EVPN table. */
+ delete_evpn_route_entry (bgp, vpn, afi, safi, global_rn, &ri);
+
+ /* Schedule for processing - withdraws to peers happen from
+ * this table.
+ */
+ if (ri)
+ bgp_process (bgp, global_rn, afi, safi);
+ bgp_unlock_node (global_rn);
+ }
+
+ return 0;
+}
+
+/*
+ * Handle router-id change. Update and advertise local routes corresponding
+ * to this VNI from peers. Note that this is invoked after updating the
+ * router-id. The routes in the per-VNI table are used to create routes in
+ * the global table and schedule them.
+ */
+static void
+update_router_id_vni (struct hash_backet *backet, struct bgp *bgp)
+{
+ struct bgpevpn *vpn;
+
+ vpn = (struct bgpevpn *) backet->data;
+
+ if (!vpn)
+ {
+ zlog_warn ("%s: VNI hash entry for VNI not found",
+ __FUNCTION__);
+ return;
+ }
+
+ /* Skip VNIs with configured RD. */
+ if (is_rd_configured (vpn))
+ return;
+
+ bgp_evpn_derive_auto_rd (bgp, vpn);
+ update_advertise_vni_routes (bgp, vpn);
+}
+
+/*
+ * Handle router-id change. Delete and withdraw local routes corresponding
+ * to this VNI from peers. Note that this is invoked prior to updating
+ * the router-id and is done only on the global route table, the routes
+ * are needed in the per-VNI table to re-advertise with new router id.
+ */
+static void
+withdraw_router_id_vni (struct hash_backet *backet, struct bgp *bgp)
+{
+ struct bgpevpn *vpn;
+
+ vpn = (struct bgpevpn *) backet->data;
+
+ if (!vpn)
+ {
+ zlog_warn ("%s: VNI hash entry for VNI not found",
+ __FUNCTION__);
+ return;
+ }
+
+ /* Skip VNIs with configured RD. */
+ if (is_rd_configured (vpn))
+ return;
+
+ delete_withdraw_vni_routes (bgp, vpn);
+}
+
+/*
+ * Process received EVPN type-2 route (advertise or withdraw).
+ */
+static int
+process_type2_route (struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, u_char *pfx, int psize,
+ u_int32_t addpath_id)
+{
+ struct prefix_rd prd;
+ struct prefix_evpn p;
+ u_char ipaddr_len;
+ u_char macaddr_len;
+ mpls_label_t *label_pnt;
+ int ret;
+
+ /* Type-2 route should be either 33, 37 or 49 bytes or an
+ * additional 3 bytes if there is a second label (VNI):
+ * RD (8), ESI (10), Eth Tag (4), MAC Addr Len (1),
+ * MAC Addr (6), IP len (1), IP (0, 4 or 16),
+ * MPLS Lbl1 (3), MPLS Lbl2 (0 or 3)
+ */
+ if (psize != 33 && psize != 37 && psize != 49 &&
+ psize != 36 && psize != 40 && psize != 52)
+ {
+ zlog_err ("%u:%s - Rx EVPN Type-2 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy (&prd.val, pfx, 8);
+ pfx += 8;
+
+ /* Make EVPN prefix. */
+ memset (&p, 0, sizeof (struct prefix_evpn));
+ p.family = AF_ETHERNET;
+ p.prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN;
+ p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
+
+ /* Skip over Ethernet Seg Identifier for now. */
+ pfx += 10;
+
+ /* Skip over Ethernet Tag for now. */
+ pfx += 4;
+
+ /* Get the MAC Addr len */
+ macaddr_len = *pfx++;
+
+ /* Get the MAC Addr */
+ if (macaddr_len == (ETHER_ADDR_LEN * 8))
+ {
+ memcpy (&p.prefix.mac.octet, pfx, ETHER_ADDR_LEN);
+ pfx += ETHER_ADDR_LEN;
+ }
+ else
+ {
+ zlog_err ("%u:%s - Rx EVPN Type-2 NLRI with unsupported MAC address length %d",
+ peer->bgp->vrf_id, peer->host, macaddr_len);
+ return -1;
+ }
+
+
+ /* Get the IP. */
+ ipaddr_len = *pfx++;
+ if (ipaddr_len != 0 &&
+ ipaddr_len != IPV4_MAX_BITLEN &&
+ ipaddr_len != IPV6_MAX_BITLEN)
+ {
+ zlog_err ("%u:%s - Rx EVPN Type-2 NLRI with unsupported IP address length %d",
+ peer->bgp->vrf_id, peer->host, ipaddr_len);
+ return -1;
+ }
+
+ if (ipaddr_len)
+ {
+ ipaddr_len /= 8; /* Convert to bytes. */
+ p.prefix.ip.ipa_type = (ipaddr_len == IPV4_MAX_BYTELEN) ?
+ IPADDR_V4 : IPADDR_V6;
+ memcpy (&p.prefix.ip.ip.addr, pfx, ipaddr_len);
+ }
+ pfx += ipaddr_len;
+
+ /* Get the VNI (in MPLS label field). */
+ /* Note: We ignore the second VNI, if any. */
+ label_pnt = (mpls_label_t *) pfx;
+
+ /* Process the route. */
+ if (attr)
+ ret = bgp_update (peer, (struct prefix *)&p, addpath_id, attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, label_pnt, 0, NULL);
+ else
+ ret = bgp_withdraw (peer, (struct prefix *)&p, addpath_id, attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, label_pnt, NULL);
+ return ret;
+}
+
+/*
+ * Process received EVPN type-3 route (advertise or withdraw).
+ */
+static int
+process_type3_route (struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, u_char *pfx, int psize,
+ u_int32_t addpath_id)
+{
+ struct prefix_rd prd;
+ struct prefix_evpn p;
+ u_char ipaddr_len;
+ int ret;
+
+ /* Type-3 route should be either 17 or 29 bytes: RD (8), Eth Tag (4),
+ * IP len (1) and IP (4 or 16).
+ */
+ if (psize != 17 && psize != 29)
+ {
+ zlog_err ("%u:%s - Rx EVPN Type-3 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy (&prd.val, pfx, 8);
+ pfx += 8;
+
+ /* Make EVPN prefix. */
+ memset (&p, 0, sizeof (struct prefix_evpn));
+ p.family = AF_ETHERNET;
+ p.prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN;
+ p.prefix.route_type = BGP_EVPN_IMET_ROUTE;
+
+ /* Skip over Ethernet Tag for now. */
+ pfx += 4;
+
+ /* Get the IP. */
+ ipaddr_len = *pfx++;
+ if (ipaddr_len == IPV4_MAX_BITLEN)
+ {
+ p.prefix.ip.ipa_type = IPADDR_V4;
+ memcpy (&p.prefix.ip.ip.addr, pfx, IPV4_MAX_BYTELEN);
+ }
+ else
+ {
+ zlog_err ("%u:%s - Rx EVPN Type-3 NLRI with unsupported IP address length %d",
+ peer->bgp->vrf_id, peer->host, ipaddr_len);
+ return -1;
+ }
+
+ /* Process the route. */
+ if (attr)
+ ret = bgp_update (peer, (struct prefix *)&p, addpath_id, attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, 0, NULL);
+ else
+ ret = bgp_withdraw (peer, (struct prefix *)&p, addpath_id, attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, NULL, NULL);
+ return ret;
+}
+
+/*
+ * Process received EVPN type-5 route (advertise or withdraw).
+ */
+static int
+process_type5_route (struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, u_char *pfx, int psize,
+ u_int32_t addpath_id, int withdraw)
+{
+ struct prefix_rd prd;
+ struct prefix_evpn p;
+ struct bgp_route_evpn evpn;
+ u_char ippfx_len;
+ u_int32_t eth_tag;
+ mpls_label_t *label_pnt;
+ int ret;
+
+ /* Type-5 route should be 34 or 58 bytes:
+ * RD (8), ESI (10), Eth Tag (4), IP len (1), IP (4 or 16),
+ * GW (4 or 16) and VNI (3).
+ * Note that the IP and GW should both be IPv4 or both IPv6.
+ */
+ if (psize != 34 && psize != 58)
+ {
+ zlog_err ("%u:%s - Rx EVPN Type-5 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy (&prd.val, pfx, 8);
+ pfx += 8;
+
+ /* Make EVPN prefix. */
+ memset (&p, 0, sizeof (struct prefix_evpn));
+ p.family = AF_ETHERNET;
+ p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
+
+ /* Additional information outside of prefix - ESI and GW IP */
+ memset(&evpn, 0, sizeof(evpn));
+
+ /* Fetch ESI */
+ memcpy (&evpn.eth_s_id.val, pfx, 10);
+ pfx += 10;
+
+ /* Fetch Ethernet Tag. */
+ memcpy (&eth_tag, pfx, 4);
+ p.prefix.eth_tag = ntohl (eth_tag);
+ pfx += 4;
+
+ /* Fetch IP prefix length. */
+ ippfx_len = *pfx++;
+ if (ippfx_len > IPV6_MAX_BITLEN)
+ {
+ zlog_err ("%u:%s - Rx EVPN Type-5 NLRI with invalid IP Prefix length %d",
+ peer->bgp->vrf_id, peer->host, ippfx_len);
+ return -1;
+ }
+ p.prefix.ip_prefix_length = ippfx_len;
+
+ /* Determine IPv4 or IPv6 prefix */
+ /* Since the address and GW are from the same family, this just becomes
+ * a simple check on the total size.
+ */
+ if (psize == 34)
+ {
+ SET_IPADDR_V4 (&p.prefix.ip);
+ memcpy (&p.prefix.ip.ipaddr_v4, pfx, 4);
+ pfx += 4;
+ memcpy (&evpn.gw_ip.ipv4, pfx, 4);
+ pfx += 4;
+ p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV4;
+ }
+ else
+ {
+ SET_IPADDR_V6 (&p.prefix.ip);
+ memcpy (&p.prefix.ip.ipaddr_v6, pfx, 16);
+ pfx += 16;
+ memcpy (&evpn.gw_ip.ipv6, pfx, 16);
+ pfx += 16;
+ p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV6;
+ }
+
+ label_pnt = (mpls_label_t *) pfx;
+
+ /* Process the route. */
+ if (!withdraw)
+ ret = bgp_update (peer, (struct prefix *)&p, addpath_id, attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, label_pnt, 0, &evpn);
+ else
+ ret = bgp_withdraw (peer, (struct prefix *)&p, addpath_id, attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, &prd, label_pnt, &evpn);
+
+ return ret;
+}
+
+static void
+evpn_mpattr_encode_type5 (struct stream *s, struct prefix *p,
+ struct prefix_rd *prd, mpls_label_t * label,
+ struct attr *attr)
+{
+ int len;
+ char temp[16];
+ struct evpn_addr *p_evpn_p;
+
+ memset(&temp, 0, 16);
+ if (p->family != AF_ETHERNET)
+ return;
+ p_evpn_p = &(p->u.prefix_evpn);
+
+ if (IS_IPADDR_V4(&p_evpn_p->ip))
+ len = 8; /* ipv4 */
+ else
+ len = 32; /* ipv6 */
+ stream_putc(s, BGP_EVPN_IP_PREFIX_ROUTE);
+ /* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */
+ stream_putc(s, 8 + 10 + 4 + 1 + len + 3);
+ stream_put(s, prd->val, 8);
+ if (attr && attr)
+ stream_put(s, &(attr->evpn_overlay.eth_s_id), 10);
+ else
+ stream_put(s, &temp, 10);
+ stream_putl(s, p_evpn_p->eth_tag);
+ stream_putc(s, p_evpn_p->ip_prefix_length);
+ if (IS_IPADDR_V4(&p_evpn_p->ip))
+ stream_put_ipv4(s, p_evpn_p->ip.ipaddr_v4.s_addr);
+ else
+ stream_put(s, &p_evpn_p->ip.ipaddr_v6, 16);
+ if (attr && attr)
+ {
+ if (IS_IPADDR_V4(&p_evpn_p->ip))
+ stream_put_ipv4(s, attr->evpn_overlay.gw_ip.ipv4. s_addr);
+ else
+ stream_put(s, &(attr->evpn_overlay.gw_ip.ipv6), 16);
+ }
+ else
+ {
+ if (IS_IPADDR_V4(&p_evpn_p->ip))
+ stream_put_ipv4(s, 0);
+ else
+ stream_put(s, &temp, 16);
+ }
+
+ if (label)
+ stream_put(s, label, 3);
+ else
+ stream_put3(s, 0);
+}
+
+/*
+ * Cleanup specific VNI upon EVPN (advertise-all-vni) being disabled.
+ */
+static void
+cleanup_vni_on_disable (struct hash_backet *backet, struct bgp *bgp)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *) backet->data;
+
+ /* Remove EVPN routes and schedule for processing. */
+ delete_routes_for_vni (bgp, vpn);
+
+ /* Clear "live" flag and see if hash needs to be freed. */
+ UNSET_FLAG (vpn->flags, VNI_FLAG_LIVE);
+ if (!is_vni_configured (vpn))
+ bgp_evpn_free (bgp, vpn);
+}
+
+/*
+ * Free a VNI entry; iterator function called during cleanup.
+ */
+static void
+free_vni_entry (struct hash_backet *backet, struct bgp *bgp)
+{
+ struct bgpevpn *vpn;
+
+ vpn = (struct bgpevpn *) backet->data;
+ delete_all_vni_routes (bgp, vpn);
+ bgp_evpn_free(bgp, vpn);
+}
+
+
+/*
+ * Public functions.
+ */
+
+/*
+ * Handle change to BGP router id. This is invoked twice by the change
+ * handler, first before the router id has been changed and then after
+ * the router id has been changed. The first invocation will result in
+ * local routes for all VNIs being deleted and withdrawn and the next
+ * will result in the routes being re-advertised.
+ */
+void
+bgp_evpn_handle_router_id_update (struct bgp *bgp, int withdraw)
+{
+ if (withdraw)
+ hash_iterate (bgp->vnihash,
+ (void (*) (struct hash_backet *, void *))
+ withdraw_router_id_vni, bgp);
+ else
+ hash_iterate (bgp->vnihash,
+ (void (*) (struct hash_backet *, void *))
+ update_router_id_vni, bgp);
+}
+
+/*
+ * Handle change to export RT - update and advertise local routes.
+ */
+int
+bgp_evpn_handle_export_rt_change (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ return update_routes_for_vni (bgp, vpn);
+}
+
+/*
+ * Handle change to RD. This is invoked twice by the change handler,
+ * first before the RD has been changed and then after the RD has
+ * been changed. The first invocation will result in local routes
+ * of this VNI being deleted and withdrawn and the next will result
+ * in the routes being re-advertised.
+ */
+void
+bgp_evpn_handle_rd_change (struct bgp *bgp, struct bgpevpn *vpn,
+ int withdraw)
+{
+ if (withdraw)
+ delete_withdraw_vni_routes (bgp, vpn);
+ else
+ update_advertise_vni_routes (bgp, vpn);
+}
+
+/*
+ * Install routes for this VNI. Invoked upon change to Import RT.
+ */
+int
+bgp_evpn_install_routes (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ return install_routes_for_vni (bgp, vpn);
+}
+
+/*
+ * Uninstall all routes installed for this VNI. Invoked upon change
+ * to Import RT.
+ */
+int
+bgp_evpn_uninstall_routes (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ return uninstall_routes_for_vni (bgp, vpn);
+}
+
+/*
+ * Function to display "tag" in route as a VNI.
+ */
+char *
+bgp_evpn_label2str (mpls_label_t *label, char *buf, int len)
+{
+ vni_t vni;
+
+ vni = label2vni (label);
+ snprintf (buf, len, "%u", vni);
+ return buf;
+}
+
+/*
+ * Function to convert evpn route to string.
+ * NOTE: We don't use prefix2str as the output here is a bit different.
+ */
+char *
+bgp_evpn_route2str (struct prefix_evpn *p, char *buf, int len)
+{
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[PREFIX2STR_BUFFER];
+
+ if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE)
+ {
+ snprintf (buf, len, "[%d]:[0]:[%d]:[%s]",
+ p->prefix.route_type, IS_EVPN_PREFIX_IPADDR_V4(p) ? \
+ IPV4_MAX_BITLEN : IPV6_MAX_BITLEN,
+ inet_ntoa(p->prefix.ip.ipaddr_v4));
+ }
+ else if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ {
+ if (IS_EVPN_PREFIX_IPADDR_NONE(p))
+ snprintf (buf, len, "[%d]:[0]:[0]:[%d]:[%s]",
+ p->prefix.route_type, 8*ETHER_ADDR_LEN,
+ prefix_mac2str (&p->prefix.mac, buf1, sizeof(buf1)));
+ else
+ {
+ u_char family;
+
+ family = IS_EVPN_PREFIX_IPADDR_V4(p) ? \
+ AF_INET : AF_INET6;
+ snprintf (buf, len, "[%d]:[0]:[0]:[%d]:[%s]:[%d]:[%s]",
+ p->prefix.route_type, 8*ETHER_ADDR_LEN,
+ prefix_mac2str (&p->prefix.mac, buf1, sizeof(buf1)),
+ family == AF_INET ? IPV4_MAX_BITLEN : IPV6_MAX_BITLEN,
+ inet_ntop (family, &p->prefix.ip.ip.addr,
+ buf2, PREFIX2STR_BUFFER));
+ }
+ }
+ else
+ {
+ /* Currently, this is to cater to other AF_ETHERNET code. */
+ }
+
+ return(buf);
+}
+
+/*
+ * Encode EVPN prefix in Update (MP_REACH)
+ */
+void
+bgp_evpn_encode_prefix (struct stream *s, struct prefix *p,
+ struct prefix_rd *prd, mpls_label_t *label,
+ struct attr *attr, int addpath_encode,
+ u_int32_t addpath_tx_id)
+{
+ struct prefix_evpn *evp = (struct prefix_evpn *)p;
+ int ipa_len = 0;
+
+ if (addpath_encode)
+ stream_putl (s, addpath_tx_id);
+
+ /* Route type */
+ stream_putc (s, evp->prefix.route_type);
+
+ switch (evp->prefix.route_type)
+ {
+ case BGP_EVPN_MAC_IP_ROUTE:
+ if (IS_EVPN_PREFIX_IPADDR_V4(evp))
+ ipa_len = IPV4_MAX_BYTELEN;
+ else if (IS_EVPN_PREFIX_IPADDR_V6(evp))
+ ipa_len = IPV6_MAX_BYTELEN;
+ stream_putc (s, 33 + ipa_len); // 1 VNI
+ stream_put (s, prd->val, 8); /* RD */
+ stream_put (s, 0, 10); /* ESI */
+ stream_putl (s, 0); /* Ethernet Tag ID */
+ stream_putc (s, 8*ETHER_ADDR_LEN); /* Mac Addr Len - bits */
+ stream_put (s, evp->prefix.mac.octet, 6); /* Mac Addr */
+ stream_putc (s, 8*ipa_len); /* IP address Length */
+ if (ipa_len)
+ stream_put (s, &evp->prefix.ip.ip.addr, ipa_len); /* IP */
+ stream_put (s, label, BGP_LABEL_BYTES); /* VNI is contained in 'tag' */
+ break;
+
+ case BGP_EVPN_IMET_ROUTE:
+ stream_putc (s, 17); // TODO: length - assumes IPv4 address
+ stream_put (s, prd->val, 8); /* RD */
+ stream_putl (s, 0); /* Ethernet Tag ID */
+ stream_putc (s, IPV4_MAX_BITLEN); /* IP address Length - bits */
+ /* Originating Router's IP Addr */
+ stream_put_in_addr (s, &evp->prefix.ip.ipaddr_v4);
+ break;
+
+ case BGP_EVPN_IP_PREFIX_ROUTE:
+ /* TODO: AddPath support. */
+ evpn_mpattr_encode_type5 (s, p, prd, label, attr);
+ break;
+
+ default:
+ break;
+ }
+}
int
bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
- struct bgp_nlri *packet, int withdraw)
-{
- u_char *pnt;
- u_char *lim;
- struct prefix p;
- struct prefix_rd prd;
- struct evpn_addr *p_evpn_p;
- struct bgp_route_evpn evpn;
- uint8_t route_type, route_length;
- mpls_label_t label;
- u_int32_t addpath_id = 0;
-
- /* Check peer status. */
- if (peer->status != Established)
- return 0;
-
- /* Make prefix_rd */
- prd.family = AF_UNSPEC;
- prd.prefixlen = 64;
-
- p_evpn_p = &p.u.prefix_evpn;
- pnt = packet->nlri;
- lim = pnt + packet->length;
- while (pnt < lim) {
- /* clear evpn structure */
- memset(&evpn, 0, sizeof(evpn));
-
- /* Clear prefix structure. */
- memset(&p, 0, sizeof(struct prefix));
- memset(&evpn.gw_ip, 0, sizeof(union gw_addr));
- memset(&evpn.eth_s_id, 0, sizeof(struct eth_segment_id));
-
- /* Fetch Route Type */
- route_type = *pnt++;
- route_length = *pnt++;
- /* simply ignore. goto next route type if any */
- if (route_type != EVPN_IP_PREFIX) {
- if (pnt + route_length > lim) {
- zlog_err
- ("not enough bytes for New Route Type left in NLRI?");
- return -1;
- }
- pnt += route_length;
- continue;
- }
-
- /* Fetch RD */
- if (pnt + 8 > lim) {
- zlog_err("not enough bytes for RD left in NLRI?");
- return -1;
- }
-
- /* Copy routing distinguisher to rd. */
- memcpy(&prd.val, pnt, 8);
- pnt += 8;
-
- /* Fetch ESI */
- if (pnt + 10 > lim) {
- zlog_err("not enough bytes for ESI left in NLRI?");
- return -1;
- }
- memcpy(&evpn.eth_s_id.val, pnt, 10);
- pnt += 10;
-
- /* Fetch Ethernet Tag */
- if (pnt + 4 > lim) {
- zlog_err("not enough bytes for Eth Tag left in NLRI?");
- return -1;
- }
-
- if (route_type == EVPN_IP_PREFIX) {
- p_evpn_p->route_type = route_type;
- memcpy(&(p_evpn_p->eth_tag), pnt, 4);
- p_evpn_p->eth_tag = ntohl(p_evpn_p->eth_tag);
- pnt += 4;
-
- /* Fetch IP prefix length. */
- p_evpn_p->ip_prefix_length = *pnt++;
-
- if (p_evpn_p->ip_prefix_length > 128) {
- zlog_err("invalid prefixlen %d in EVPN NLRI?",
- p.prefixlen);
- return -1;
- }
- /* determine IPv4 or IPv6 prefix */
- if (route_length - 4 - 10 - 8 -
- 3 /* label to be read */ >= 32) {
- SET_IPADDR_V6 (&p_evpn_p->ip);
- memcpy(&(p_evpn_p->ip.ipaddr_v6), pnt, 16);
- pnt += 16;
- memcpy(&evpn.gw_ip.ipv6, pnt, 16);
- pnt += 16;
- } else {
- SET_IPADDR_V4 (&p_evpn_p->ip);
- memcpy(&(p_evpn_p->ip.ipaddr_v4), pnt, 4);
- pnt += 4;
- memcpy(&evpn.gw_ip.ipv4, pnt, 4);
- pnt += 4;
- }
- p.family = AFI_L2VPN;
- if (IS_IPADDR_V4(&p_evpn_p->ip))
- p.prefixlen =
- (u_char) PREFIX_LEN_ROUTE_TYPE_5_IPV4;
- else
- p.prefixlen = PREFIX_LEN_ROUTE_TYPE_5_IPV6;
- p.family = AF_ETHERNET;
- }
-
- /* Fetch Label */
- if (pnt + BGP_LABEL_BYTES > lim) {
- zlog_err("not enough bytes for Label left in NLRI?");
- return -1;
- }
-
- memcpy(&label, pnt, BGP_LABEL_BYTES);
- bgp_set_valid_label(&label);
- pnt += BGP_LABEL_BYTES;
-
- if (!withdraw) {
- bgp_update(peer, &p, addpath_id, attr, AFI_L2VPN,
- SAFI_EVPN, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
- &prd, &label, 0, &evpn);
- } else {
- bgp_withdraw(peer, &p, addpath_id, attr, AFI_L2VPN,
- SAFI_EVPN, ZEBRA_ROUTE_BGP,
- BGP_ROUTE_NORMAL, &prd, &label, &evpn);
- }
- }
-
- /* Packet length consistency check. */
- if (pnt != lim)
- return -1;
- return 0;
+ struct bgp_nlri *packet, int withdraw)
+{
+ u_char *pnt;
+ u_char *lim;
+ afi_t afi;
+ safi_t safi;
+ u_int32_t addpath_id;
+ int addpath_encoded;
+ int psize = 0;
+ u_char rtype;
+ u_char rlen;
+ struct prefix p;
+
+ /* Check peer status. */
+ if (peer->status != Established)
+ {
+ zlog_err ("%u:%s - EVPN update received in state %d",
+ peer->bgp->vrf_id, peer->host, peer->status);
+ return -1;
+ }
+
+ /* Start processing the NLRI - there may be multiple in the MP_REACH */
+ pnt = packet->nlri;
+ lim = pnt + packet->length;
+ afi = packet->afi;
+ safi = packet->safi;
+ addpath_id = 0;
+
+ addpath_encoded = (CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV) &&
+ CHECK_FLAG (peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_RCV));
+
+ for (; pnt < lim; pnt += psize)
+ {
+ /* Clear prefix structure. */
+ memset (&p, 0, sizeof (struct prefix));
+
+ /* Deal with path-id if AddPath is supported. */
+ if (addpath_encoded)
+ {
+ /* When packet overflow occurs return immediately. */
+ if (pnt + BGP_ADDPATH_ID_LEN > lim)
+ return -1;
+
+ addpath_id = ntohl(*((uint32_t*) pnt));
+ pnt += BGP_ADDPATH_ID_LEN;
+ }
+
+ /* All EVPN NLRI types start with type and length. */
+ if (pnt + 2 > lim)
+ return -1;
+
+ rtype = *pnt++;
+ psize = rlen = *pnt++;
+
+ /* When packet overflow occur return immediately. */
+ if (pnt + psize > lim)
+ return -1;
+
+ switch (rtype)
+ {
+ case BGP_EVPN_MAC_IP_ROUTE:
+ if (process_type2_route (peer, afi, safi,
+ withdraw ? NULL : attr,
+ pnt, psize, addpath_id))
+ {
+ zlog_err ("%u:%s - Error in processing EVPN type-2 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+ break;
+
+ case BGP_EVPN_IMET_ROUTE:
+ if (process_type3_route (peer, afi, safi,
+ withdraw ? NULL : attr,
+ pnt, psize, addpath_id))
+ {
+ zlog_err ("%u:%s - Error in processing EVPN type-3 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+ break;
+
+ case BGP_EVPN_IP_PREFIX_ROUTE:
+ if (process_type5_route (peer, afi, safi, attr,
+ pnt, psize, addpath_id, withdraw))
+ {
+ zlog_err ("%u:%s - Error in processing EVPN type-5 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ }
+
+ /* Packet length consistency check. */
+ if (pnt != lim)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Map the RTs (configured or automatically derived) of a VNI to the VNI.
+ * The mapping will be used during route processing.
+ */
+void
+bgp_evpn_map_vni_to_its_rts (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int i;
+ struct ecommunity_val *eval;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom))
+ {
+ for (i = 0; i < ecom->size; i++)
+ {
+ eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
+ map_vni_to_rt (bgp, vpn, eval);
+ }
+ }
+}
+
+/*
+ * Unmap the RTs (configured or automatically derived) of a VNI from the VNI.
+ */
+void
+bgp_evpn_unmap_vni_from_its_rts (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int i;
+ struct ecommunity_val *eval;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom))
+ {
+ for (i = 0; i < ecom->size; i++)
+ {
+ struct irt_node *irt;
+ struct ecommunity_val eval_tmp;
+
+ eval = (struct ecommunity_val *) (ecom->val + (i * ECOMMUNITY_SIZE));
+ /* If using "automatic" RT, we only care about the local-admin sub-field.
+ * This is to facilitate using VNI as the RT for EBGP peering too.
+ */
+ memcpy (&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!is_import_rt_configured (vpn))
+ mask_ecom_global_admin (&eval_tmp, eval);
+
+ irt = lookup_import_rt (bgp, &eval_tmp);
+ if (irt)
+ unmap_vni_from_rt (bgp, vpn, irt);
+ }
+ }
+}
+
+/*
+ * Derive Import RT automatically for VNI and map VNI to RT.
+ * The mapping will be used during route processing.
+ */
+void
+bgp_evpn_derive_auto_rt_import (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ form_auto_rt (bgp, vpn, vpn->import_rtl);
+ UNSET_FLAG (vpn->flags, VNI_FLAG_IMPRT_CFGD);
+
+ /* Map RT to VNI */
+ bgp_evpn_map_vni_to_its_rts (bgp, vpn);
+}
+
+/*
+ * Derive Export RT automatically for VNI.
+ */
+void
+bgp_evpn_derive_auto_rt_export (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ form_auto_rt (bgp, vpn, vpn->export_rtl);
+ UNSET_FLAG (vpn->flags, VNI_FLAG_EXPRT_CFGD);
+}
+
+/*
+ * Derive RD automatically for VNI using passed information - it
+ * is of the form RouterId:unique-id-for-vni.
+ */
+void
+bgp_evpn_derive_auto_rd (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ char buf[100];
+
+ vpn->prd.family = AF_UNSPEC;
+ vpn->prd.prefixlen = 64;
+ sprintf (buf, "%s:%hu", inet_ntoa (bgp->router_id), vpn->rd_id);
+ str2prefix_rd (buf, &vpn->prd);
+ UNSET_FLAG (vpn->flags, VNI_FLAG_RD_CFGD);
+}
+
+/*
+ * Lookup VNI.
+ */
+struct bgpevpn *
+bgp_evpn_lookup_vni (struct bgp *bgp, vni_t vni)
+{
+ struct bgpevpn *vpn;
+ struct bgpevpn tmp;
+
+ memset(&tmp, 0, sizeof(struct bgpevpn));
+ tmp.vni = vni;
+ vpn = hash_lookup (bgp->vnihash, &tmp);
+ return vpn;
+}
+
+/*
+ * Create a new vpn - invoked upon configuration or zebra notification.
+ */
+struct bgpevpn *
+bgp_evpn_new (struct bgp *bgp, vni_t vni, struct in_addr originator_ip)
+{
+ struct bgpevpn *vpn;
+
+ if (!bgp)
+ return NULL;
+
+ vpn = XCALLOC (MTYPE_BGP_EVPN, sizeof (struct bgpevpn));
+ if (!vpn)
+ return NULL;
+
+ /* Set values - RD and RT set to defaults. */
+ vpn->vni = vni;
+ vpn->originator_ip = originator_ip;
+
+ /* Initialize route-target import and export lists */
+ vpn->import_rtl = list_new ();
+ vpn->import_rtl->cmp = (int (*)(void *, void *)) evpn_route_target_cmp;
+ vpn->export_rtl = list_new ();
+ vpn->export_rtl->cmp = (int (*)(void *, void *)) evpn_route_target_cmp;
+ bf_assign_index(bgp->rd_idspace, vpn->rd_id);
+ derive_rd_rt_for_vni (bgp, vpn);
+
+ /* Initialize EVPN route table. */
+ vpn->route_table = bgp_table_init (AFI_L2VPN, SAFI_EVPN);
+
+ /* Add to hash */
+ if (!hash_get(bgp->vnihash, vpn, hash_alloc_intern))
+ {
+ XFREE(MTYPE_BGP_EVPN, vpn);
+ return NULL;
+ }
+ QOBJ_REG (vpn, bgpevpn);
+ return vpn;
+}
+
+/*
+ * Free a given VPN - called in multiple scenarios such as zebra
+ * notification, configuration being deleted, advertise-all-vni disabled etc.
+ * This just frees appropriate memory, caller should have taken other
+ * needed actions.
+ */
+void
+bgp_evpn_free (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ bgp_table_unlock (vpn->route_table);
+ bgp_evpn_unmap_vni_from_its_rts (bgp, vpn);
+ list_delete (vpn->import_rtl);
+ list_delete (vpn->export_rtl);
+ vpn->import_rtl = NULL;
+ vpn->export_rtl = NULL;
+ bf_release_index(bgp->rd_idspace, vpn->rd_id);
+ hash_release (bgp->vnihash, vpn);
+ QOBJ_UNREG (vpn);
+ XFREE(MTYPE_BGP_EVPN, vpn);
+}
+
+/*
+ * Import route into matching VNI(s).
+ */
+int
+bgp_evpn_import_route (struct bgp *bgp, afi_t afi, safi_t safi,
+ struct prefix *p, struct bgp_info *ri)
+{
+ return install_uninstall_evpn_route (bgp, afi, safi, p, ri, 1);
+}
+
+/*
+ * Unimport route from matching VNI(s).
+ */
+int
+bgp_evpn_unimport_route (struct bgp *bgp, afi_t afi, safi_t safi,
+ struct prefix *p, struct bgp_info *ri)
+{
+ return install_uninstall_evpn_route (bgp, afi, safi, p, ri, 0);
+}
+
+/*
+ * Handle del of a local MACIP.
+ */
+int
+bgp_evpn_local_macip_del (struct bgp *bgp, vni_t vni,
+ struct ethaddr *mac, struct ipaddr *ip)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+
+ if (!bgp->vnihash)
+ {
+ zlog_err ("%u: VNI hash not created", bgp->vrf_id);
+ return -1;
+ }
+
+ /* Lookup VNI hash - should exist. */
+ vpn = bgp_evpn_lookup_vni (bgp, vni);
+ if (!vpn || !is_vni_live (vpn))
+ {
+ zlog_warn ("%u: VNI hash entry for VNI %u %s at MACIP DEL",
+ bgp->vrf_id, vni, vpn ? "not live" : "not found");
+ return -1;
+ }
+
+ /* Remove EVPN type-2 route and schedule for processing. */
+ build_evpn_type2_prefix (&p, mac, ip);
+ delete_evpn_route (bgp, vpn, &p);
+
+ return 0;
+}
+
+/*
+ * 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 bgpevpn *vpn;
+ struct prefix_evpn p;
+
+ if (!bgp->vnihash)
+ {
+ zlog_err ("%u: VNI hash not created", bgp->vrf_id);
+ return -1;
+ }
+
+ /* Lookup VNI hash - should exist. */
+ vpn = bgp_evpn_lookup_vni (bgp, vni);
+ if (!vpn || !is_vni_live (vpn))
+ {
+ zlog_warn ("%u: VNI hash entry for VNI %u %s at MACIP ADD",
+ bgp->vrf_id, vni, vpn ? "not live" : "not found");
+ return -1;
+ }
+
+ /* Create EVPN type-2 route and schedule for processing. */
+ build_evpn_type2_prefix (&p, mac, ip);
+ if (update_evpn_route (bgp, vpn, &p, sticky))
+ {
+ 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" : "",
+ prefix_mac2str (mac, buf, sizeof (buf)),
+ ipaddr2str (ip, buf2, sizeof(buf2)));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Handle del of a local VNI.
+ */
+int
+bgp_evpn_local_vni_del (struct bgp *bgp, vni_t vni)
+{
+ struct bgpevpn *vpn;
+
+ if (!bgp->vnihash)
+ {
+ zlog_err ("%u: VNI hash not created", bgp->vrf_id);
+ return -1;
+ }
+
+ /* Locate VNI hash */
+ vpn = bgp_evpn_lookup_vni (bgp, vni);
+ if (!vpn)
+ {
+ zlog_warn ("%u: VNI hash entry for VNI %u not found at DEL",
+ bgp->vrf_id, vni);
+ return 0;
+ }
+
+ /* Remove all local EVPN routes and schedule for processing (to
+ * withdraw from peers).
+ */
+ delete_routes_for_vni (bgp, vpn);
+
+ /* Clear "live" flag and see if hash needs to be freed. */
+ UNSET_FLAG (vpn->flags, VNI_FLAG_LIVE);
+ if (!is_vni_configured (vpn))
+ bgp_evpn_free (bgp, vpn);
+
+ return 0;
+}
+
+/*
+ * Handle add (or update) of a local VNI. The only VNI change we care
+ * about is change to local-tunnel-ip.
+ */
+int
+bgp_evpn_local_vni_add (struct bgp *bgp, vni_t vni, struct in_addr originator_ip)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+
+ if (!bgp->vnihash)
+ {
+ zlog_err ("%u: VNI hash not created", bgp->vrf_id);
+ return -1;
+ }
+
+ /* 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))
+ /* 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);
+ }
+
+ /* Create or update as appropriate. */
+ if (!vpn)
+ {
+ vpn = bgp_evpn_new (bgp, vni, originator_ip);
+ if (!vpn)
+ {
+ zlog_err ("%u: Failed to allocate VNI entry for VNI %u - at Add",
+ bgp->vrf_id, vni);
+ return -1;
+ }
+ }
+
+ /* Mark as "live" */
+ SET_FLAG (vpn->flags, VNI_FLAG_LIVE);
+
+ /* Create EVPN type-3 route and schedule for processing. */
+ build_evpn_type3_prefix (&p, vpn->originator_ip);
+ if (update_evpn_route (bgp, vpn, &p, 0))
+ {
+ zlog_err ("%u: Type3 route creation failure for VNI %u",
+ bgp->vrf_id, vni);
+ return -1;
+ }
+
+ /* If we have learnt and retained remote routes (VTEPs, MACs) for this VNI,
+ * install them.
+ */
+ install_routes_for_vni (bgp, vpn);
+
+ return 0;
+}
+
+/*
+ * Cleanup EVPN information on disable - Need to delete and withdraw
+ * EVPN routes from peers.
+ */
+void
+bgp_evpn_cleanup_on_disable (struct bgp *bgp)
+{
+ hash_iterate (bgp->vnihash,
+ (void (*) (struct hash_backet *, void *))
+ cleanup_vni_on_disable, bgp);
+}
+
+/*
+ * Cleanup EVPN information - invoked at the time of bgpd exit or when the
+ * BGP instance (default) is being freed.
+ */
+void
+bgp_evpn_cleanup (struct bgp *bgp)
+{
+ if (bgp->vnihash)
+ hash_iterate (bgp->vnihash,
+ (void (*) (struct hash_backet *, void *))
+ free_vni_entry, bgp);
+ if (bgp->import_rt_hash)
+ hash_free (bgp->import_rt_hash);
+ bgp->import_rt_hash = NULL;
+ if (bgp->vnihash)
+ hash_free (bgp->vnihash);
+ bgp->vnihash = NULL;
+ bf_free (bgp->rd_idspace);
}
+/*
+ * Initialization for EVPN
+ * Create
+ * VNI hash table
+ * hash for RT to VNI
+ * unique rd id space for auto derivation of RD for VNIs
+ */
void
-bgp_packet_mpattr_route_type_5(struct stream *s,
- struct prefix *p, struct prefix_rd *prd,
- mpls_label_t *label, struct attr *attr)
-{
- int len;
- char temp[16];
- struct evpn_addr *p_evpn_p;
-
- memset(&temp, 0, 16);
- if (p->family != AF_ETHERNET)
- return;
- p_evpn_p = &(p->u.prefix_evpn);
- if (IS_IPADDR_V4(&p_evpn_p->ip))
- len = 8; /* ipv4 */
- else
- len = 32; /* ipv6 */
- stream_putc(s, EVPN_IP_PREFIX);
- stream_putc(s,
- 8 /* RD */ + 10 /* ESI */ + 4 /* EthTag */ + 1 + len +
- 3 /* label */ );
- stream_put(s, prd->val, 8);
- if (attr && attr->extra)
- stream_put(s, &(attr->extra->evpn_overlay.eth_s_id), 10);
- else
- stream_put(s, &temp, 10);
- stream_putl(s, p_evpn_p->eth_tag);
- stream_putc(s, p_evpn_p->ip_prefix_length);
- if (IS_IPADDR_V4(&p_evpn_p->ip))
- stream_put_ipv4(s, p_evpn_p->ip.ipaddr_v4.s_addr);
- else
- stream_put(s, &p_evpn_p->ip.ipaddr_v6, 16);
- if (attr && attr->extra) {
- if (IS_IPADDR_V4(&p_evpn_p->ip))
- stream_put_ipv4(s,
- attr->extra->evpn_overlay.gw_ip.ipv4.
- s_addr);
- else
- stream_put(s, &(attr->extra->evpn_overlay.gw_ip.ipv6),
- 16);
- } else {
- if (IS_IPADDR_V4(&p_evpn_p->ip))
- stream_put_ipv4(s, 0);
- else
- stream_put(s, &temp, 16);
- }
- if (label)
- stream_put(s, label, 3);
- else
- stream_put3(s, 0);
- return;
+bgp_evpn_init (struct bgp *bgp)
+{
+ bgp->vnihash = hash_create (vni_hash_key_make,
+ vni_hash_cmp,
+ "BGP VNI Hash");
+ bgp->import_rt_hash = hash_create (import_rt_hash_key_make,
+ import_rt_hash_cmp,
+ "BGP Import RT Hash");
+ bf_init (bgp->rd_idspace, UINT16_MAX);
+ /*assign 0th index in the bitfield, so that we start with id 1*/
+ bf_assign_zero_index (bgp->rd_idspace);
}
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
index feabc9cd2..610710d84 100644
--- a/bgpd/bgp_evpn.h
+++ b/bgpd/bgp_evpn.h
@@ -21,20 +21,45 @@
#ifndef _QUAGGA_BGP_EVPN_H
#define _QUAGGA_BGP_EVPN_H
+#include "vxlan.h"
+
+#define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */
+
+extern void
+bgp_evpn_handle_router_id_update (struct bgp *bgp, int withdraw);
+extern char *
+bgp_evpn_label2str (mpls_label_t *label, char *buf, int len);
+extern char *
+bgp_evpn_route2str (struct prefix_evpn *p, char *buf, int len);
+extern void
+bgp_evpn_encode_prefix (struct stream *s, struct prefix *p,
+ struct prefix_rd *prd, mpls_label_t *label,
+ struct attr *attr, int addpath_encode,
+ u_int32_t addpath_tx_id);
extern int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
struct bgp_nlri *packet, int withdraw);
-
+extern int
+bgp_evpn_import_route (struct bgp *bgp, afi_t afi, safi_t safi,
+ struct prefix *p, struct bgp_info *ri);
+extern int
+bgp_evpn_unimport_route (struct bgp *bgp, afi_t afi, safi_t safi,
+ struct prefix *p, struct bgp_info *ri);
+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);
+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);
extern void
-bgp_packet_mpattr_route_type_5(struct stream *s,
- struct prefix *p, struct prefix_rd *prd,
- mpls_label_t *label, struct attr *attr);
-/* EVPN route types as per RFC7432 and
- * as per draft-ietf-bess-evpn-prefix-advertisement-02
- */
-#define EVPN_ETHERNET_AUTO_DISCOVERY 1
-#define EVPN_MACIP_ADVERTISEMENT 2
-#define EVPN_INCLUSIVE_MULTICAST_ETHERNET_TAG 3
-#define EVPN_ETHERNET_SEGMENT 4
-#define EVPN_IP_PREFIX 5
+bgp_evpn_cleanup_on_disable (struct bgp *bgp);
+extern void
+bgp_evpn_cleanup (struct bgp *bgp);
+extern void
+bgp_evpn_init (struct bgp *bgp);
-#endif /* _QUAGGA_BGP_EVPN_H */
+#endif /* _QUAGGA_BGP_EVPN_H */
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
new file mode 100644
index 000000000..cd40fa9f0
--- /dev/null
+++ b/bgpd/bgp_evpn_private.h
@@ -0,0 +1,232 @@
+/* BGP EVPN internal definitions
+ * Copyright (C) 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _BGP_EVPN_PRIVATE_H
+#define _BGP_EVPN_PRIVATE_H
+
+#include "vxlan.h"
+#include "zebra.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+
+/* EVPN prefix lengths. */
+#define EVPN_TYPE_2_ROUTE_PREFIXLEN 224
+#define EVPN_TYPE_3_ROUTE_PREFIXLEN 224
+
+/* EVPN route types. */
+typedef enum
+{
+ BGP_EVPN_AD_ROUTE = 1, /* Ethernet Auto-Discovery (A-D) route */
+ BGP_EVPN_MAC_IP_ROUTE, /* MAC/IP Advertisement route */
+ BGP_EVPN_IMET_ROUTE, /* Inclusive Multicast Ethernet Tag route */
+ BGP_EVPN_ES_ROUTE, /* Ethernet Segment route */
+ BGP_EVPN_IP_PREFIX_ROUTE, /* IP Prefix route */
+} bgp_evpn_route_type;
+
+/*
+ * Hash table of EVIs. Right now, the only type of EVI supported is with
+ * VxLAN encapsulation, hence each EVI corresponds to a L2 VNI.
+ * The VNIs are not "created" through BGP but through some other interface
+ * on the system. This table stores VNIs that BGP comes to know as present
+ * on the system (through interaction with zebra) as well as pre-configured
+ * VNIs (which need to be defined in the system to become "live").
+ */
+struct bgpevpn
+{
+ vni_t vni;
+ u_int32_t flags;
+#define VNI_FLAG_CFGD 0x1 /* VNI is user configured */
+#define VNI_FLAG_LIVE 0x2 /* VNI is "live" */
+#define VNI_FLAG_RD_CFGD 0x4 /* RD is user configured. */
+#define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */
+#define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */
+
+ /* Id for deriving the RD automatically for this VNI */
+ u_int16_t rd_id;
+
+ /* RD for this VNI. */
+ struct prefix_rd prd;
+
+ /* Route type 3 field */
+ struct in_addr originator_ip;
+
+ /* Import and Export RTs. */
+ struct list *import_rtl;
+ struct list *export_rtl;
+
+ /* Route table for EVPN routes for this VNI. */
+ struct bgp_table *route_table;
+
+ QOBJ_FIELDS
+};
+
+DECLARE_QOBJ_TYPE(bgpevpn)
+
+/* Mapping of Import RT to VNIs.
+ * The Import RTs of all VNIs are maintained in a hash table with each
+ * RT linking to all VNIs that will import routes matching this RT.
+ */
+struct irt_node
+{
+ /* RT */
+ struct ecommunity_val rt;
+
+ /* List of VNIs importing routes matching this RT. */
+ struct list *vnis;
+};
+
+#define RT_TYPE_IMPORT 1
+#define RT_TYPE_EXPORT 2
+#define RT_TYPE_BOTH 3
+
+static inline int
+is_vni_configured (struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG (vpn->flags, VNI_FLAG_CFGD));
+}
+
+static inline int
+is_vni_live (struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG (vpn->flags, VNI_FLAG_LIVE));
+}
+
+static inline int
+is_rd_configured (struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG (vpn->flags, VNI_FLAG_RD_CFGD));
+}
+
+static inline int
+bgp_evpn_rd_matches_existing (struct bgpevpn *vpn, struct prefix_rd *prd)
+{
+ return(memcmp (&vpn->prd.val, prd->val, ECOMMUNITY_SIZE) == 0);
+}
+
+static inline int
+is_import_rt_configured (struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG (vpn->flags, VNI_FLAG_IMPRT_CFGD));
+}
+
+static inline int
+is_export_rt_configured (struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG (vpn->flags, VNI_FLAG_EXPRT_CFGD));
+}
+
+static inline int
+is_vni_param_configured (struct bgpevpn *vpn)
+{
+ return (is_rd_configured (vpn) ||
+ is_import_rt_configured (vpn) ||
+ is_export_rt_configured (vpn));
+}
+
+static inline void
+vni2label (vni_t vni, mpls_label_t *label)
+{
+ u_char *tag = (u_char *) label;
+ tag[0] = (vni >> 16) & 0xFF;
+ tag[1] = (vni >> 8) & 0xFF;
+ tag[2] = vni & 0xFF;
+}
+
+static inline vni_t
+label2vni (mpls_label_t *label)
+{
+ u_char *tag = (u_char *) label;
+ vni_t vni;
+
+ vni = ((u_int32_t) *tag++ << 16);
+ vni |= (u_int32_t) *tag++ << 8;
+ vni |= (u_int32_t) (*tag & 0xFF);
+
+ return vni;
+}
+
+static inline void
+encode_mac_mobility_extcomm (int static_mac, u_int32_t seq,
+ struct ecommunity_val *eval)
+{
+ memset (eval, 0, sizeof (*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY;
+ if (static_mac)
+ eval->val[2] = ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY;
+ eval->val[4] = (seq >> 24) & 0xff;
+ eval->val[5] = (seq >> 16) & 0xff;
+ eval->val[6] = (seq >> 8) & 0xff;
+ eval->val[7] = seq & 0xff;
+}
+
+static inline void
+build_evpn_type2_prefix (struct prefix_evpn *p, struct ethaddr *mac,
+ struct ipaddr *ip)
+{
+ memset (p, 0, sizeof (struct prefix_evpn));
+ p->family = AF_ETHERNET;
+ p->prefixlen = EVPN_TYPE_2_ROUTE_PREFIXLEN;
+ p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
+ memcpy(&p->prefix.mac.octet, mac->octet, ETHER_ADDR_LEN);
+ p->prefix.ip.ipa_type = IPADDR_NONE;
+ if (ip)
+ memcpy(&p->prefix.ip, ip, sizeof (*ip));
+}
+
+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->prefixlen = EVPN_TYPE_3_ROUTE_PREFIXLEN;
+ p->prefix.route_type = BGP_EVPN_IMET_ROUTE;
+ p->prefix.ip.ipa_type = IPADDR_V4;
+ p->prefix.ip.ipaddr_v4 = originator_ip;
+}
+
+
+extern int
+bgp_evpn_handle_export_rt_change (struct bgp *bgp, struct bgpevpn *vpn);
+extern void
+bgp_evpn_handle_rd_change (struct bgp *bgp, struct bgpevpn *vpn, int withdraw);
+extern int
+bgp_evpn_install_routes (struct bgp *bgp, struct bgpevpn *vpn);
+extern int
+bgp_evpn_uninstall_routes (struct bgp *bgp, struct bgpevpn *vpn);
+extern void
+bgp_evpn_map_vni_to_its_rts (struct bgp *bgp, struct bgpevpn *vpn);
+extern void
+bgp_evpn_unmap_vni_from_its_rts (struct bgp *bgp, struct bgpevpn *vpn);
+extern void
+bgp_evpn_derive_auto_rt_import (struct bgp *bgp, struct bgpevpn *vpn);
+extern void
+bgp_evpn_derive_auto_rt_export (struct bgp *bgp, struct bgpevpn *vpn);
+extern void
+bgp_evpn_derive_auto_rd (struct bgp *bgp, struct bgpevpn *vpn);
+extern struct bgpevpn *
+bgp_evpn_lookup_vni (struct bgp *bgp, vni_t vni);
+extern struct bgpevpn *
+bgp_evpn_new (struct bgp *bgp, vni_t vni, struct in_addr originator_ip);
+extern void
+bgp_evpn_free (struct bgp *bgp, struct bgpevpn *vpn);
+#endif /* _BGP_EVPN_PRIVATE_H */
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
index a76e499f8..b5a58f014 100644
--- a/bgpd/bgp_evpn_vty.c
+++ b/bgpd/bgp_evpn_vty.c
@@ -31,11 +31,312 @@
#include "bgpd/bgp_vpn.h"
#include "bgpd/bgp_evpn_vty.h"
#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_vty.h"
#define SHOW_DISPLAY_STANDARD 0
#define SHOW_DISPLAY_TAGS 1
#define SHOW_DISPLAY_OVERLAY 2
+/*
+ * Context for VNI hash walk - used by callbacks.
+ */
+struct vni_walk_ctx
+{
+ struct bgp *bgp;
+ struct vty *vty;
+ struct in_addr vtep_ip;
+};
+
+struct evpn_config_write
+{
+ int write;
+ struct vty *vty;
+};
+
+#if defined (HAVE_CUMULUS)
+static void
+display_import_rt (struct vty *vty, struct irt_node *irt)
+{
+ u_char *pnt;
+ u_char type, sub_type;
+ struct ecommunity_as
+ {
+ as_t as;
+ u_int32_t val;
+ } eas;
+ struct ecommunity_ip
+ {
+ struct in_addr ip;
+ u_int16_t val;
+ } eip;
+ struct listnode *node, *nnode;
+ struct bgpevpn *tmp_vpn;
+
+
+ /* TODO: This needs to go into a function */
+
+ pnt = (u_char *)&irt->rt.val;
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ return;
+
+ switch (type)
+ {
+ case ECOMMUNITY_ENCODE_AS:
+ eas.as = (*pnt++ << 8);
+ eas.as |= (*pnt++);
+
+ eas.val = (*pnt++ << 24);
+ eas.val |= (*pnt++ << 16);
+ eas.val |= (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ vty_out (vty, "Route-target: %u:%u", eas.as, eas.val);
+ break;
+
+ case ECOMMUNITY_ENCODE_IP:
+ memcpy (&eip.ip, pnt, 4);
+ pnt += 4;
+ eip.val = (*pnt++ << 8);
+ eip.val |= (*pnt++);
+
+ vty_out (vty, "Route-target: %s:%u",
+ inet_ntoa (eip.ip), eip.val);
+ break;
+
+ case ECOMMUNITY_ENCODE_AS4:
+ eas.as = (*pnt++ << 24);
+ eas.as |= (*pnt++ << 16);
+ eas.as |= (*pnt++ << 8);
+ eas.as |= (*pnt++);
+
+ eas.val = (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ vty_out (vty, "Route-target: %u:%u", eas.as, eas.val);
+ break;
+
+ default:
+ return;
+ }
+
+ vty_out (vty, "\n");
+ vty_out (vty, "List of VNIs importing routes with this route-target:\n");
+
+ for (ALL_LIST_ELEMENTS (irt->vnis, node, nnode, tmp_vpn))
+ vty_out (vty, " %u\n", tmp_vpn->vni);
+}
+
+static void
+show_import_rt_entry (struct hash_backet *backet, struct vty *vty)
+{
+ struct irt_node *irt = (struct irt_node *) backet->data;
+ display_import_rt (vty, irt);
+}
+
+static void
+bgp_evpn_show_route_rd_header (struct vty *vty, struct bgp_node *rd_rn)
+{
+ u_int16_t type;
+ struct rd_as rd_as;
+ struct rd_ip rd_ip;
+ u_char *pnt;
+
+ pnt = rd_rn->p.u.val;
+
+ /* Decode RD type. */
+ type = decode_rd_type (pnt);
+
+ vty_out (vty, "Route Distinguisher: ");
+
+ switch (type)
+ {
+ case RD_TYPE_AS:
+ decode_rd_as (pnt + 2, &rd_as);
+ vty_out (vty, "%u:%d", rd_as.as, rd_as.val);
+ break;
+
+ case RD_TYPE_IP:
+ decode_rd_ip (pnt + 2, &rd_ip);
+ vty_out (vty, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+ break;
+
+ default:
+ vty_out (vty, "Unknown RD type");
+ break;
+ }
+
+ vty_out (vty, "\n");
+}
+
+static void
+bgp_evpn_show_route_header (struct vty *vty, struct bgp *bgp)
+{
+ char ri_header[] = " Network Next Hop Metric LocPrf Weight Path\n";
+
+ vty_out (vty, "BGP table version is 0, local router ID is %s\n",
+ inet_ntoa (bgp->router_id));
+ vty_out (vty, "Status codes: s suppressed, d damped, h history, "
+ "* valid, > best, i - internal\n");
+ vty_out (vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n");
+ vty_out (vty, "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
+ vty_out (vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n\n");
+ vty_out (vty, "%s", ri_header);
+}
+
+static void
+display_vni (struct vty *vty, struct bgpevpn *vpn)
+{
+ char buf1[INET6_ADDRSTRLEN];
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ vty_out (vty, "VNI: %d", vpn->vni);
+ if (is_vni_live (vpn))
+ vty_out (vty, " (known to the kernel)");
+ vty_out (vty, "\n");
+
+ 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, " Import Route Target:\n");
+ for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom))
+ {
+ ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out (vty, " %s\n", ecom_str);
+ XFREE (MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+
+ vty_out (vty, " Export Route Target:\n");
+ for (ALL_LIST_ELEMENTS (vpn->export_rtl, node, nnode, ecom))
+ {
+ ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out (vty, " %s\n", ecom_str);
+ XFREE (MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+}
+
+static void
+show_vni_routes (struct bgp *bgp, struct bgpevpn *vpn, int type,
+ struct vty *vty, struct in_addr vtep_ip)
+{
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int header = 1;
+ u_int32_t prefix_cnt, path_cnt;
+
+ prefix_cnt = path_cnt = 0;
+
+ for (rn = bgp_table_top (vpn->route_table); rn; rn = bgp_route_next (rn))
+ {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ if (type &&
+ evp->prefix.route_type != type)
+ continue;
+
+ if (rn->info)
+ {
+ /* Overall header/legend displayed once. */
+ if (header)
+ {
+ bgp_evpn_show_route_header (vty, bgp);
+ header = 0;
+ }
+
+ prefix_cnt++;
+ }
+
+ /* For EVPN, the prefix is displayed for each path (to fit in
+ * with code that already exists).
+ */
+ for (ri = rn->info; ri; ri = ri->next)
+ {
+ if (vtep_ip.s_addr &&
+ !IPV4_ADDR_SAME(&(vtep_ip), &(ri->attr->nexthop)))
+ continue;
+
+ path_cnt++;
+ route_vty_out (vty, &rn->p, ri, 0, SAFI_EVPN, NULL);
+ }
+ }
+
+ if (prefix_cnt == 0)
+ vty_out (vty, "No EVPN prefixes %sexist for this VNI\n",
+ type ? "(of requested type) " : "");
+ else
+ vty_out (vty, "\nDisplayed %u prefixes (%u paths)%s\n",
+ prefix_cnt, path_cnt,
+ type ? " (of requested type)" : "");
+}
+
+static void
+show_vni_routes_hash (struct hash_backet *backet, void *arg)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *) backet->data;
+ struct vni_walk_ctx *wctx = arg;
+ struct vty *vty = wctx->vty;
+
+ vty_out (vty, "\nVNI: %d\n\n", vpn->vni);
+ show_vni_routes (wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip);
+}
+
+static void
+show_vni_entry (struct hash_backet *backet, struct vty *vty)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *) backet->data;
+ char buf1[10];
+ char buf2[INET6_ADDRSTRLEN];
+ char rt_buf[25];
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ buf1[0] = '\0';
+ if (is_vni_live (vpn))
+ sprintf (buf1, "*");
+
+ vty_out(vty, "%-1s %-10u %-15s %-21s",
+ buf1, vpn->vni, inet_ntoa(vpn->originator_ip),
+ prefix_rd2str (&vpn->prd, buf2, RD_ADDRSTRLEN));
+
+ for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom))
+ {
+ ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (listcount(vpn->import_rtl) > 1)
+ sprintf (rt_buf, "%s, ...", ecom_str);
+ else
+ sprintf (rt_buf, "%s", ecom_str);
+ vty_out (vty, " %-25s", rt_buf);
+
+ XFREE (MTYPE_ECOMMUNITY_STR, ecom_str);
+ break;
+ }
+
+ for (ALL_LIST_ELEMENTS (vpn->export_rtl, node, nnode, ecom))
+ {
+ ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (listcount(vpn->export_rtl) > 1)
+ sprintf (rt_buf, "%s, ...", ecom_str);
+ else
+ sprintf (rt_buf, "%s", ecom_str);
+ vty_out (vty, " %-25s", rt_buf);
+
+ XFREE (MTYPE_ECOMMUNITY_STR, ecom_str);
+ break;
+ }
+ vty_out (vty, "\n");
+}
+#endif /* HAVE_CUMULUS */
+
static int
bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
enum bgp_show_type type, void *output_arg, int option,
@@ -677,7 +978,7 @@ DEFUN(evpnrt5_network,
argv[idx_ext_community]->arg,
argv[idx_word]->arg,
argv[idx_rmap] ? argv[idx_gwip]->arg : NULL,
- EVPN_IP_PREFIX, argv[idx_esi]->arg,
+ BGP_EVPN_IP_PREFIX_ROUTE, argv[idx_esi]->arg,
argv[idx_gwip]->arg, argv[idx_ethtag]->arg,
argv[idx_routermac]->arg);
}
@@ -709,27 +1010,1657 @@ DEFUN(no_evpnrt5_network,
return bgp_static_unset_safi(AFI_L2VPN, SAFI_EVPN, vty,
argv[idx_ipv4_prefixlen]->arg,
argv[idx_ext_community]->arg,
- argv[idx_label]->arg, EVPN_IP_PREFIX,
+ argv[idx_label]->arg, BGP_EVPN_IP_PREFIX_ROUTE,
argv[idx_esi]->arg, argv[idx_gwip]->arg,
argv[idx_ethtag]->arg);
}
+#if defined(HAVE_CUMULUS)
+static void
+evpn_rt_delete_auto (struct bgp *bgp, struct bgpevpn *vpn, struct list *rtl)
+{
+ struct listnode *node, *nnode, *node_to_del;
+ struct ecommunity *ecom, *ecom_auto;
+ struct ecommunity_val eval;
+
+ encode_route_target_as ((bgp->as & 0xFFFF), vpn->vni, &eval);
+
+ ecom_auto = ecommunity_new ();
+ ecommunity_add_val (ecom_auto, &eval);
+ node_to_del = NULL;
+
+ for (ALL_LIST_ELEMENTS (rtl, node, nnode, ecom))
+ {
+ if (ecommunity_match (ecom, ecom_auto))
+ {
+ ecommunity_free (&ecom);
+ node_to_del = node;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(rtl, node_to_del);
+
+ ecommunity_free(&ecom_auto);
+}
+
+static void
+evpn_import_rt_delete_auto (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ evpn_rt_delete_auto (bgp, vpn, vpn->import_rtl);
+}
+
+static void
+evpn_export_rt_delete_auto (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ evpn_rt_delete_auto (bgp, vpn, vpn->export_rtl);
+}
+
+/*
+ * Configure the Import RTs for a VNI (vty handler). Caller expected to
+ * check that this is a change.
+ */
+static void
+evpn_configure_import_rt (struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity *ecomadd)
+{
+ /* If the VNI is "live", we need to uninstall routes using the current
+ * import RT(s) first before we update the import RT, and subsequently
+ * install routes.
+ */
+ if (is_vni_live (vpn))
+ bgp_evpn_uninstall_routes (bgp, vpn);
+
+ /* Cleanup the RT to VNI mapping and get rid of existing import RT. */
+ bgp_evpn_unmap_vni_from_its_rts (bgp, vpn);
+
+ /* If the auto route-target is in use we must remove it */
+ evpn_import_rt_delete_auto(bgp, vpn);
+
+ /* Add new RT and rebuild the RT to VNI mapping */
+ listnode_add_sort (vpn->import_rtl, ecomadd);
+
+ SET_FLAG (vpn->flags, VNI_FLAG_IMPRT_CFGD);
+ bgp_evpn_map_vni_to_its_rts (bgp, vpn);
+
+ /* Install routes that match new import RT */
+ if (is_vni_live (vpn))
+ bgp_evpn_install_routes (bgp, vpn);
+}
+
+/*
+ * Unconfigure Import RT(s) for a VNI (vty handler).
+ */
+static void
+evpn_unconfigure_import_rt (struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node, *nnode, *node_to_del;
+ struct ecommunity *ecom;
+
+ /* Along the lines of "configure" except we have to reset to the
+ * automatic value.
+ */
+ if (is_vni_live (vpn))
+ bgp_evpn_uninstall_routes (bgp, vpn);
+
+ /* Cleanup the RT to VNI mapping and get rid of existing import RT. */
+ bgp_evpn_unmap_vni_from_its_rts (bgp, vpn);
+
+ /* Delete all import RTs */
+ if (ecomdel == NULL)
+ {
+ for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom))
+ ecommunity_free (&ecom);
+
+ list_delete_all_node(vpn->import_rtl);
+ }
+
+ /* Delete a specific import RT */
+ else
+ {
+ node_to_del = NULL;
+
+ for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom))
+ {
+ if (ecommunity_match (ecom, ecomdel))
+ {
+ ecommunity_free (&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(vpn->import_rtl, node_to_del);
+ }
+
+ /* Reset to auto RT - this also rebuilds the RT to VNI mapping */
+ if (list_isempty(vpn->import_rtl))
+ {
+ UNSET_FLAG (vpn->flags, VNI_FLAG_IMPRT_CFGD);
+ bgp_evpn_derive_auto_rt_import (bgp, vpn);
+ }
+ /* Rebuild the RT to VNI mapping */
+ else
+ bgp_evpn_map_vni_to_its_rts (bgp, vpn);
+
+ /* Install routes that match new import RT */
+ if (is_vni_live (vpn))
+ bgp_evpn_install_routes (bgp, vpn);
+}
+
+/*
+ * Configure the Export RT for a VNI (vty handler). Caller expected to
+ * check that this is a change. Note that only a single export RT is
+ * allowed for a VNI and any change to configuration is implemented as
+ * a "replace" (similar to other configuration).
+ */
+static void
+evpn_configure_export_rt (struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity *ecomadd)
+{
+ /* If the auto route-target is in use we must remove it */
+ evpn_export_rt_delete_auto (bgp, vpn);
+
+ listnode_add_sort (vpn->export_rtl, ecomadd);
+ SET_FLAG (vpn->flags, VNI_FLAG_EXPRT_CFGD);
+
+ if (is_vni_live (vpn))
+ bgp_evpn_handle_export_rt_change (bgp, vpn);
+}
+
+/*
+ * Unconfigure the Export RT for a VNI (vty handler)
+ */
+static void
+evpn_unconfigure_export_rt (struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node, *nnode, *node_to_del;
+ struct ecommunity *ecom;
+
+ /* Delete all export RTs */
+ if (ecomdel == NULL)
+ {
+ /* Reset to default and process all routes. */
+ for (ALL_LIST_ELEMENTS (vpn->export_rtl, node, nnode, ecom))
+ ecommunity_free (&ecom);
+
+ list_delete_all_node(vpn->export_rtl);
+ }
+
+ /* Delete a specific export RT */
+ else
+ {
+ node_to_del = NULL;
+
+ for (ALL_LIST_ELEMENTS (vpn->export_rtl, node, nnode, ecom))
+ {
+ if (ecommunity_match (ecom, ecomdel))
+ {
+ ecommunity_free (&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(vpn->export_rtl, node_to_del);
+ }
+
+ if (list_isempty(vpn->export_rtl))
+ {
+ UNSET_FLAG (vpn->flags, VNI_FLAG_EXPRT_CFGD);
+ bgp_evpn_derive_auto_rt_export (bgp, vpn);
+ }
+
+ if (is_vni_live (vpn))
+ bgp_evpn_handle_export_rt_change (bgp, vpn);
+}
+
+/*
+ * Configure RD for a VNI (vty handler)
+ */
+static void
+evpn_configure_rd (struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_rd *rd)
+{
+ /* If the VNI is "live", we need to delete and withdraw this VNI's
+ * local routes with the prior RD first. Then, after updating RD,
+ * need to re-advertise.
+ */
+ if (is_vni_live (vpn))
+ bgp_evpn_handle_rd_change (bgp, vpn, 1);
+
+ /* update RD */
+ memcpy(&vpn->prd, rd, sizeof (struct prefix_rd));
+ SET_FLAG (vpn->flags, VNI_FLAG_RD_CFGD);
+
+ if (is_vni_live (vpn))
+ bgp_evpn_handle_rd_change (bgp, vpn, 0);
+}
+
+/*
+ * Unconfigure RD for a VNI (vty handler)
+ */
+static void
+evpn_unconfigure_rd (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ /* If the VNI is "live", we need to delete and withdraw this VNI's
+ * local routes with the prior RD first. Then, after resetting RD
+ * to automatic value, need to re-advertise.
+ */
+ if (is_vni_live (vpn))
+ bgp_evpn_handle_rd_change (bgp, vpn, 1);
+
+ /* reset RD to default */
+ bgp_evpn_derive_auto_rd (bgp, vpn);
+
+ if (is_vni_live (vpn))
+ bgp_evpn_handle_rd_change (bgp, vpn, 0);
+}
+
+/*
+ * Create VNI, if not already present (VTY handler). Mark as configured.
+ */
+static struct bgpevpn *
+evpn_create_update_vni (struct bgp *bgp, vni_t vni)
+{
+ struct bgpevpn *vpn;
+
+ if (!bgp->vnihash)
+ return NULL;
+
+ vpn = bgp_evpn_lookup_vni (bgp, vni);
+ if (!vpn)
+ {
+ vpn = bgp_evpn_new (bgp, vni, bgp->router_id);
+ if (!vpn)
+ {
+ zlog_err ("%u: Failed to allocate VNI entry for VNI %u - at Config",
+ bgp->vrf_id, vni);
+ return NULL;
+ }
+ }
+
+ /* Mark as configured. */
+ SET_FLAG (vpn->flags, VNI_FLAG_CFGD);
+ return vpn;
+}
+
+/*
+ * Delete VNI. If VNI does not exist in the system (i.e., just
+ * configuration), all that is needed is to free it. Otherwise,
+ * any parameters configured for the VNI need to be reset (with
+ * appropriate action) and the VNI marked as unconfigured; the
+ * VNI will continue to exist, purely as a "learnt" entity.
+ */
+static int
+evpn_delete_vni (struct bgp *bgp, struct bgpevpn *vpn)
+{
+ assert (bgp->vnihash);
+
+ if (!is_vni_live (vpn))
+ {
+ bgp_evpn_free (bgp, vpn);
+ return 0;
+ }
+
+ /* We need to take the unconfigure action for each parameter of this VNI
+ * that is configured. Some optimization is possible, but not worth the
+ * additional code for an operation that should be pretty rare.
+ */
+ UNSET_FLAG (vpn->flags, VNI_FLAG_CFGD);
+
+ /* First, deal with the export side - RD and export RT changes. */
+ if (is_rd_configured (vpn))
+ evpn_unconfigure_rd (bgp, vpn);
+ if (is_export_rt_configured (vpn))
+ evpn_unconfigure_export_rt (bgp, vpn, NULL);
+
+ /* Next, deal with the import side. */
+ if (is_import_rt_configured (vpn))
+ evpn_unconfigure_import_rt (bgp, vpn, NULL);
+
+ return 0;
+}
+
+/*
+ * Display import RT mapping to VNIs (vty handler)
+ */
+static void
+evpn_show_import_rts (struct vty *vty, struct bgp *bgp)
+{
+ hash_iterate (bgp->import_rt_hash,
+ (void (*) (struct hash_backet *, void *))
+ show_import_rt_entry, vty);
+}
+
+/*
+ * Display EVPN routes for all VNIs - vty handler.
+ */
+static void
+evpn_show_routes_vni_all (struct vty *vty, struct bgp *bgp, struct in_addr vtep_ip)
+{
+ u_int32_t num_vnis;
+ struct vni_walk_ctx wctx;
+
+ num_vnis = hashcount(bgp->vnihash);
+ if (!num_vnis)
+ return;
+ memset (&wctx, 0, sizeof (struct vni_walk_ctx));
+ wctx.bgp = bgp;
+ wctx.vty = vty;
+ wctx.vtep_ip = vtep_ip;
+ hash_iterate (bgp->vnihash,
+ (void (*) (struct hash_backet *, void *))
+ show_vni_routes_hash, &wctx);
+}
+
+/*
+ * Display EVPN routes for a VNI -- for specific type-3 route (vty handler).
+ */
+static void
+evpn_show_route_vni_multicast (struct vty *vty, struct bgp *bgp,
+ vni_t vni, struct in_addr orig_ip)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ u_int32_t path_cnt = 0;
+ afi_t afi;
+ safi_t safi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Locate VNI. */
+ vpn = bgp_evpn_lookup_vni (bgp, vni);
+ if (!vpn)
+ {
+ vty_out (vty, "VNI not found\n");
+ return;
+ }
+
+ /* See if route exists. */
+ build_evpn_type3_prefix (&p, orig_ip);
+ rn = bgp_node_lookup (vpn->route_table, (struct prefix *)&p);
+ if (!rn || !rn->info)
+ {
+ vty_out (vty, "%% Network not in table\n");
+ return;
+ }
+
+ /* Prefix and num paths displayed once per prefix. */
+ route_vty_out_detail_header (vty, bgp, rn, NULL, afi, safi, NULL);
+
+ /* Display each path for this prefix. */
+ for (ri = rn->info; ri; ri = ri->next)
+ {
+ route_vty_out_detail (vty, bgp, &rn->p, ri, afi, safi, NULL);
+ path_cnt++;
+ }
+
+ vty_out (vty, "\nDisplayed %u paths for requested prefix\n",
+ path_cnt);
+}
+
+/*
+ * Display EVPN routes for a VNI -- for specific MAC and/or IP (vty handler).
+ * By definition, only matching type-2 route will be displayed.
+ */
+static void
+evpn_show_route_vni_macip (struct vty *vty, struct bgp *bgp,
+ vni_t vni, struct ethaddr *mac,
+ struct ipaddr *ip)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ u_int32_t path_cnt = 0;
+ afi_t afi;
+ safi_t safi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Locate VNI. */
+ vpn = bgp_evpn_lookup_vni (bgp, vni);
+ if (!vpn)
+ {
+ vty_out (vty, "VNI not found\n");
+ return;
+ }
+
+ /* See if route exists. Look for both non-sticky and sticky. */
+ build_evpn_type2_prefix (&p, mac, ip);
+ rn = bgp_node_lookup (vpn->route_table, (struct prefix *)&p);
+ if (!rn || !rn->info)
+ {
+ vty_out (vty, "%% Network not in table\n");
+ return;
+ }
+
+ /* Prefix and num paths displayed once per prefix. */
+ route_vty_out_detail_header (vty, bgp, rn, NULL, afi, safi, NULL);
+
+ /* Display each path for this prefix. */
+ for (ri = rn->info; ri; ri = ri->next)
+ {
+ route_vty_out_detail (vty, bgp, &rn->p, ri, afi, safi, NULL);
+ path_cnt++;
+ }
+
+ vty_out (vty, "\nDisplayed %u paths for requested prefix\n",
+ path_cnt);
+}
+
+/*
+ * Display EVPN routes for a VNI - vty handler.
+ * If 'type' is non-zero, only routes matching that type are shown.
+ * If the vtep_ip is non zero, only routes behind that vtep are shown
+ */
+static void
+evpn_show_routes_vni (struct vty *vty, struct bgp *bgp,
+ vni_t vni, int type, struct in_addr vtep_ip)
+{
+ struct bgpevpn *vpn;
+
+ /* Locate VNI. */
+ vpn = bgp_evpn_lookup_vni (bgp, vni);
+ if (!vpn)
+ {
+ vty_out (vty, "VNI not found\n");
+ return;
+ }
+
+ /* Walk this VNI's route table and display appropriate routes. */
+ show_vni_routes (bgp, vpn, type, vty, vtep_ip);
+}
+
+/*
+ * Display BGP EVPN routing table -- for specific RD and MAC and/or
+ * IP (vty handler). By definition, only matching type-2 route will be
+ * displayed.
+ */
+static void
+evpn_show_route_rd_macip (struct vty *vty, struct bgp *bgp,
+ struct prefix_rd *prd, struct ethaddr *mac,
+ struct ipaddr *ip)
+{
+ struct prefix_evpn p;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ afi_t afi;
+ safi_t safi;
+ u_int32_t path_cnt = 0;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* See if route exists. Look for both non-sticky and sticky. */
+ build_evpn_type2_prefix (&p, mac, ip);
+ rn = bgp_afi_node_lookup (bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)&p, prd);
+ if (!rn || !rn->info)
+ {
+ vty_out (vty, "%% Network not in table\n");
+ return;
+ }
+
+ /* Prefix and num paths displayed once per prefix. */
+ route_vty_out_detail_header (vty, bgp, rn, prd, afi, safi, NULL);
+
+ /* Display each path for this prefix. */
+ for (ri = rn->info; ri; ri = ri->next)
+ {
+ route_vty_out_detail (vty, bgp, &rn->p, ri, afi, safi, NULL);
+ path_cnt++;
+ }
+
+ vty_out (vty, "\nDisplayed %u paths for requested prefix\n",
+ path_cnt);
+}
+
+/*
+ * Display BGP EVPN routing table -- for specific RD (vty handler)
+ * If 'type' is non-zero, only routes matching that type are shown.
+ */
+static void
+evpn_show_route_rd (struct vty *vty, struct bgp *bgp,
+ struct prefix_rd *prd, int type)
+{
+ struct bgp_node *rd_rn;
+ struct bgp_table *table;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int rd_header = 1;
+ afi_t afi;
+ safi_t safi;
+ u_int32_t prefix_cnt, path_cnt;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+ prefix_cnt = path_cnt = 0;
+
+ rd_rn = bgp_node_lookup (bgp->rib[afi][safi], (struct prefix *) prd);
+ if (!rd_rn)
+ return;
+ table = (struct bgp_table *)rd_rn->info;
+ if (table == NULL)
+ return;
+
+ /* Display all prefixes with this RD. */
+ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ if (type &&
+ evp->prefix.route_type != type)
+ continue;
+
+ if (rn->info)
+ {
+ /* RD header and legend - once overall. */
+ if (rd_header)
+ {
+ vty_out (vty, "EVPN type-2 prefix: [2]:[ESI]:[EthTag]:[MAClen]:"
+ "[MAC]\n");
+ vty_out (vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:"
+ "[OrigIP]\n\n");
+ rd_header = 0;
+ }
+
+ /* Prefix and num paths displayed once per prefix. */
+ route_vty_out_detail_header (vty, bgp, rn, prd, afi, safi, NULL);
+
+ prefix_cnt++;
+ }
+
+ /* Display each path for this prefix. */
+ for (ri = rn->info; ri; ri = ri->next)
+ {
+ route_vty_out_detail (vty, bgp, &rn->p, ri, afi, safi, NULL);
+ path_cnt++;
+ }
+ }
+
+ if (prefix_cnt == 0)
+ vty_out (vty, "No prefixes exist with this RD%s\n",
+ type ? " (of requested type)" : "");
+ else
+ vty_out (vty, "\nDisplayed %u prefixes (%u paths) with this RD%s\n",
+ prefix_cnt, path_cnt,
+ type ? " (of requested type)" : "");
+}
+
+/*
+ * Display BGP EVPN routing table - all routes (vty handler).
+ * If 'type' is non-zero, only routes matching that type are shown.
+ */
+static void
+evpn_show_all_routes (struct vty *vty, struct bgp *bgp, int type)
+{
+ struct bgp_node *rd_rn;
+ struct bgp_table *table;
+ struct bgp_node *rn;
+ struct bgp_info *ri;
+ int header = 1;
+ int rd_header;
+ afi_t afi;
+ safi_t safi;
+ u_int32_t prefix_cnt, path_cnt;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+ prefix_cnt = path_cnt = 0;
+
+ /* EVPN routing table is a 2-level table with the first level being
+ * the RD.
+ */
+ for (rd_rn = bgp_table_top (bgp->rib[afi][safi]); rd_rn;
+ rd_rn = bgp_route_next (rd_rn))
+ {
+ table = (struct bgp_table *)rd_rn->info;
+ if (table == NULL)
+ continue;
+
+ rd_header = 1;
+
+ /* Display all prefixes for an RD */
+ for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
+ {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ if (type &&
+ evp->prefix.route_type != type)
+ continue;
+
+ if (rn->info)
+ {
+ /* Overall header/legend displayed once. */
+ if (header)
+ {
+ bgp_evpn_show_route_header (vty, bgp);
+ header = 0;
+ }
+
+ /* RD header - per RD. */
+ if (rd_header)
+ {
+ bgp_evpn_show_route_rd_header (vty, rd_rn);
+ rd_header = 0;
+ }
+
+ prefix_cnt++;
+ }
+
+ /* For EVPN, the prefix is displayed for each path (to fit in
+ * with code that already exists).
+ */
+ for (ri = rn->info; ri; ri = ri->next)
+ {
+ path_cnt++;
+ route_vty_out (vty, &rn->p, ri, 0, SAFI_EVPN, NULL);
+ }
+ }
+ }
+
+ if (prefix_cnt == 0)
+ vty_out (vty, "No EVPN prefixes %sexist\n",
+ type ? "(of requested type) " : "");
+ else
+ vty_out (vty, "\nDisplayed %u prefixes (%u paths)%s\n",
+ prefix_cnt, path_cnt,
+ type ? " (of requested type)" : "");
+}
+
+/*
+ * Display specified VNI (vty handler)
+ */
+static void
+evpn_show_vni (struct vty *vty, struct bgp *bgp, vni_t vni)
+{
+ struct bgpevpn *vpn;
+
+ vpn = bgp_evpn_lookup_vni (bgp, vni);
+ if (!vpn)
+ {
+ vty_out (vty, "VNI not found\n");
+ return;
+ }
+
+ display_vni (vty, vpn);
+}
+
+/*
+ * Display a VNI (upon user query).
+ */
+static void
+evpn_show_all_vnis (struct vty *vty, struct bgp *bgp)
+{
+ u_int32_t num_vnis;
+
+ num_vnis = hashcount(bgp->vnihash);
+ if (!num_vnis)
+ return;
+ vty_out(vty, "Number of VNIs: %u\n", num_vnis);
+ vty_out(vty, "Flags: * - Kernel \n");
+ vty_out(vty, " %-10s %-15s %-21s %-25s %-25s\n",
+ "VNI", "Orig IP", "RD", "Import RT", "Export RT");
+ hash_iterate (bgp->vnihash,
+ (void (*) (struct hash_backet *, void *))
+ show_vni_entry, vty);
+}
+
+/*
+ * EVPN (VNI advertisement) enabled. Register with zebra.
+ */
+static void
+evpn_set_advertise_all_vni (struct bgp *bgp)
+{
+ bgp->advertise_all_vni = 1;
+ bgp_zebra_advertise_all_vni (bgp, bgp->advertise_all_vni);
+}
+
+/*
+ * EVPN (VNI advertisement) disabled. De-register with zebra. Cleanup VNI
+ * cache, EVPN routes (delete and withdraw from peers).
+ */
+static void
+evpn_unset_advertise_all_vni (struct bgp *bgp)
+{
+ bgp->advertise_all_vni = 0;
+ bgp_zebra_advertise_all_vni (bgp, bgp->advertise_all_vni);
+ bgp_evpn_cleanup_on_disable (bgp);
+}
+#endif /* HAVE_CUMULUS */
+
+static void
+write_vni_config (struct vty *vty, struct bgpevpn *vpn, int *write)
+{
+ char buf1[INET6_ADDRSTRLEN];
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ if (is_vni_configured (vpn))
+ {
+ bgp_config_write_family_header (vty, afi, safi, write);
+ vty_out (vty, " vni %d\n", vpn->vni);
+ if (is_rd_configured (vpn))
+ vty_out (vty, " rd %s\n",
+ prefix_rd2str (&vpn->prd, buf1, RD_ADDRSTRLEN));
+
+ if (is_import_rt_configured (vpn))
+ {
+ for (ALL_LIST_ELEMENTS (vpn->import_rtl, node, nnode, ecom))
+ {
+ ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out (vty, " route-target import %s\n", ecom_str);
+ XFREE (MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
+ if (is_export_rt_configured (vpn))
+ {
+ for (ALL_LIST_ELEMENTS (vpn->export_rtl, node, nnode, ecom))
+ {
+ ecom_str = ecommunity_ecom2str (ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out (vty, " route-target export %s\n", ecom_str);
+ XFREE (MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
+ vty_out (vty, " exit-vni\n");
+ }
+}
+
+static void
+write_vni_config_for_entry (struct hash_backet *backet,
+ struct evpn_config_write *cfg)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *) backet->data;
+ write_vni_config (cfg->vty, vpn, &cfg->write);
+}
+
+#if defined (HAVE_CUMULUS)
+DEFUN (bgp_evpn_advertise_all_vni,
+ bgp_evpn_advertise_all_vni_cmd,
+ "advertise-all-vni",
+ "Advertise All local VNIs\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+ evpn_set_advertise_all_vni (bgp);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_all_vni,
+ no_bgp_evpn_advertise_all_vni_cmd,
+ "no advertise-all-vni",
+ NO_STR
+ "Advertise All local VNIs\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+ evpn_unset_advertise_all_vni (bgp);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_evpn_vni,
+ show_bgp_evpn_vni_cmd,
+ "show bgp evpn vni",
+ SHOW_STR
+ BGP_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;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ vni = strtoul(argv[4]->arg, NULL, 10);
+
+ 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]",
+ SHOW_STR
+ BGP_STR
+ "EVPN\n"
+ "Summary of BGP neighbor status\n"
+ "JavaScript Object Notation\n")
+{
+ 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>]",
+ SHOW_STR
+ BGP_STR
+ "Address Family Modifier\n"
+ "Display EVPN route information\n"
+ "Specify Route type\n"
+ "MAC-IP (Type-2) route\n"
+ "Multicast (Type-3) route\n")
+{
+ struct bgp *bgp;
+ int type = 0;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (argc == 6)
+ {
+ if (strncmp (argv[5]->arg, "ma", 2) == 0)
+ type = BGP_EVPN_MAC_IP_ROUTE;
+ else if (strncmp (argv[5]->arg, "mu", 2) == 0)
+ type = BGP_EVPN_IMET_ROUTE;
+ else
+ return CMD_WARNING;
+ }
+
+ evpn_show_all_routes (vty, bgp, type);
+ 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>]",
+ SHOW_STR
+ BGP_STR
+ "Address Family Modifier\n"
+ "Display 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")
+{
+ struct bgp *bgp;
+ int ret;
+ struct prefix_rd prd;
+ int type = 0;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd (argv[5]->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)
+ type = BGP_EVPN_MAC_IP_ROUTE;
+ else if (strncmp (argv[7]->arg, "mu", 2) == 0)
+ type = BGP_EVPN_IMET_ROUTE;
+ else
+ return CMD_WARNING;
+ }
+
+ evpn_show_route_rd (vty, bgp, &prd, type);
+ 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]",
+ SHOW_STR
+ BGP_STR
+ "Address Family Modifier\n"
+ "Display 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")
+{
+ struct bgp *bgp;
+ int ret;
+ struct prefix_rd prd;
+ struct ethaddr mac;
+ struct ipaddr ip;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd (argv[5]->arg, &prd);
+ if (! ret)
+ {
+ vty_out (vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ if (!prefix_str2mac (argv[7]->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)
+ {
+ vty_out (vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
+ }
+
+ evpn_show_route_rd_macip (vty, bgp, &prd, &mac, &ip);
+ 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>]",
+ SHOW_STR
+ BGP_STR
+ "Address Family Modifier\n"
+ "Display 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")
+{
+ vni_t vni;
+ struct bgp *bgp;
+ struct in_addr vtep_ip;
+ int type = 0;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ vtep_ip.s_addr = 0;
+
+ vni = strtoul(argv[5]->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)
+ type = BGP_EVPN_MAC_IP_ROUTE;
+ else if (strncmp (argv[7]->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))
+ {
+ vty_out (vty, "%% Malformed VTEP IP address\n");
+ return CMD_WARNING;
+ }
+ }
+ else
+ return CMD_WARNING;
+ }
+
+ evpn_show_routes_vni (vty, bgp, vni, type, vtep_ip);
+ 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]",
+ SHOW_STR
+ BGP_STR
+ "Address Family Modifier\n"
+ "Display 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")
+{
+ vni_t vni;
+ struct bgp *bgp;
+ struct ethaddr mac;
+ struct ipaddr ip;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ vni = strtoul(argv[5]->arg, NULL, 10);
+ if (!prefix_str2mac (argv[7]->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)
+ {
+ vty_out (vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
+ }
+
+ evpn_show_route_vni_macip (vty, bgp, vni, &mac, &ip);
+ 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",
+ SHOW_STR
+ BGP_STR
+ "Address Family Modifier\n"
+ "Display EVPN route information\n"
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ "Multicast (Type-3) route\n"
+ "Originating Router IP address\n")
+{
+ vni_t vni;
+ struct bgp *bgp;
+ int ret;
+ struct in_addr orig_ip;
+
+ 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 (!ret)
+ {
+ vty_out (vty, "%% Malformed Originating Router IP address\n");
+ return CMD_WARNING;
+ }
+
+ evpn_show_route_vni_multicast (vty, bgp, vni, orig_ip);
+ 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]",
+ SHOW_STR
+ BGP_STR
+ "Address Family Modifier\n"
+ "Display EVPN route information\n"
+ "VXLAN Network Identifier\n"
+ "All VNIs\n"
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n")
+{
+ struct bgp *bgp;
+ struct in_addr vtep_ip;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ vtep_ip.s_addr = 0;
+ if (argc == 8 && argv[7]->arg)
+ {
+ if (!inet_aton (argv[7]->arg, &vtep_ip))
+ {
+ vty_out (vty, "%% Malformed VTEP IP address\n");
+ return CMD_WARNING;
+ }
+ }
+
+ evpn_show_routes_vni_all (vty, bgp, vtep_ip);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_evpn_import_rt,
+ show_bgp_evpn_import_rt_cmd,
+ "show bgp evpn import-rt",
+ SHOW_STR
+ BGP_STR
+ "Address family modifier\n"
+ "Show import route target\n")
+{
+ struct bgp *bgp;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_WARNING;
+
+ evpn_show_import_rts (vty, bgp);
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (bgp_evpn_vni,
+ bgp_evpn_vni_cmd,
+ "vni (1-16777215)",
+ "VXLAN Network Identifier\n"
+ "VNI number\n")
+{
+ vni_t vni;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct bgpevpn *vpn;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ vni = strtoul(argv[1]->arg, NULL, 10);
+
+ /* Create VNI, or mark as configured. */
+ vpn = evpn_create_update_vni (bgp, vni);
+ if (!vpn)
+ {
+ vty_out (vty, "%% Failed to create VNI \n");
+ return CMD_WARNING;
+ }
+
+ VTY_PUSH_CONTEXT_SUB (BGP_EVPN_VNI_NODE, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni,
+ no_bgp_evpn_vni_cmd,
+ "no vni (1-16777215)",
+ NO_STR
+ "VXLAN Network Identifier\n"
+ "VNI number\n")
+{
+ vni_t vni;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct bgpevpn *vpn;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ vni = strtoul(argv[2]->arg, NULL, 10);
+
+ /* Check if we should disallow. */
+ vpn = bgp_evpn_lookup_vni (bgp, vni);
+ if (!vpn)
+ {
+ vty_out (vty, "%% Specified VNI does not exist\n");
+ return CMD_WARNING;
+ }
+ if (!is_vni_configured (vpn))
+ {
+ vty_out (vty, "%% Specified VNI is not configured\n");
+ return CMD_WARNING;
+ }
+
+ evpn_delete_vni (bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (exit_vni,
+ exit_vni_cmd,
+ "exit-vni",
+ "Exit from VNI mode\n")
+{
+ if (vty->node == BGP_EVPN_VNI_NODE)
+ vty->node = BGP_EVPN_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_vni_rd,
+ bgp_evpn_vni_rd_cmd,
+ "rd ASN:nn_or_IP-address:nn",
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ struct prefix_rd prd;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int ret;
+
+ if (!bgp || !vpn)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd (argv[1]->arg, &prd);
+ if (! ret)
+ {
+ vty_out (vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* If same as existing value, there is nothing more to do. */
+ if (bgp_evpn_rd_matches_existing (vpn, &prd))
+ return CMD_SUCCESS;
+
+ /* Configure or update the RD. */
+ evpn_configure_rd (bgp, vpn, &prd);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni_rd,
+ no_bgp_evpn_vni_rd_cmd,
+ "no rd ASN:nn_or_IP-address:nn",
+ NO_STR
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ struct prefix_rd prd;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int ret;
+
+ if (!bgp || !vpn)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd (argv[2]->arg, &prd);
+ if (! ret)
+ {
+ vty_out (vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* Check if we should disallow. */
+ if (!is_rd_configured (vpn))
+ {
+ vty_out (vty, "%% RD is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+
+ if (!bgp_evpn_rd_matches_existing(vpn, &prd))
+ {
+ vty_out (vty, "%% RD specified does not match configuration for this VNI\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_rd (bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni_rd_without_val,
+ no_bgp_evpn_vni_rd_without_val_cmd,
+ "no rd",
+ NO_STR
+ "Route Distinguisher\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+
+ if (!bgp || !vpn)
+ return CMD_WARNING;
+
+ /* Check if we should disallow. */
+ if (!is_rd_configured (vpn))
+ {
+ vty_out (vty, "%% RD is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_rd (bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+/*
+ * Loop over all extended-communities in the route-target list rtl and
+ * return 1 if we find ecomtarget
+ */
+static int
+bgp_evpn_rt_matches_existing (struct list *rtl,
+ struct ecommunity *ecomtarget)
+{
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS (rtl, node, nnode, ecom))
+ {
+ if (ecommunity_match (ecom, ecomtarget))
+ return 1;
+ }
+
+ return 0;
+}
+
+
+DEFUN (bgp_evpn_vni_rt,
+ bgp_evpn_vni_rt_cmd,
+ "route-target <both|import|export> RT",
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int rt_type;
+ struct ecommunity *ecomadd = NULL;
+
+ if (!bgp || !vpn)
+ return CMD_WARNING;
+
+ if (!strcmp (argv[1]->arg, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp (argv[1]->arg, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp (argv[1]->arg, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else
+ {
+ vty_out (vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ /* Add/update the import route-target */
+ 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;
+ }
+
+ /* Do nothing if we already have this import route-target */
+ if (! bgp_evpn_rt_matches_existing (vpn->import_rtl, ecomadd))
+ evpn_configure_import_rt (bgp, vpn, ecomadd);
+ }
+
+ /* Add/update the export route-target */
+ 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;
+ }
+
+ /* Do nothing if we already have this export route-target */
+ if (! bgp_evpn_rt_matches_existing (vpn->export_rtl, ecomadd))
+ evpn_configure_export_rt (bgp, vpn, ecomadd);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni_rt,
+ no_bgp_evpn_vni_rt_cmd,
+ "no route-target <both|import|export> RT",
+ NO_STR
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int rt_type, found_ecomdel;
+ struct ecommunity *ecomdel = NULL;
+
+ if (!bgp || !vpn)
+ return CMD_WARNING;
+
+ if (!strcmp (argv[2]->arg, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp (argv[2]->arg, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp (argv[2]->arg, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else
+ {
+ vty_out (vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ /* The user did "no route-target import", check to see if there are any
+ * import route-targets configured. */
+ if (rt_type == RT_TYPE_IMPORT)
+ {
+ if (!is_import_rt_configured (vpn))
+ {
+ vty_out (vty, "%% Import RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ }
+ else if (rt_type == RT_TYPE_EXPORT)
+ {
+ if (!is_export_rt_configured (vpn))
+ {
+ vty_out (vty, "%% Export RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ }
+ else if (rt_type == RT_TYPE_BOTH)
+ {
+ if (!is_import_rt_configured (vpn) && !is_export_rt_configured (vpn))
+ {
+ vty_out (vty, "%% Import/Export RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ }
+
+ 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;
+ }
+
+ if (rt_type == RT_TYPE_IMPORT)
+ {
+ if (!bgp_evpn_rt_matches_existing (vpn->import_rtl, ecomdel))
+ {
+ vty_out (vty, "%% RT specified does not match configuration for this VNI\n");
+ return CMD_WARNING;
+ }
+ evpn_unconfigure_import_rt (bgp, vpn, ecomdel);
+ }
+ else if (rt_type == RT_TYPE_EXPORT)
+ {
+ if (!bgp_evpn_rt_matches_existing (vpn->export_rtl, ecomdel))
+ {
+ vty_out (vty, "%% RT specified does not match configuration for this VNI\n");
+ return CMD_WARNING;
+ }
+ evpn_unconfigure_export_rt (bgp, vpn, ecomdel);
+ }
+ else if (rt_type == RT_TYPE_BOTH)
+ {
+ found_ecomdel = 0;
+
+ if (bgp_evpn_rt_matches_existing (vpn->import_rtl, ecomdel))
+ {
+ evpn_unconfigure_import_rt (bgp, vpn, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (bgp_evpn_rt_matches_existing (vpn->export_rtl, ecomdel))
+ {
+ evpn_unconfigure_export_rt (bgp, vpn, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (! found_ecomdel)
+ {
+ vty_out (vty, "%% RT specified does not match configuration for this VNI\n");
+ return CMD_WARNING;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni_rt_without_val,
+ no_bgp_evpn_vni_rt_without_val_cmd,
+ "no route-target <import|export>",
+ NO_STR
+ "Route Target\n"
+ "import\n"
+ "export\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int rt_type;
+
+ if (!bgp || !vpn)
+ return CMD_WARNING;
+
+ if (!strcmp (argv[2]->arg, "import"))
+ {
+ rt_type = RT_TYPE_IMPORT;
+ }
+ else if (!strcmp (argv[2]->arg, "export"))
+ {
+ rt_type = RT_TYPE_EXPORT;
+ }
+ else
+ {
+ vty_out (vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ /* Check if we should disallow. */
+ if (rt_type == RT_TYPE_IMPORT)
+ {
+ if (!is_import_rt_configured (vpn))
+ {
+ vty_out (vty, "%% Import RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ }
+ else
+ {
+ if (!is_export_rt_configured (vpn))
+ {
+ vty_out (vty, "%% Export RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ }
+
+ /* Unconfigure the RT. */
+ if (rt_type == RT_TYPE_IMPORT)
+ evpn_unconfigure_import_rt (bgp, vpn, NULL);
+ else
+ evpn_unconfigure_export_rt (bgp, vpn, NULL);
+ return CMD_SUCCESS;
+}
+#endif
+/*
+ * Output EVPN configuration information.
+ */
+void
+bgp_config_write_evpn_info (struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, int *write)
+{
+ struct evpn_config_write cfg;
+
+ if (bgp->vnihash)
+ {
+ cfg.write = *write;
+ cfg.vty = vty;
+ hash_iterate (bgp->vnihash,
+ (void (*) (struct hash_backet *, void *))
+ write_vni_config_for_entry, &cfg);
+ *write = cfg.write;
+ }
+
+ if (bgp->advertise_all_vni)
+ {
+ bgp_config_write_family_header (vty, afi, safi, write);
+ vty_out (vty, " advertise-all-vni\n");
+ }
+}
+
void bgp_ethernetvpn_init(void)
{
- install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_cmd);
- install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_cmd);
- install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_tags_cmd);
- install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_tags_cmd);
- install_element(VIEW_NODE,
- &show_ip_bgp_l2vpn_evpn_all_neighbor_routes_cmd);
- install_element(VIEW_NODE,
- &show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd);
- install_element(VIEW_NODE,
- &show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes_cmd);
- install_element(VIEW_NODE,
- &show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd);
- install_element(VIEW_NODE, &show_ip_bgp_evpn_rd_overlay_cmd);
- install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_overlay_cmd);
- install_element(BGP_EVPN_NODE, &no_evpnrt5_network_cmd);
- install_element(BGP_EVPN_NODE, &evpnrt5_network_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_tags_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_tags_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_neighbor_routes_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_bgp_l2vpn_evpn_all_neighbor_advertised_routes_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_evpn_rd_overlay_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_overlay_cmd);
+ install_element(BGP_EVPN_NODE, &no_evpnrt5_network_cmd);
+ install_element(BGP_EVPN_NODE, &evpnrt5_network_cmd);
+#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);
+
+ /* "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);
+ install_element (VIEW_NODE, &show_bgp_evpn_route_rd_macip_cmd);
+ install_element (VIEW_NODE, &show_bgp_evpn_route_vni_cmd);
+ install_element (VIEW_NODE, &show_bgp_evpn_route_vni_multicast_cmd);
+ install_element (VIEW_NODE, &show_bgp_evpn_route_vni_macip_cmd);
+ install_element (VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd);
+ install_element (VIEW_NODE, &show_bgp_evpn_import_rt_cmd);
+
+ install_element (BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
+ install_element (BGP_EVPN_NODE, &no_bgp_evpn_vni_cmd);
+ install_element (BGP_EVPN_VNI_NODE, &exit_vni_cmd);
+ install_element (BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rd_cmd);
+ install_element (BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rd_cmd);
+ install_element (BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rd_without_val_cmd);
+ 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);
+#endif
}
diff --git a/bgpd/bgp_evpn_vty.h b/bgpd/bgp_evpn_vty.h
index fe01e84e2..4c8f63c2f 100644
--- a/bgpd/bgp_evpn_vty.h
+++ b/bgpd/bgp_evpn_vty.h
@@ -21,6 +21,9 @@
#ifndef _FRR_BGP_EVPN_VTY_H
#define _FRR_BGP_EVPN_VTY_H
+extern void
+bgp_config_write_evpn_info (struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, int *write);
extern void bgp_ethernetvpn_init(void);
#define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n"
diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c
index fc0654f18..27533a581 100644
--- a/bgpd/bgp_label.c
+++ b/bgpd/bgp_label.c
@@ -151,12 +151,10 @@ bgp_reg_dereg_for_label (struct bgp_node *rn, struct bgp_info *ri,
assert (ri);
if (ri->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_PREFIX_SID))
{
- assert (ri->attr->extra);
-
- if (ri->attr->extra->label_index != BGP_INVALID_LABEL_INDEX)
+ if (ri->attr->label_index != BGP_INVALID_LABEL_INDEX)
{
flags |= ZEBRA_FEC_REGISTER_LABEL_INDEX;
- stream_putl (s, ri->attr->extra->label_index);
+ stream_putl (s, ri->attr->label_index);
}
}
SET_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL);
diff --git a/bgpd/bgp_label.h b/bgpd/bgp_label.h
index c98f4531f..a0994d68a 100644
--- a/bgpd/bgp_label.h
+++ b/bgpd/bgp_label.h
@@ -41,6 +41,9 @@ extern int bgp_nlri_parse_label (struct peer *peer, struct attr *attr,
static inline int
bgp_labeled_safi (safi_t safi)
{
+ /* NOTE: This API really says a label (tag) MAY be present. Not all EVPN
+ * routes will have a label.
+ */
if ((safi == SAFI_LABELED_UNICAST) || (safi == SAFI_MPLS_VPN) ||
(safi == SAFI_EVPN))
return 1;
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
index c457f4b3e..6da9ff868 100644
--- a/bgpd/bgp_memory.c
+++ b/bgpd/bgp_memory.c
@@ -43,7 +43,6 @@ DEFINE_MTYPE(BGPD, BGP_UPDGRP, "BGP update group")
DEFINE_MTYPE(BGPD, BGP_UPD_SUBGRP, "BGP update subgroup")
DEFINE_MTYPE(BGPD, BGP_PACKET, "BGP packet")
DEFINE_MTYPE(BGPD, ATTR, "BGP attribute")
-DEFINE_MTYPE(BGPD, ATTR_EXTRA, "BGP extra attributes")
DEFINE_MTYPE(BGPD, AS_PATH, "BGP aspath")
DEFINE_MTYPE(BGPD, AS_SEG, "BGP aspath seg")
DEFINE_MTYPE(BGPD, AS_SEG_DATA, "BGP aspath segment data")
@@ -114,3 +113,7 @@ DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS_VALUE, "BGP TEA Options Value")
DEFINE_MTYPE(BGPD, LCOMMUNITY, "Large Community")
DEFINE_MTYPE(BGPD, LCOMMUNITY_STR, "Large Community display string")
DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value")
+
+DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information")
+DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT")
+DEFINE_MTYPE(BGPD, BGP_EVPN_MACIP, "BGP EVPN MAC IP")
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
index 454092cef..152cfaeaf 100644
--- a/bgpd/bgp_memory.h
+++ b/bgpd/bgp_memory.h
@@ -39,7 +39,6 @@ DECLARE_MTYPE(BGP_UPDGRP)
DECLARE_MTYPE(BGP_UPD_SUBGRP)
DECLARE_MTYPE(BGP_PACKET)
DECLARE_MTYPE(ATTR)
-DECLARE_MTYPE(ATTR_EXTRA)
DECLARE_MTYPE(AS_PATH)
DECLARE_MTYPE(AS_SEG)
DECLARE_MTYPE(AS_SEG_DATA)
@@ -110,4 +109,8 @@ DECLARE_MTYPE(BGP_TEA_OPTIONS_VALUE)
DECLARE_MTYPE(LCOMMUNITY)
DECLARE_MTYPE(LCOMMUNITY_STR)
DECLARE_MTYPE(LCOMMUNITY_VAL)
+
+DECLARE_MTYPE(BGP_EVPN)
+DECLARE_MTYPE(BGP_EVPN_IMPORT_RT)
+DECLARE_MTYPE(BGP_EVPN_MACIP)
#endif /* _QUAGGA_BGP_MEMORY_H */
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c
index 34690ac77..7ea5c9e77 100644
--- a/bgpd/bgp_mpath.c
+++ b/bgpd/bgp_mpath.c
@@ -106,35 +106,31 @@ bgp_maximum_paths_unset (struct bgp *bgp, afi_t afi, safi_t safi,
int
bgp_info_nexthop_cmp (struct bgp_info *bi1, struct bgp_info *bi2)
{
- struct attr_extra *ae1, *ae2;
int compare;
- ae1 = bi1->attr->extra;
- ae2 = bi2->attr->extra;
-
compare = IPV4_ADDR_CMP (&bi1->attr->nexthop, &bi2->attr->nexthop);
- if (!compare && ae1 && ae2)
+ if (!compare)
{
- if (ae1->mp_nexthop_len == ae2->mp_nexthop_len)
+ if (bi1->attr->mp_nexthop_len == bi2->attr->mp_nexthop_len)
{
- switch (ae1->mp_nexthop_len)
+ switch (bi1->attr->mp_nexthop_len)
{
case BGP_ATTR_NHLEN_IPV4:
case BGP_ATTR_NHLEN_VPNV4:
- compare = IPV4_ADDR_CMP (&ae1->mp_nexthop_global_in,
- &ae2->mp_nexthop_global_in);
+ compare = IPV4_ADDR_CMP (&bi1->attr->mp_nexthop_global_in,
+ &bi2->attr->mp_nexthop_global_in);
break;
case BGP_ATTR_NHLEN_IPV6_GLOBAL:
case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
- compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
- &ae2->mp_nexthop_global);
+ compare = IPV6_ADDR_CMP (&bi1->attr->mp_nexthop_global,
+ &bi2->attr->mp_nexthop_global);
break;
case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
- compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
- &ae2->mp_nexthop_global);
+ compare = IPV6_ADDR_CMP (&bi1->attr->mp_nexthop_global,
+ &bi2->attr->mp_nexthop_global);
if (!compare)
- compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_local,
- &ae2->mp_nexthop_local);
+ compare = IPV6_ADDR_CMP (&bi1->attr->mp_nexthop_local,
+ &bi2->attr->mp_nexthop_local);
break;
}
}
@@ -142,14 +138,14 @@ bgp_info_nexthop_cmp (struct bgp_info *bi1, struct bgp_info *bi2)
/* This can happen if one IPv6 peer sends you global and link-local
* nexthops but another IPv6 peer only sends you global
*/
- else if (ae1->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL ||
- ae1->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ else if (bi1->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL ||
+ bi1->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
{
- compare = IPV6_ADDR_CMP (&ae1->mp_nexthop_global,
- &ae2->mp_nexthop_global);
+ compare = IPV6_ADDR_CMP (&bi1->attr->mp_nexthop_global,
+ &bi2->attr->mp_nexthop_global);
if (!compare)
{
- if (ae1->mp_nexthop_len < ae2->mp_nexthop_len)
+ if (bi1->attr->mp_nexthop_len < bi2->attr->mp_nexthop_len)
compare = -1;
else
compare = 1;
@@ -663,7 +659,6 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
struct community *community, *commerge;
struct ecommunity *ecomm, *ecommerge;
struct lcommunity *lcomm, *lcommerge;
- struct attr_extra *ae;
struct attr attr = { 0 };
if (old_best && (old_best != new_best) &&
@@ -697,9 +692,8 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
aspath = aspath_dup (attr.aspath);
origin = attr.origin;
community = attr.community ? community_dup (attr.community) : NULL;
- ae = attr.extra;
- ecomm = (ae && ae->ecommunity) ? ecommunity_dup (ae->ecommunity) : NULL;
- lcomm = (ae && ae->lcommunity) ? lcommunity_dup (ae->lcommunity) : NULL;
+ ecomm = (attr.ecommunity) ? ecommunity_dup (attr.ecommunity) : NULL;
+ lcomm = (attr.lcommunity) ? lcommunity_dup (attr.lcommunity) : NULL;
for (mpinfo = bgp_info_mpath_first (new_best); mpinfo;
mpinfo = bgp_info_mpath_next (mpinfo))
@@ -723,28 +717,27 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
community = community_dup (mpinfo->attr->community);
}
- ae = mpinfo->attr->extra;
- if (ae && ae->ecommunity)
+ if (mpinfo->attr->ecommunity)
{
if (ecomm)
{
- ecommerge = ecommunity_merge (ecomm, ae->ecommunity);
+ ecommerge = ecommunity_merge (ecomm, mpinfo->attr->ecommunity);
ecomm = ecommunity_uniq_sort (ecommerge);
ecommunity_free (&ecommerge);
}
else
- ecomm = ecommunity_dup (ae->ecommunity);
+ ecomm = ecommunity_dup (mpinfo->attr->ecommunity);
}
- if (ae && ae->lcommunity)
+ if (mpinfo->attr->lcommunity)
{
if (lcomm)
{
- lcommerge = lcommunity_merge (lcomm, ae->lcommunity);
+ lcommerge = lcommunity_merge (lcomm, mpinfo->attr->lcommunity);
lcomm = lcommunity_uniq_sort (lcommerge);
lcommunity_free (&lcommerge);
}
else
- lcomm = lcommunity_dup (ae->lcommunity);
+ lcomm = lcommunity_dup (mpinfo->attr->lcommunity);
}
}
@@ -757,21 +750,18 @@ bgp_info_mpath_aggregate_update (struct bgp_info *new_best,
}
if (ecomm)
{
- ae = bgp_attr_extra_get (&attr);
- ae->ecommunity = ecomm;
+ attr.ecommunity = ecomm;
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
}
/* Zap multipath attr nexthop so we set nexthop to self */
attr.nexthop.s_addr = 0;
- if (attr.extra)
- memset (&attr.extra->mp_nexthop_global, 0, sizeof (struct in6_addr));
+ memset (&attr.mp_nexthop_global, 0, sizeof (struct in6_addr));
/* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */
}
new_attr = bgp_attr_intern (&attr);
- bgp_attr_extra_free (&attr);
if (new_attr != bgp_info_mpath_attr (new_best))
{
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
index c1db9fed2..3a8398d8a 100644
--- a/bgpd/bgp_mplsvpn.c
+++ b/bgpd/bgp_mplsvpn.c
@@ -62,31 +62,6 @@ argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc, int *index, afi_t *
return ret;
}
-u_int16_t
-decode_rd_type (u_char *pnt)
-{
- u_int16_t v;
-
- v = ((u_int16_t) *pnt++ << 8);
-#if ENABLE_BGP_VNC
- /*
- * VNC L2 stores LHI in lower byte, so omit it
- */
- if (v != RD_TYPE_VNC_ETH)
- v |= (u_int16_t) *pnt;
-#else /* duplicate code for clarity */
- v |= (u_int16_t) *pnt;
-#endif
-
- return v;
-}
-
-void
-encode_rd_type (u_int16_t v, u_char *pnt)
-{
- *((u_int16_t *)pnt) = htons(v);
-}
-
u_int32_t
decode_label (mpls_label_t *label_pnt)
{
@@ -111,54 +86,6 @@ encode_label(mpls_label_t label,
*pnt++ = ((label<<4)+1) & 0xff; /* S=1 */
}
-/* type == RD_TYPE_AS */
-void
-decode_rd_as (u_char *pnt, struct rd_as *rd_as)
-{
- rd_as->as = (u_int16_t) *pnt++ << 8;
- rd_as->as |= (u_int16_t) *pnt++;
-
- rd_as->val = ((u_int32_t) *pnt++ << 24);
- rd_as->val |= ((u_int32_t) *pnt++ << 16);
- rd_as->val |= ((u_int32_t) *pnt++ << 8);
- rd_as->val |= (u_int32_t) *pnt;
-}
-
-/* type == RD_TYPE_AS4 */
-void
-decode_rd_as4 (u_char *pnt, struct rd_as *rd_as)
-{
- rd_as->as = (u_int32_t) *pnt++ << 24;
- rd_as->as |= (u_int32_t) *pnt++ << 16;
- rd_as->as |= (u_int32_t) *pnt++ << 8;
- rd_as->as |= (u_int32_t) *pnt++;
-
- rd_as->val = ((u_int16_t) *pnt++ << 8);
- rd_as->val |= (u_int16_t) *pnt;
-}
-
-/* type == RD_TYPE_IP */
-void
-decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip)
-{
- memcpy (&rd_ip->ip, pnt, 4);
- pnt += 4;
-
- rd_ip->val = ((u_int16_t) *pnt++ << 8);
- rd_ip->val |= (u_int16_t) *pnt;
-}
-
-#if ENABLE_BGP_VNC
-/* type == RD_TYPE_VNC_ETH */
-void
-decode_rd_vnc_eth (u_char *pnt, struct rd_vnc_eth *rd_vnc_eth)
-{
- rd_vnc_eth->type = RD_TYPE_VNC_ETH;
- rd_vnc_eth->local_nve_id = pnt[1];
- memcpy (rd_vnc_eth->macaddr.octet, pnt + 2, ETHER_ADDR_LEN);
-}
-#endif
-
int
bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
struct bgp_nlri *packet)
@@ -313,129 +240,6 @@ bgp_nlri_parse_vpn (struct peer *peer, struct attr *attr,
#undef VPN_PREFIXLEN_MIN_BYTES
}
-int
-str2prefix_rd (const char *str, struct prefix_rd *prd)
-{
- int ret; /* ret of called functions */
- int lret; /* local ret, of this func */
- char *p;
- char *p2;
- struct stream *s = NULL;
- char *half = NULL;
- struct in_addr addr;
-
- s = stream_new (8);
-
- prd->family = AF_UNSPEC;
- prd->prefixlen = 64;
-
- lret = 0;
- p = strchr (str, ':');
- if (! p)
- goto out;
-
- if (! all_digit (p + 1))
- goto out;
-
- half = XMALLOC (MTYPE_TMP, (p - str) + 1);
- memcpy (half, str, (p - str));
- half[p - str] = '\0';
-
- p2 = strchr (str, '.');
-
- if (! p2)
- {
- unsigned long as_val;
-
- if (! all_digit (half))
- goto out;
-
- as_val = atol(half);
- if (as_val > 0xffff)
- {
- stream_putw (s, RD_TYPE_AS4);
- stream_putl (s, as_val);
- stream_putw (s, atol (p + 1));
- }
- else
- {
- stream_putw (s, RD_TYPE_AS);
- stream_putw (s, as_val);
- stream_putl (s, atol (p + 1));
- }
- }
- else
- {
- ret = inet_aton (half, &addr);
- if (! ret)
- goto out;
-
- stream_putw (s, RD_TYPE_IP);
- stream_put_in_addr (s, &addr);
- stream_putw (s, atol (p + 1));
- }
- memcpy (prd->val, s->data, 8);
- lret = 1;
-
-out:
- if (s)
- stream_free (s);
- if (half)
- XFREE(MTYPE_TMP, half);
- return lret;
-}
-
-char *
-prefix_rd2str (struct prefix_rd *prd, char *buf, size_t size)
-{
- u_char *pnt;
- u_int16_t type;
- struct rd_as rd_as;
- struct rd_ip rd_ip;
-
- if (size < RD_ADDRSTRLEN)
- return NULL;
-
- pnt = prd->val;
-
- type = decode_rd_type (pnt);
-
- if (type == RD_TYPE_AS)
- {
- decode_rd_as (pnt + 2, &rd_as);
- snprintf (buf, size, "%u:%d", rd_as.as, rd_as.val);
- return buf;
- }
- else if (type == RD_TYPE_AS4)
- {
- decode_rd_as4 (pnt + 2, &rd_as);
- snprintf (buf, size, "%u:%d", rd_as.as, rd_as.val);
- return buf;
- }
- else if (type == RD_TYPE_IP)
- {
- decode_rd_ip (pnt + 2, &rd_ip);
- snprintf (buf, size, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
- return buf;
- }
-#if ENABLE_BGP_VNC
- else if (type == RD_TYPE_VNC_ETH)
- {
- snprintf(buf, size, "LHI:%d, %02x:%02x:%02x:%02x:%02x:%02x",
- *(pnt+1), /* LHI */
- *(pnt+2), /* MAC[0] */
- *(pnt+3),
- *(pnt+4),
- *(pnt+5),
- *(pnt+6),
- *(pnt+7));
-
- return buf;
- }
-#endif
- return NULL;
-}
-
/* For testing purpose, static route of MPLS-VPN. */
DEFUN (vpnv4_network,
vpnv4_network_cmd,
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
index 98806abc3..8d4f5f806 100644
--- a/bgpd/bgp_mplsvpn.h
+++ b/bgpd/bgp_mplsvpn.h
@@ -1,7 +1,7 @@
/* MPLS-VPN
* Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
*
- * This file is part of GNU Zebra.
+ * This file is part of GxNU Zebra.
*
* GNU Zebra is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -22,15 +22,7 @@
#define _QUAGGA_BGP_MPLSVPN_H
#include "bgpd/bgp_route.h"
-
-#define RD_TYPE_AS 0
-#define RD_TYPE_IP 1
-#define RD_TYPE_AS4 2
-#if ENABLE_BGP_VNC
-#define RD_TYPE_VNC_ETH 0xff00 /* VNC L2VPN */
-#endif
-
-#define RD_ADDRSTRLEN 28
+#include "bgpd/bgp_rd.h"
#ifdef MPLS_LABEL_MAX
# undef MPLS_LABEL_MAX
@@ -74,44 +66,10 @@ typedef enum {
#define V4_HEADER_OVERLAY \
" Network Next Hop EthTag Overlay Index RouterMac\n"
-struct rd_as
-{
- u_int16_t type;
- as_t as;
- u_int32_t val;
-};
-
-struct rd_ip
-{
- u_int16_t type;
- struct in_addr ip;
- u_int16_t val;
-};
-
-#if ENABLE_BGP_VNC
-struct rd_vnc_eth
-{
- u_int16_t type;
- uint8_t local_nve_id;
- struct ethaddr macaddr;
-};
-#endif
-
-extern u_int16_t decode_rd_type (u_char *);
-extern void encode_rd_type (u_int16_t, u_char *);
extern void bgp_mplsvpn_init (void);
extern int bgp_nlri_parse_vpn (struct peer *, struct attr *, struct bgp_nlri *);
extern u_int32_t decode_label (mpls_label_t *);
extern void encode_label(mpls_label_t, mpls_label_t *);
-extern void decode_rd_as (u_char *, struct rd_as *);
-extern void decode_rd_as4 (u_char *, struct rd_as *);
-extern void decode_rd_ip (u_char *, struct rd_ip *);
-#if ENABLE_BGP_VNC
-extern void
-decode_rd_vnc_eth (u_char *pnt, struct rd_vnc_eth *rd_vnc_eth);
-#endif
-extern int str2prefix_rd (const char *, struct prefix_rd *);
-extern char *prefix_rd2str (struct prefix_rd *, char *, size_t);
extern int
argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc, int *index, afi_t *afi);
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
index 45ccb3743..148fba2d4 100644
--- a/bgpd/bgp_nexthop.c
+++ b/bgpd/bgp_nexthop.c
@@ -329,11 +329,11 @@ bgp_connected_delete (struct bgp *bgp, struct connected *ifc)
}
int
-bgp_nexthop_self (struct bgp *bgp, struct attr *attr)
+bgp_nexthop_self (struct bgp *bgp, struct in_addr nh_addr)
{
struct bgp_addr tmp, *addr;
- tmp.addr = attr->nexthop;
+ tmp.addr = nh_addr;
addr = hash_lookup (bgp->address_hash, &tmp);
if (addr)
diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h
index c5d9232e3..3aa20a9d5 100644
--- a/bgpd/bgp_nexthop.h
+++ b/bgpd/bgp_nexthop.h
@@ -74,7 +74,7 @@ extern void bgp_connected_add (struct bgp *bgp, struct connected *c);
extern void bgp_connected_delete (struct bgp *bgp, struct connected *c);
extern int bgp_multiaccess_check_v4 (struct in_addr, struct peer *);
extern int bgp_config_write_scan_time (struct vty *);
-extern int bgp_nexthop_self (struct bgp *, struct attr *);
+extern int bgp_nexthop_self (struct bgp *, struct in_addr);
extern struct bgp_nexthop_cache *bnc_new(void);
extern void bnc_free(struct bgp_nexthop_cache *bnc);
extern void bnc_nexthop_free(struct bgp_nexthop_cache *bnc);
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index 341bb0abb..3feb42246 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -532,8 +532,8 @@ make_prefix (int afi, struct bgp_info *ri, struct prefix *p)
break;
case AFI_IP6:
/* We don't register link local NH */
- if (ri->attr->extra->mp_nexthop_len != BGP_ATTR_NHLEN_IPV6_GLOBAL
- || IN6_IS_ADDR_LINKLOCAL (&ri->attr->extra->mp_nexthop_global))
+ if (ri->attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV6_GLOBAL
+ || IN6_IS_ADDR_LINKLOCAL (&ri->attr->mp_nexthop_global))
return -1;
p->family = AF_INET6;
@@ -545,7 +545,7 @@ make_prefix (int afi, struct bgp_info *ri, struct prefix *p)
}
else
{
- p->u.prefix6 = ri->attr->extra->mp_nexthop_global;
+ p->u.prefix6 = ri->attr->mp_nexthop_global;
p->prefixlen = IPV6_MAX_BITLEN;
}
break;
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
index 0c31d6e9f..2b48b34e1 100644
--- a/bgpd/bgp_packet.c
+++ b/bgpd/bgp_packet.c
@@ -1364,7 +1364,6 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
u_char *end;
struct stream *s;
struct attr attr;
- struct attr_extra extra;
bgp_size_t attribute_len;
bgp_size_t update_len;
bgp_size_t withdraw_len;
@@ -1389,11 +1388,9 @@ bgp_update_receive (struct peer *peer, bgp_size_t size)
/* Set initial values. */
memset (&attr, 0, sizeof (struct attr));
- memset (&extra, 0, sizeof (struct attr_extra));
- extra.label_index = BGP_INVALID_LABEL_INDEX;
- extra.label = MPLS_INVALID_LABEL;
+ attr.label_index = BGP_INVALID_LABEL_INDEX;
+ attr.label = MPLS_INVALID_LABEL;
memset (&nlris, 0, sizeof (nlris));
- attr.extra = &extra;
memset (peer->rcvd_attr_str, 0, BUFSIZ);
peer->rcvd_attr_printed = 0;
diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c
new file mode 100644
index 000000000..03257a292
--- /dev/null
+++ b/bgpd/bgp_rd.c
@@ -0,0 +1,232 @@
+/* BGP RD definitions for BGP-based VPNs (IP/EVPN)
+ * -- brought over from bgpd/bgp_mplsvpn.c
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "log.h"
+#include "prefix.h"
+#include "memory.h"
+#include "stream.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_rd.h"
+#include "bgpd/bgp_attr.h"
+
+#if ENABLE_BGP_VNC
+#include "bgpd/rfapi/rfapi_backend.h"
+#endif
+
+u_int16_t
+decode_rd_type (u_char *pnt)
+{
+ u_int16_t v;
+
+ v = ((u_int16_t) *pnt++ << 8);
+#if ENABLE_BGP_VNC
+ /*
+ * VNC L2 stores LHI in lower byte, so omit it
+ */
+ if (v != RD_TYPE_VNC_ETH)
+ v |= (u_int16_t) *pnt;
+#else /* duplicate code for clarity */
+ v |= (u_int16_t) *pnt;
+#endif
+ return v;
+}
+
+void
+encode_rd_type (u_int16_t v, u_char *pnt)
+{
+ *((u_int16_t *)pnt) = htons(v);
+}
+
+/* type == RD_TYPE_AS */
+void
+decode_rd_as (u_char *pnt, struct rd_as *rd_as)
+{
+ rd_as->as = (u_int16_t) *pnt++ << 8;
+ rd_as->as |= (u_int16_t) *pnt++;
+
+ rd_as->val = ((u_int32_t) *pnt++ << 24);
+ rd_as->val |= ((u_int32_t) *pnt++ << 16);
+ rd_as->val |= ((u_int32_t) *pnt++ << 8);
+ rd_as->val |= (u_int32_t) *pnt;
+}
+
+/* type == RD_TYPE_AS4 */
+void
+decode_rd_as4 (u_char *pnt, struct rd_as *rd_as)
+{
+ rd_as->as = (u_int32_t) *pnt++ << 24;
+ rd_as->as |= (u_int32_t) *pnt++ << 16;
+ rd_as->as |= (u_int32_t) *pnt++ << 8;
+ rd_as->as |= (u_int32_t) *pnt++;
+
+ rd_as->val = ((u_int16_t) *pnt++ << 8);
+ rd_as->val |= (u_int16_t) *pnt;
+}
+
+/* type == RD_TYPE_IP */
+void
+decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip)
+{
+ memcpy (&rd_ip->ip, pnt, 4);
+ pnt += 4;
+
+ rd_ip->val = ((u_int16_t) *pnt++ << 8);
+ rd_ip->val |= (u_int16_t) *pnt;
+}
+
+#if ENABLE_BGP_VNC
+/* type == RD_TYPE_VNC_ETH */
+void
+decode_rd_vnc_eth (u_char *pnt, struct rd_vnc_eth *rd_vnc_eth)
+{
+ rd_vnc_eth->type = RD_TYPE_VNC_ETH;
+ rd_vnc_eth->local_nve_id = pnt[1];
+ memcpy (rd_vnc_eth->macaddr.octet, pnt + 2, ETHER_ADDR_LEN);
+}
+#endif
+
+int
+str2prefix_rd (const char *str, struct prefix_rd *prd)
+{
+ int ret; /* ret of called functions */
+ int lret; /* local ret, of this func */
+ char *p;
+ char *p2;
+ struct stream *s = NULL;
+ char *half = NULL;
+ struct in_addr addr;
+
+ s = stream_new (8);
+
+ prd->family = AF_UNSPEC;
+ prd->prefixlen = 64;
+
+ lret = 0;
+ p = strchr (str, ':');
+ if (! p)
+ goto out;
+
+ if (! all_digit (p + 1))
+ goto out;
+
+ half = XMALLOC (MTYPE_TMP, (p - str) + 1);
+ memcpy (half, str, (p - str));
+ half[p - str] = '\0';
+
+ p2 = strchr (str, '.');
+
+ if (! p2)
+ {
+ unsigned long as_val;
+
+ if (! all_digit (half))
+ goto out;
+
+ as_val = atol(half);
+ if (as_val > 0xffff)
+ {
+ stream_putw (s, RD_TYPE_AS4);
+ stream_putl (s, as_val);
+ stream_putw (s, atol (p + 1));
+ }
+ else
+ {
+ stream_putw (s, RD_TYPE_AS);
+ stream_putw (s, as_val);
+ stream_putl (s, atol (p + 1));
+ }
+ }
+ else
+ {
+ ret = inet_aton (half, &addr);
+ if (! ret)
+ goto out;
+
+ stream_putw (s, RD_TYPE_IP);
+ stream_put_in_addr (s, &addr);
+ stream_putw (s, atol (p + 1));
+ }
+ memcpy (prd->val, s->data, 8);
+ lret = 1;
+
+out:
+ if (s)
+ stream_free (s);
+ if (half)
+ XFREE(MTYPE_TMP, half);
+ return lret;
+}
+
+char *
+prefix_rd2str (struct prefix_rd *prd, char *buf, size_t size)
+{
+ u_char *pnt;
+ u_int16_t type;
+ struct rd_as rd_as;
+ struct rd_ip rd_ip;
+
+ if (size < RD_ADDRSTRLEN)
+ return NULL;
+
+ pnt = prd->val;
+
+ type = decode_rd_type (pnt);
+
+ if (type == RD_TYPE_AS)
+ {
+ decode_rd_as (pnt + 2, &rd_as);
+ snprintf (buf, size, "%u:%d", rd_as.as, rd_as.val);
+ return buf;
+ }
+ else if (type == RD_TYPE_AS4)
+ {
+ decode_rd_as4 (pnt + 2, &rd_as);
+ snprintf (buf, size, "%u:%d", rd_as.as, rd_as.val);
+ return buf;
+ }
+ else if (type == RD_TYPE_IP)
+ {
+ decode_rd_ip (pnt + 2, &rd_ip);
+ snprintf (buf, size, "%s:%d", inet_ntoa (rd_ip.ip), rd_ip.val);
+ return buf;
+ }
+#if ENABLE_BGP_VNC
+ else if (type == RD_TYPE_VNC_ETH)
+ {
+ snprintf(buf, size, "LHI:%d, %02x:%02x:%02x:%02x:%02x:%02x",
+ *(pnt+1), /* LHI */
+ *(pnt+2), /* MAC[0] */
+ *(pnt+3),
+ *(pnt+4),
+ *(pnt+5),
+ *(pnt+6),
+ *(pnt+7));
+
+ return buf;
+ }
+#endif
+ return NULL;
+}
diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h
new file mode 100644
index 000000000..2c7df9d74
--- /dev/null
+++ b/bgpd/bgp_rd.h
@@ -0,0 +1,74 @@
+/* BGP RD definitions for BGP-based VPNs (IP/EVPN)
+ * -- brought over from bgpd/bgp_mplsvpn.h
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_RD_H
+#define _QUAGGA_BGP_RD_H
+
+/* RD types */
+#define RD_TYPE_AS 0
+#define RD_TYPE_IP 1
+#define RD_TYPE_AS4 2
+
+#if ENABLE_BGP_VNC
+#define RD_TYPE_VNC_ETH 0xff00 /* VNC L2VPN */
+#endif
+
+#define RD_ADDRSTRLEN 28
+
+struct rd_as
+{
+ u_int16_t type;
+ as_t as;
+ u_int32_t val;
+};
+
+struct rd_ip
+{
+ u_int16_t type;
+ struct in_addr ip;
+ u_int16_t val;
+};
+
+#if ENABLE_BGP_VNC
+struct rd_vnc_eth
+{
+ u_int16_t type;
+ uint8_t local_nve_id;
+ struct ethaddr macaddr;
+};
+#endif
+
+extern u_int16_t decode_rd_type (u_char *pnt);
+extern void encode_rd_type (u_int16_t, u_char *);
+
+extern void decode_rd_as (u_char *pnt, struct rd_as *rd_as);
+extern void decode_rd_as4 (u_char *pnt, struct rd_as *rd_as);
+extern void decode_rd_ip (u_char *pnt, struct rd_ip *rd_ip);
+#if ENABLE_BGP_VNC
+extern void
+decode_rd_vnc_eth (u_char *pnt, struct rd_vnc_eth *rd_vnc_eth);
+#endif
+
+extern int str2prefix_rd (const char *, struct prefix_rd *);
+extern char *prefix_rd2str (struct prefix_rd *, char *, size_t);
+
+#endif /* _QUAGGA_BGP_RD_H */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index c2694b82b..88b63107e 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -110,6 +110,36 @@ bgp_afi_node_get (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix
return rn;
}
+struct bgp_node *
+bgp_afi_node_lookup (struct bgp_table *table, afi_t afi, safi_t safi, struct prefix *p,
+ struct prefix_rd *prd)
+{
+ struct bgp_node *rn;
+ struct bgp_node *prn = NULL;
+
+ if (!table)
+ return NULL;
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP) || (safi == SAFI_EVPN))
+ {
+ prn = bgp_node_lookup (table, (struct prefix *) prd);
+ if (!prn)
+ return NULL;
+
+ if (prn->info == NULL)
+ {
+ bgp_unlock_node (prn);
+ return NULL;
+ }
+
+ table = prn->info;
+ }
+
+ rn = bgp_node_lookup (table, p);
+
+ return rn;
+}
+
/* Allocate bgp_info_extra */
static struct bgp_info_extra *
bgp_info_extra_new (void)
@@ -224,7 +254,7 @@ bgp_info_add (struct bgp_node *rn, struct bgp_info *ri)
/* Do the actual removal of info from RIB, for use by bgp_process
completion callback *only* */
-static void
+void
bgp_info_reap (struct bgp_node *rn, struct bgp_info *ri)
{
if (ri->next)
@@ -300,7 +330,7 @@ bgp_pcount_adjust (struct bgp_node *rn, struct bgp_info *ri)
static int
bgp_label_index_differs (struct bgp_info *ri1, struct bgp_info *ri2)
{
- return (!(ri1->attr->extra->label_index == ri2->attr->extra->label_index));
+ return (!(ri1->attr->label_index == ri2->attr->label_index));
}
/* Set/unset bgp_info flags, adjusting any other state as needed.
@@ -359,10 +389,9 @@ bgp_info_path_with_addpath_rx_str (struct bgp_info *ri, char *buf)
static int
bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
int *paths_eq, struct bgp_maxpaths_cfg *mpath_cfg, int debug,
- const char *pfx_buf)
+ char *pfx_buf, afi_t afi, safi_t safi)
{
struct attr *newattr, *existattr;
- struct attr_extra *newattre, *existattre;
bgp_peer_sort_t new_sort;
bgp_peer_sort_t exist_sort;
u_int32_t new_pref;
@@ -381,6 +410,8 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
int ret;
char new_buf[PATH_ADDPATH_STR_BUFFER];
char exist_buf[PATH_ADDPATH_STR_BUFFER];
+ u_int32_t new_mm_seq;
+ u_int32_t exist_mm_seq;
*paths_eq = 0;
@@ -411,16 +442,67 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
newattr = new->attr;
existattr = exist->attr;
- newattre = newattr->extra;
- existattre = existattr->extra;
+
+ /* For EVPN routes, we cannot just go by local vs remote, we have to
+ * look at the MAC mobility sequence number, if present.
+ */
+ if (safi == SAFI_EVPN)
+ {
+ /* This is an error condition described in RFC 7432 Section 15.2. The RFC
+ * states that in this scenario "the PE MUST alert the operator" but it
+ * does not state what other action to take. In order to provide some
+ * consistency in this scenario we are going to prefer the path with the
+ * sticky flag.
+ */
+ if (newattr->sticky != existattr->sticky)
+ {
+ if (!debug)
+ {
+ prefix2str (&new->net->p, pfx_buf, sizeof (*pfx_buf) * PREFIX2STR_BUFFER);
+ bgp_info_path_with_addpath_rx_str (new, new_buf);
+ bgp_info_path_with_addpath_rx_str (exist, exist_buf);
+ }
+
+ if (newattr->sticky && !existattr->sticky)
+ {
+ zlog_warn("%s: %s wins over %s due to sticky MAC flag",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ if (!newattr->sticky && existattr->sticky)
+ {
+ zlog_warn("%s: %s loses to %s due to sticky MAC flag",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+ }
+
+ new_mm_seq = mac_mobility_seqnum (newattr);
+ exist_mm_seq = mac_mobility_seqnum (existattr);
+
+ if (new_mm_seq > exist_mm_seq)
+ {
+ if (debug)
+ zlog_debug("%s: %s wins over %s due to MM seq %u > %u",
+ pfx_buf, new_buf, exist_buf, new_mm_seq, exist_mm_seq);
+ return 1;
+ }
+
+ if (new_mm_seq < exist_mm_seq)
+ {
+ if (debug)
+ zlog_debug("%s: %s loses to %s due to MM seq %u < %u",
+ pfx_buf, new_buf, exist_buf, new_mm_seq, exist_mm_seq);
+ return 0;
+ }
+ }
/* 1. Weight check. */
new_weight = exist_weight = 0;
- if (newattre)
- new_weight = newattre->weight;
- if (existattre)
- exist_weight = existattre->weight;
+ new_weight = newattr->weight;
+ exist_weight = existattr->weight;
if (new_weight > exist_weight)
{
@@ -790,11 +872,11 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
* used for the comparision, it will decide which path is better.
*/
if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
- new_id.s_addr = newattre->originator_id.s_addr;
+ new_id.s_addr = newattr->originator_id.s_addr;
else
new_id.s_addr = new->peer->remote_id.s_addr;
if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
- exist_id.s_addr = existattre->originator_id.s_addr;
+ exist_id.s_addr = existattr->originator_id.s_addr;
else
exist_id.s_addr = exist->peer->remote_id.s_addr;
@@ -891,11 +973,12 @@ bgp_info_cmp (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
* This version is compatible with */
int
bgp_info_cmp_compatible (struct bgp *bgp, struct bgp_info *new, struct bgp_info *exist,
- afi_t afi, safi_t safi)
+ char *pfx_buf, afi_t afi, safi_t safi)
{
int paths_eq;
int ret;
- ret = bgp_info_cmp (bgp, new, exist, &paths_eq, NULL, 0, __func__);
+ ret = bgp_info_cmp (bgp, new, exist, &paths_eq, NULL, 0,
+ pfx_buf, afi, safi);
if (paths_eq)
ret = 0;
@@ -1017,14 +1100,14 @@ bgp_cluster_filter (struct peer *peer, struct attr *attr)
{
struct in_addr cluster_id;
- if (attr->extra && attr->extra->cluster)
+ if (attr->cluster)
{
if (peer->bgp->config & BGP_CONFIG_CLUSTER_ID)
cluster_id = peer->bgp->cluster_id;
else
cluster_id = peer->bgp->router_id;
- if (cluster_loop_check (attr->extra->cluster, cluster_id))
+ if (cluster_loop_check (attr->cluster, cluster_id))
return 1;
}
return 0;
@@ -1043,7 +1126,7 @@ bgp_input_modifier (struct peer *peer, struct prefix *p, struct attr *attr,
/* Apply default weight value. */
if (peer->weight[afi][safi])
- (bgp_attr_extra_get (attr))->weight = peer->weight[afi][safi];
+ attr->weight = peer->weight[afi][safi];
if (rmap_name)
{
@@ -1100,7 +1183,7 @@ bgp_output_modifier (struct peer *peer, struct prefix *p, struct attr *attr,
/* Apply default weight value. */
if (peer->weight[afi][safi])
- (bgp_attr_extra_get (attr))->weight = peer->weight[afi][safi];
+ attr->weight = peer->weight[afi][safi];
if (rmap_name)
{
@@ -1199,7 +1282,7 @@ subgroup_announce_reset_nhop (u_char family, struct attr *attr)
if (family == AF_INET)
attr->nexthop.s_addr = 0;
if (family == AF_INET6)
- memset (&attr->extra->mp_nexthop_global, 0, IPV6_MAX_BYTELEN);
+ memset (&attr->mp_nexthop_global, 0, IPV6_MAX_BYTELEN);
}
int
@@ -1328,7 +1411,7 @@ subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri,
peer's id. */
if (onlypeer &&
riattr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID) &&
- (IPV4_ADDR_SAME (&onlypeer->remote_id, &riattr->extra->originator_id)))
+ (IPV4_ADDR_SAME (&onlypeer->remote_id, &riattr->originator_id)))
{
if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
zlog_debug ("%s [Update:SEND] %s originator-id is same as "
@@ -1435,8 +1518,7 @@ subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri,
set the originator id */
if (reflect && (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))))
{
- attr->extra = bgp_attr_extra_get(attr);
- IPV4_ADDR_COPY(&(attr->extra->originator_id), &(from->remote_id));
+ IPV4_ADDR_COPY(&(attr->originator_id), &(from->remote_id));
SET_FLAG(attr->flag, BGP_ATTR_ORIGINATOR_ID);
}
@@ -1467,7 +1549,7 @@ subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri,
(safi != SAFI_ENCAP && safi != SAFI_MPLS_VPN &&\
(p->family == AF_INET6 || peer_cap_enhe(peer, afi, safi))) || \
((safi == SAFI_ENCAP || safi == SAFI_MPLS_VPN) &&\
- attr->extra->mp_nexthop_len >= IPV6_MAX_BYTELEN))
+ attr->mp_nexthop_len >= IPV6_MAX_BYTELEN))
/* IPv6/MP starts with 1 nexthop. The link-local address is passed only if
* the peer (group) is configured to receive link-local nexthop unchanged
@@ -1477,14 +1559,14 @@ subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri,
*/
if (NEXTHOP_IS_V6)
{
- attr->extra->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
if ((CHECK_FLAG (peer->af_flags[afi][safi],
PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED) &&
- IN6_IS_ADDR_LINKLOCAL (&attr->extra->mp_nexthop_local)) ||
+ IN6_IS_ADDR_LINKLOCAL (&attr->mp_nexthop_local)) ||
(!reflect && peer->shared_network &&
(from == bgp->peer_self || peer->sort == BGP_PEER_EBGP)))
{
- attr->extra->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
+ attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
}
/* Clear off link-local nexthop in source, whenever it is not needed to
@@ -1492,7 +1574,7 @@ subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri,
*/
if (!(CHECK_FLAG (peer->af_flags[afi][safi],
PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)))
- memset (&attr->extra->mp_nexthop_local, 0, IPV6_MAX_BYTELEN);
+ memset (&attr->mp_nexthop_local, 0, IPV6_MAX_BYTELEN);
}
bgp_peer_remove_private_as(bgp, afi, safi, peer, attr);
@@ -1504,9 +1586,6 @@ subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri,
{
struct bgp_info info;
struct attr dummy_attr;
- struct attr_extra dummy_extra;
-
- dummy_attr.extra = &dummy_extra;
info.peer = peer;
info.attr = attr;
@@ -1591,7 +1670,7 @@ subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri,
*/
if (p->family == AF_INET6 || peer_cap_enhe(peer, afi, safi))
{
- if (IN6_IS_ADDR_LINKLOCAL (&attr->extra->mp_nexthop_global))
+ if (IN6_IS_ADDR_LINKLOCAL (&attr->mp_nexthop_global))
subgroup_announce_reset_nhop (AF_INET6, attr);
}
}
@@ -1599,16 +1678,11 @@ subgroup_announce_check (struct bgp_node *rn, struct bgp_info *ri,
return 1;
}
-struct bgp_info_pair
-{
- struct bgp_info *old;
- struct bgp_info *new;
-};
-
-static void
+void
bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
struct bgp_maxpaths_cfg *mpath_cfg,
- struct bgp_info_pair *result)
+ struct bgp_info_pair *result,
+ afi_t afi, safi_t safi)
{
struct bgp_info *new_select;
struct bgp_info *old_select;
@@ -1668,7 +1742,7 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
ri2->attr->aspath))
{
if (bgp_info_cmp (bgp, ri2, new_select, &paths_eq,
- mpath_cfg, debug, pfx_buf))
+ mpath_cfg, debug, pfx_buf, afi, safi))
{
bgp_info_unset_flag (rn, new_select, BGP_INFO_DMED_SELECTED);
new_select = ri2;
@@ -1725,7 +1799,8 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
bgp_info_unset_flag (rn, ri, BGP_INFO_DMED_CHECK);
- if (bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg, debug, pfx_buf))
+ if (bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg,
+ debug, pfx_buf, afi, safi))
{
new_select = ri;
}
@@ -1779,7 +1854,8 @@ bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
continue;
}
- bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg, debug, pfx_buf);
+ bgp_info_cmp (bgp, ri, new_select, &paths_eq, mpath_cfg,
+ debug, pfx_buf, afi, safi);
if (paths_eq)
{
@@ -1814,7 +1890,6 @@ subgroup_process_announce_selected (struct update_subgroup *subgrp,
struct prefix *p;
struct peer *onlypeer;
struct attr attr;
- struct attr_extra extra;
afi_t afi;
safi_t safi;
@@ -1829,9 +1904,8 @@ subgroup_process_announce_selected (struct update_subgroup *subgrp,
PEER_STATUS_ORF_WAIT_REFRESH))
return 0;
- memset(&extra, 0, sizeof(struct attr_extra));
+ memset(&attr, 0, sizeof(struct attr));
/* It's initialized in bgp_announce_check() */
- attr.extra = &extra;
/* Announcement to the subgroup. If the route is filtered withdraw it. */
if (selected)
@@ -1855,7 +1929,7 @@ subgroup_process_announce_selected (struct update_subgroup *subgrp,
* Clear IGP changed flag and attribute changed flag for a route (all paths).
* This is called at the end of route processing.
*/
-static void
+void
bgp_zebra_clear_route_change_flags (struct bgp_node *rn)
{
struct bgp_info *ri;
@@ -1874,7 +1948,7 @@ bgp_zebra_clear_route_change_flags (struct bgp_node *rn)
* if the route selection returns the same best route as earlier - to
* determine if we need to update zebra or not.
*/
-static int
+int
bgp_zebra_has_route_changed (struct bgp_node *rn, struct bgp_info *selected)
{
struct bgp_info *mpinfo;
@@ -1933,7 +2007,8 @@ bgp_process_main (struct work_queue *wq, void *data)
for (afi = AFI_IP; afi < AFI_MAX; afi++)
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
{
- bgp_zebra_announce_table(bgp, afi, safi);
+ if (bgp_fibupd_safi(safi))
+ bgp_zebra_announce_table(bgp, afi, safi);
}
bgp->main_peers_update_hold = 0;
@@ -1942,7 +2017,8 @@ bgp_process_main (struct work_queue *wq, void *data)
}
/* Best path selection. */
- bgp_best_selection (bgp, rn, &bgp->maxpaths[afi][safi], &old_and_new);
+ bgp_best_selection (bgp, rn, &bgp->maxpaths[afi][safi],
+ &old_and_new, afi, safi);
old_select = old_and_new.old;
new_select = old_and_new.new;
@@ -1960,7 +2036,7 @@ bgp_process_main (struct work_queue *wq, void *data)
{
if (new_select->sub_type == BGP_ROUTE_STATIC &&
new_select->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_PREFIX_SID) &&
- new_select->attr->extra->label_index != BGP_INVALID_LABEL_INDEX)
+ new_select->attr->label_index != BGP_INVALID_LABEL_INDEX)
{
if (CHECK_FLAG (rn->flags, BGP_NODE_REGISTERED_FOR_LABEL))
bgp_unregister_for_label (rn);
@@ -2342,12 +2418,17 @@ bgp_rib_withdraw (struct bgp_node *rn, struct bgp_info *ri, struct peer *peer,
}
}
#endif
+
+ /* If this is an EVPN route, process for un-import. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route (peer->bgp, afi, safi, &rn->p, ri);
+
bgp_rib_remove (rn, ri, peer, afi, safi);
}
-static struct bgp_info *
-info_make (int type, int sub_type, u_short instance, struct peer *peer, struct attr *attr,
- struct bgp_node *rn)
+struct bgp_info *
+info_make (int type, int sub_type, u_short instance, struct peer *peer,
+ struct attr *attr, struct bgp_node *rn)
{
struct bgp_info *new;
@@ -2367,27 +2448,24 @@ info_make (int type, int sub_type, u_short instance, struct peer *peer, struct a
static void
overlay_index_update(struct attr *attr, struct eth_segment_id *eth_s_id, union gw_addr *gw_ip)
{
- struct attr_extra *extra;
-
if(!attr)
return;
- extra = bgp_attr_extra_get(attr);
if(eth_s_id == NULL)
{
- memset(&(extra->evpn_overlay.eth_s_id),0, sizeof(struct eth_segment_id));
+ memset(&(attr->evpn_overlay.eth_s_id),0, sizeof(struct eth_segment_id));
}
else
{
- memcpy(&(extra->evpn_overlay.eth_s_id), eth_s_id, sizeof(struct eth_segment_id));
+ memcpy(&(attr->evpn_overlay.eth_s_id), eth_s_id, sizeof(struct eth_segment_id));
}
if(gw_ip == NULL)
{
- memset(&(extra->evpn_overlay.gw_ip), 0, sizeof(union gw_addr));
+ memset(&(attr->evpn_overlay.gw_ip), 0, sizeof(union gw_addr));
}
else
{
- memcpy(&(extra->evpn_overlay.gw_ip),gw_ip, sizeof(union gw_addr));
+ memcpy(&(attr->evpn_overlay.gw_ip),gw_ip, sizeof(union gw_addr));
}
}
@@ -2400,7 +2478,7 @@ overlay_index_equal(afi_t afi, struct bgp_info *info, struct eth_segment_id *eth
if(afi != AFI_L2VPN)
return true;
- if (!info->attr || !info->attr->extra)
+ if (!info->attr)
{
memset(&temp, 0, 16);
info_eth_s_id = (struct eth_segment_id *)&temp;
@@ -2410,8 +2488,8 @@ overlay_index_equal(afi_t afi, struct bgp_info *info, struct eth_segment_id *eth
}
else
{
- info_eth_s_id = &(info->attr->extra->evpn_overlay.eth_s_id);
- info_gw_ip = &(info->attr->extra->evpn_overlay.gw_ip);
+ info_eth_s_id = &(info->attr->evpn_overlay.eth_s_id);
+ info_gw_ip = &(info->attr->evpn_overlay.gw_ip);
}
if(gw_ip == NULL)
info_gw_ip_remote = (union gw_addr *)&temp;
@@ -2430,11 +2508,11 @@ overlay_index_equal(afi_t afi, struct bgp_info *info, struct eth_segment_id *eth
static int
bgp_update_martian_nexthop (struct bgp *bgp, afi_t afi, safi_t safi, struct attr *attr)
{
- struct attr_extra *attre = attr->extra;
int ret = 0;
/* Only validated for unicast and multicast currently. */
- if (safi != SAFI_UNICAST && safi != SAFI_MULTICAST)
+ /* Also valid for EVPN where the nexthop is an IP address. */
+ if (safi != SAFI_UNICAST && safi != SAFI_MULTICAST && safi != SAFI_EVPN)
return 0;
/* If NEXT_HOP is present, validate it. */
@@ -2442,8 +2520,8 @@ bgp_update_martian_nexthop (struct bgp *bgp, afi_t afi, safi_t safi, struct attr
{
if (attr->nexthop.s_addr == 0 ||
IPV4_CLASS_DE (ntohl (attr->nexthop.s_addr)) ||
- bgp_nexthop_self (bgp, attr))
- ret = 1;
+ bgp_nexthop_self (bgp, attr->nexthop))
+ return 1;
}
/* If MP_NEXTHOP is present, validate it. */
@@ -2451,22 +2529,23 @@ bgp_update_martian_nexthop (struct bgp *bgp, afi_t afi, safi_t safi, struct attr
* there is code in bgp_attr.c to ignore the link-local (2nd) nexthop if
* it is not an IPv6 link-local address.
*/
- if (attre && attre->mp_nexthop_len)
+ if (attr->mp_nexthop_len)
{
- switch (attre->mp_nexthop_len)
+ switch (attr->mp_nexthop_len)
{
case BGP_ATTR_NHLEN_IPV4:
case BGP_ATTR_NHLEN_VPNV4:
- ret = (attre->mp_nexthop_global_in.s_addr == 0 ||
- IPV4_CLASS_DE (ntohl (attre->mp_nexthop_global_in.s_addr)));
+ ret = (attr->mp_nexthop_global_in.s_addr == 0 ||
+ IPV4_CLASS_DE (ntohl (attr->mp_nexthop_global_in.s_addr)) ||
+ bgp_nexthop_self (bgp, attr->mp_nexthop_global_in));
break;
case BGP_ATTR_NHLEN_IPV6_GLOBAL:
case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
- ret = (IN6_IS_ADDR_UNSPECIFIED(&attre->mp_nexthop_global) ||
- IN6_IS_ADDR_LOOPBACK(&attre->mp_nexthop_global) ||
- IN6_IS_ADDR_MULTICAST(&attre->mp_nexthop_global));
+ ret = (IN6_IS_ADDR_UNSPECIFIED(&attr->mp_nexthop_global) ||
+ IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global) ||
+ IN6_IS_ADDR_MULTICAST(&attr->mp_nexthop_global));
break;
default:
@@ -2489,7 +2568,6 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
struct bgp_node *rn;
struct bgp *bgp;
struct attr new_attr;
- struct attr_extra new_extra;
struct attr *attr_new;
struct bgp_info *ri;
struct bgp_info *new;
@@ -2502,16 +2580,14 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
#if ENABLE_BGP_VNC
int vnc_implicit_withdraw = 0;
#endif
+ int same_attr=0;
memset (&new_attr, 0, sizeof(struct attr));
- memset (&new_extra, 0, sizeof(struct attr_extra));
- new_extra.label_index = BGP_INVALID_LABEL_INDEX;
- new_extra.label = MPLS_INVALID_LABEL;
+ new_attr.label_index = BGP_INVALID_LABEL_INDEX;
+ new_attr.label = MPLS_INVALID_LABEL;
bgp = peer->bgp;
rn = bgp_afi_node_get (bgp->rib[afi][safi], afi, safi, p, prd);
- label_buf[0] = '\0';
-
has_valid_label = bgp_is_valid_label(label);
if (has_valid_label)
@@ -2563,7 +2639,7 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
/* Route reflector originator ID check. */
if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID)
- && IPV4_ADDR_SAME (&bgp->router_id, &attr->extra->originator_id))
+ && IPV4_ADDR_SAME (&bgp->router_id, &attr->originator_id))
{
reason = "originator is us;";
goto filtered;
@@ -2583,7 +2659,6 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
goto filtered;
}
- new_attr.extra = &new_extra;
bgp_attr_dup (&new_attr, attr);
/* Apply incoming route-map.
@@ -2611,6 +2686,7 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
if (ri)
{
ri->uptime = bgp_clock ();
+ same_attr = attrhash_cmp (ri->attr, attr_new);
/* Same attribute comes in. */
if (!CHECK_FLAG (ri->flags, BGP_INFO_REMOVED)
@@ -2625,9 +2701,12 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
&& CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
{
if (bgp_debug_update(peer, p, NULL, 1))
- zlog_debug ("%s rcvd %s %s", peer->host,
- bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0,
- addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf);
+ {
+ bgp_debug_rdpfxpath2str (afi, safi, prd, p, label,
+ addpath_id ? 1 : 0, addpath_id,
+ pfx_buf, sizeof (pfx_buf));
+ zlog_debug ("%s rcvd %s", peer->host, pfx_buf);
+ }
if (bgp_damp_update (ri, rn, afi, safi) != BGP_DAMP_SUPPRESSED)
{
@@ -2645,10 +2724,11 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
peer->rcvd_attr_printed = 1;
}
- zlog_debug ("%s rcvd %s %s...duplicate ignored",
- peer->host,
- bgp_debug_rdpfxpath2str (prd, p, addpath_id ?
- 1 : 0, addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf);
+ bgp_debug_rdpfxpath2str (afi, safi, prd, p, label,
+ addpath_id ? 1 : 0, addpath_id,
+ pfx_buf, sizeof (pfx_buf));
+ zlog_debug ("%s rcvd %s...duplicate ignored",
+ peer->host, pfx_buf);
}
/* graceful restart STALE flag unset. */
@@ -2669,18 +2749,25 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
if (CHECK_FLAG(ri->flags, BGP_INFO_REMOVED))
{
if (bgp_debug_update(peer, p, NULL, 1))
- zlog_debug ("%s rcvd %s %s, flapped quicker than processing",
- peer->host,
- bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0,
- addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf);
+ {
+ bgp_debug_rdpfxpath2str (afi, safi, prd, p, label,
+ addpath_id ? 1 : 0, addpath_id,
+ pfx_buf, sizeof (pfx_buf));
+ zlog_debug ("%s rcvd %s, flapped quicker than processing",
+ peer->host, pfx_buf);
+ }
+
bgp_info_restore (rn, ri);
}
/* Received Logging. */
if (bgp_debug_update(peer, p, NULL, 1))
- zlog_debug ("%s rcvd %s %s", peer->host,
- bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0,
- addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf);
+ {
+ bgp_debug_rdpfxpath2str (afi, safi, prd, p, label,
+ addpath_id ? 1 : 0, addpath_id,
+ pfx_buf, sizeof (pfx_buf));
+ zlog_debug ("%s rcvd %s", peer->host, pfx_buf);
+ }
/* graceful restart STALE flag unset. */
if (CHECK_FLAG (ri->flags, BGP_INFO_STALE))
@@ -2732,7 +2819,32 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
}
}
#endif
-
+
+ /* Special handling for EVPN update of an existing route. If the
+ * extended community attribute has changed, we need to un-import
+ * the route using its existing extended community. It will be
+ * subsequently processed for import with the new extended community.
+ */
+ if (safi == SAFI_EVPN && !same_attr)
+ {
+ if ((ri->attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)) &&
+ (attr_new->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
+ {
+ int cmp;
+
+ cmp = ecommunity_cmp (ri->attr->ecommunity,
+ attr_new->ecommunity);
+ if (!cmp)
+ {
+ if (bgp_debug_update(peer, p, NULL, 1))
+ zlog_debug ("Change in EXT-COMM, existing %s new %s",
+ ecommunity_str (ri->attr->ecommunity),
+ ecommunity_str (attr_new->ecommunity));
+ bgp_evpn_unimport_route (bgp, afi, safi, p, ri);
+ }
+ }
+ }
+
/* Update to new attribute. */
bgp_attr_unintern (&ri->attr);
ri->attr = attr_new;
@@ -2831,6 +2943,16 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
}
#endif
+ /* If this is an EVPN route and some attribute has changed, process
+ * route for import. If the extended community has changed, we would
+ * have done the un-import earlier and the import would result in the
+ * route getting injected into appropriate L2 VNIs. If it is just
+ * some other attribute change, the import will result in updating
+ * the attributes for the route in the VNI(s).
+ */
+ if (safi == SAFI_EVPN && !same_attr)
+ bgp_evpn_import_route (bgp, afi, safi, p, ri);
+
/* Process change. */
bgp_aggregate_increment (bgp, p, ri, afi, safi);
@@ -2864,9 +2986,10 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
peer->rcvd_attr_printed = 1;
}
- zlog_debug ("%s rcvd %s %s ", peer->host,
- bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0,
- addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf);
+ bgp_debug_rdpfxpath2str (afi, safi, prd, p, label,
+ addpath_id ? 1 : 0, addpath_id,
+ pfx_buf, sizeof (pfx_buf));
+ zlog_debug ("%s rcvd %s", peer->host, pfx_buf);
}
/* Make new BGP info. */
@@ -2951,6 +3074,10 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
if (bgp_maximum_prefix_overflow (peer, afi, safi, 0))
return -1;
+ /* If this is an EVPN route, process for import. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_import_route (bgp, afi, safi, p, new);
+
/* Process change. */
bgp_process (bgp, rn, afi, safi);
@@ -2982,14 +3109,21 @@ bgp_update (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
peer->rcvd_attr_printed = 1;
}
- zlog_debug ("%s rcvd UPDATE about %s %s -- DENIED due to: %s",
- peer->host,
- bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0,
- addpath_id, pfx_buf, sizeof (pfx_buf)), label_buf, reason);
+ bgp_debug_rdpfxpath2str (afi, safi, prd, p, label,
+ addpath_id ? 1 : 0, addpath_id,
+ pfx_buf, sizeof (pfx_buf));
+ zlog_debug ("%s rcvd UPDATE about %s -- DENIED due to: %s",
+ peer->host, pfx_buf, reason);
}
if (ri)
- bgp_rib_remove (rn, ri, peer, afi, safi);
+ {
+ /* If this is an EVPN route, un-import it as it is now filtered. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route (bgp, afi, safi, p, ri);
+
+ bgp_rib_remove (rn, ri, peer, afi, safi);
+ }
bgp_unlock_node (rn);
@@ -3043,10 +3177,13 @@ bgp_withdraw (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
if (!bgp_adj_in_unset (rn, peer, addpath_id))
{
if (bgp_debug_update (peer, p, NULL, 1))
- zlog_debug ("%s withdrawing route %s not in adj-in",
- peer->host,
- bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0,
- addpath_id, pfx_buf, sizeof (pfx_buf)));
+ {
+ bgp_debug_rdpfxpath2str (afi, safi, prd, p, label,
+ addpath_id ? 1 : 0, addpath_id,
+ pfx_buf, sizeof (pfx_buf));
+ zlog_debug ("%s withdrawing route %s not in adj-in",
+ peer->host, pfx_buf);
+ }
bgp_unlock_node (rn);
return 0;
}
@@ -3060,20 +3197,24 @@ bgp_withdraw (struct peer *peer, struct prefix *p, u_int32_t addpath_id,
/* Logging. */
if (bgp_debug_update(peer, p, NULL, 1))
{
+ bgp_debug_rdpfxpath2str (afi, safi, prd, p, label,
+ addpath_id ? 1 : 0, addpath_id,
+ pfx_buf, sizeof (pfx_buf));
zlog_debug ("%s rcvd UPDATE about %s -- withdrawn",
- peer->host,
- bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0,
- addpath_id, pfx_buf, sizeof (pfx_buf)));
+ peer->host, pfx_buf);
}
/* Withdraw specified route from routing table. */
if (ri && ! CHECK_FLAG (ri->flags, BGP_INFO_HISTORY))
bgp_rib_withdraw (rn, ri, peer, afi, safi, prd);
else if (bgp_debug_update(peer, p, NULL, 1))
- zlog_debug ("%s Can't find the route %s",
- peer->host,
- bgp_debug_rdpfxpath2str (prd, p, addpath_id ? 1 : 0,
- addpath_id, pfx_buf, sizeof (pfx_buf)));
+ {
+ bgp_debug_rdpfxpath2str (afi, safi, prd, p, label,
+ addpath_id ? 1 : 0, addpath_id,
+ pfx_buf, sizeof (pfx_buf));
+ zlog_debug ("%s Can't find the route %s",
+ peer->host, pfx_buf);
+ }
/* Unlock bgp_node_get() lock. */
bgp_unlock_node (rn);
@@ -3269,7 +3410,12 @@ bgp_clear_route_node (struct work_queue *wq, void *data)
&& ! CHECK_FLAG (ri->flags, BGP_INFO_UNUSEABLE))
bgp_info_set_flag (rn, ri, BGP_INFO_STALE);
else
- bgp_rib_remove (rn, ri, peer, afi, safi);
+ {
+ /* If this is an EVPN route, process for un-import. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route (peer->bgp, afi, safi, &rn->p, ri);
+ bgp_rib_remove (rn, ri, peer, afi, safi);
+ }
}
return WQ_SUCCESS;
}
@@ -3561,7 +3707,8 @@ bgp_cleanup_table(struct bgp_table *table, safi_t safi)
&& (ri->sub_type == BGP_ROUTE_NORMAL ||
ri->sub_type == BGP_ROUTE_AGGREGATE))
{
- bgp_zebra_withdraw (&rn->p, ri, safi);
+ if (bgp_fibupd_safi(safi))
+ bgp_zebra_withdraw (&rn->p, ri, safi);
bgp_info_reap (rn, ri);
}
}
@@ -3835,7 +3982,7 @@ bgp_static_update (struct bgp *bgp, struct prefix *p,
/* Store label index, if required. */
if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX)
{
- (bgp_attr_extra_get (&attr))->label_index = bgp_static->label_index;
+ attr.label_index = bgp_static->label_index;
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_PREFIX_SID);
}
@@ -3859,7 +4006,6 @@ bgp_static_update (struct bgp *bgp, struct prefix *p,
/* Unintern original. */
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
bgp_static_withdraw (bgp, p, afi, safi);
return;
}
@@ -3882,7 +4028,6 @@ bgp_static_update (struct bgp *bgp, struct prefix *p,
bgp_unlock_node (rn);
bgp_attr_unintern (&attr_new);
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
return;
}
else
@@ -3957,7 +4102,6 @@ bgp_static_update (struct bgp *bgp, struct prefix *p,
bgp_process (bgp, rn, afi, safi);
bgp_unlock_node (rn);
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
return;
}
}
@@ -4009,7 +4153,6 @@ bgp_static_update (struct bgp *bgp, struct prefix *p,
/* Unintern original. */
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
}
void
@@ -4112,8 +4255,8 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
{
if (afi == AFI_IP)
{
- bgp_attr_extra_get (&attr)->mp_nexthop_global_in = bgp_static->igpnexthop;
- bgp_attr_extra_get (&attr)->mp_nexthop_len = IPV4_MAX_BYTELEN;
+ attr.mp_nexthop_global_in = bgp_static->igpnexthop;
+ attr.mp_nexthop_len = IPV4_MAX_BYTELEN;
}
}
if(afi == AFI_L2VPN)
@@ -4158,7 +4301,6 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
/* Unintern original. */
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
bgp_static_withdraw_safi (bgp, p, afi, safi, &bgp_static->prd);
return;
}
@@ -4186,7 +4328,6 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
bgp_unlock_node (rn);
bgp_attr_unintern (&attr_new);
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
return;
}
else
@@ -4217,7 +4358,6 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
#endif
bgp_unlock_node (rn);
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
return;
}
}
@@ -4252,7 +4392,6 @@ bgp_static_update_safi (struct bgp *bgp, struct prefix *p,
/* Unintern original. */
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
}
/* Configure static BGP network. When user don't run zebra, static
@@ -4786,7 +4925,8 @@ bgp_table_map_set (struct vty *vty, afi_t afi, safi_t safi,
rmap->map = NULL;
}
- bgp_zebra_announce_table(bgp, afi, safi);
+ if (bgp_fibupd_safi(safi))
+ bgp_zebra_announce_table(bgp, afi, safi);
return CMD_SUCCESS;
}
@@ -4804,7 +4944,8 @@ bgp_table_map_unset (struct vty *vty, afi_t afi, safi_t safi,
rmap->name = NULL;
rmap->map = NULL;
- bgp_zebra_announce_table(bgp, afi, safi);
+ if (bgp_fibupd_safi(safi))
+ bgp_zebra_announce_table(bgp, afi, safi);
return CMD_SUCCESS;
}
@@ -5989,14 +6130,13 @@ bgp_redistribute_add (struct bgp *bgp, struct prefix *p, const struct in_addr *n
if (nexthop6)
{
- struct attr_extra *extra = bgp_attr_extra_get(&attr);
- extra->mp_nexthop_global = *nexthop6;
- extra->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ attr.mp_nexthop_global = *nexthop6;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
}
attr.med = metric;
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
- attr.extra->tag = tag;
+ attr.tag = tag;
afi = family2afi (p->family);
@@ -6004,10 +6144,8 @@ bgp_redistribute_add (struct bgp *bgp, struct prefix *p, const struct in_addr *n
if (red)
{
struct attr attr_new;
- struct attr_extra extra_new;
/* Copy attribute for modification. */
- attr_new.extra = &extra_new;
bgp_attr_dup (&attr_new, &attr);
if (red->redist_metric_flag)
@@ -6032,7 +6170,6 @@ bgp_redistribute_add (struct bgp *bgp, struct prefix *p, const struct in_addr *n
/* Unintern original. */
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
bgp_redistribute_delete (bgp, p, type, instance);
return;
}
@@ -6057,7 +6194,6 @@ bgp_redistribute_add (struct bgp *bgp, struct prefix *p, const struct in_addr *n
{
bgp_attr_unintern (&new_attr);
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
bgp_unlock_node (bn);
return;
}
@@ -6080,7 +6216,6 @@ bgp_redistribute_add (struct bgp *bgp, struct prefix *p, const struct in_addr *n
bgp_process (bgp, bn, afi, SAFI_UNICAST);
bgp_unlock_node (bn);
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
return;
}
}
@@ -6097,7 +6232,6 @@ bgp_redistribute_add (struct bgp *bgp, struct prefix *p, const struct in_addr *n
/* Unintern original. */
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
}
void
@@ -6182,8 +6316,13 @@ route_vty_out_route (struct prefix *p, struct vty *vty)
}
else if (p->family == AF_ETHERNET)
{
+#if defined (HAVE_CUMULUS)
+ len = vty_out (vty, "%s",
+ bgp_evpn_route2str((struct prefix_evpn *)p, buf, BUFSIZ));
+#else
prefix2str(p, buf, PREFIX_STRLEN);
len = vty_out (vty, "%s", buf);
+#endif
}
else
len = vty_out (vty, "%s/%d", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ),
@@ -6307,37 +6446,46 @@ route_vty_out (struct vty *vty, struct prefix *p,
/* Print attribute */
attr = binfo->attr;
- if (attr)
+ if (attr)
{
/*
- * For ENCAP routes, nexthop address family is not
+ * For ENCAP and EVPN routes, nexthop address family is not
* neccessarily the same as the prefix address family.
* Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field
+ * EVPN routes are also exchanged with a MP nexthop. Currently, this
+ * is only IPv4, the value will be present in either attr->nexthop or
+ * attr->mp_nexthop_global_in
*/
- if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
+ if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN))
{
- if (attr->extra)
+ char buf[BUFSIZ];
+ int af = NEXTHOP_FAMILY(attr->mp_nexthop_len);
+
+ switch (af)
{
- char buf[BUFSIZ];
- int af = NEXTHOP_FAMILY(attr->extra->mp_nexthop_len);
+ case AF_INET:
+ vty_out (vty, "%s", inet_ntop(af, &attr->mp_nexthop_global_in, buf, BUFSIZ));
+ break;
+ case AF_INET6:
+ vty_out (vty, "%s", inet_ntop(af, &attr->mp_nexthop_global, buf, BUFSIZ));
+ break;
+ default:
+ vty_out(vty, "?");
+ break;
+ }
+ }
+ else if (safi == SAFI_EVPN)
+ {
+ if (json_paths)
+ {
+ json_nexthop_global = json_object_new_object();
- switch (af)
- {
- case AF_INET:
- vty_out (vty, "%s", inet_ntop(af,
- &attr->extra->mp_nexthop_global_in, buf, BUFSIZ));
- break;
- case AF_INET6:
- vty_out (vty, "%s", inet_ntop(af,
- &attr->extra->mp_nexthop_global, buf, BUFSIZ));
- break;
- default:
- vty_out(vty, "?");
- break;
- }
+ json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->nexthop));
+ json_object_string_add(json_nexthop_global, "afi", "ipv4");
+ json_object_boolean_true_add(json_nexthop_global, "used");
}
else
- vty_out(vty, "?");
+ vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
}
/* IPv4 Next Hop */
else if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))
@@ -6347,7 +6495,7 @@ route_vty_out (struct vty *vty, struct prefix *p,
json_nexthop_global = json_object_new_object();
if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
- json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->extra->mp_nexthop_global_in));
+ json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->mp_nexthop_global_in));
else
json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->nexthop));
@@ -6358,7 +6506,7 @@ route_vty_out (struct vty *vty, struct prefix *p,
{
if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN))
vty_out (vty, "%-16s",
- inet_ntoa (attr->extra->mp_nexthop_global_in));
+ inet_ntoa (attr->mp_nexthop_global_in));
else
vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
}
@@ -6375,25 +6523,25 @@ route_vty_out (struct vty *vty, struct prefix *p,
json_nexthop_global = json_object_new_object();
json_object_string_add(json_nexthop_global, "ip",
inet_ntop (AF_INET6,
- &attr->extra->mp_nexthop_global,
+ &attr->mp_nexthop_global,
buf, BUFSIZ));
json_object_string_add(json_nexthop_global, "afi", "ipv6");
json_object_string_add(json_nexthop_global, "scope", "global");
/* We display both LL & GL if both have been received */
- if ((attr->extra->mp_nexthop_len == 32) || (binfo->peer->conf_if))
+ if ((attr->mp_nexthop_len == 32) || (binfo->peer->conf_if))
{
json_nexthop_ll = json_object_new_object();
json_object_string_add(json_nexthop_ll, "ip",
inet_ntop (AF_INET6,
- &attr->extra->mp_nexthop_local,
+ &attr->mp_nexthop_local,
buf, BUFSIZ));
json_object_string_add(json_nexthop_ll, "afi", "ipv6");
json_object_string_add(json_nexthop_ll, "scope", "link-local");
- if ((IPV6_ADDR_CMP (&attr->extra->mp_nexthop_global,
- &attr->extra->mp_nexthop_local) != 0) &&
- !attr->extra->mp_nexthop_prefer_global)
+ if ((IPV6_ADDR_CMP (&attr->mp_nexthop_global,
+ &attr->mp_nexthop_local) != 0) &&
+ !attr->mp_nexthop_prefer_global)
json_object_boolean_true_add(json_nexthop_ll, "used");
else
json_object_boolean_true_add(json_nexthop_global, "used");
@@ -6404,8 +6552,8 @@ route_vty_out (struct vty *vty, struct prefix *p,
else
{
/* Display LL if LL/Global both in table unless prefer-global is set */
- if (((attr->extra->mp_nexthop_len == 32) &&
- !attr->extra->mp_nexthop_prefer_global) ||
+ if (((attr->mp_nexthop_len == 32) &&
+ !attr->mp_nexthop_prefer_global) ||
(binfo->peer->conf_if))
{
if (binfo->peer->conf_if)
@@ -6423,7 +6571,7 @@ route_vty_out (struct vty *vty, struct prefix *p,
{
len = vty_out (vty, "%s",
inet_ntop (AF_INET6,
- &attr->extra->mp_nexthop_local,
+ &attr->mp_nexthop_local,
buf, BUFSIZ));
len = 16 - len;
@@ -6437,7 +6585,7 @@ route_vty_out (struct vty *vty, struct prefix *p,
{
len = vty_out (vty, "%s",
inet_ntop (AF_INET6,
- &attr->extra->mp_nexthop_global,
+ &attr->mp_nexthop_global,
buf, BUFSIZ));
len = 16 - len;
@@ -6470,14 +6618,9 @@ route_vty_out (struct vty *vty, struct prefix *p,
vty_out (vty, " ");
if (json_paths)
- {
- if (attr->extra)
- json_object_int_add(json_path, "weight", attr->extra->weight);
- else
- json_object_int_add(json_path, "weight", 0);
- }
+ json_object_int_add(json_path, "weight", attr->weight);
else
- vty_out (vty, "%7u ", (attr->extra ? attr->extra->weight : 0));
+ vty_out (vty, "%7u ", attr->weight);
if (json_paths) {
char buf[BUFSIZ];
@@ -6574,7 +6717,7 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, struct attr *attr, safi_t
!BGP_ATTR_NEXTHOP_AFI_IP6(attr)))
{
if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN)
- json_object_string_add(json_net, "nextHop", inet_ntoa (attr->extra->mp_nexthop_global_in));
+ json_object_string_add(json_net, "nextHop", inet_ntoa (attr->mp_nexthop_global_in));
else
json_object_string_add(json_net, "nextHop", inet_ntoa (attr->nexthop));
}
@@ -6582,7 +6725,7 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, struct attr *attr, safi_t
{
char buf[BUFSIZ];
- json_object_string_add(json_net, "netHopGloabal", inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+ json_object_string_add(json_net, "netHopGloabal", inet_ntop (AF_INET6, &attr->mp_nexthop_global,
buf, BUFSIZ));
}
@@ -6592,10 +6735,7 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, struct attr *attr, safi_t
if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
json_object_int_add(json_net, "localPref", attr->local_pref);
- if (attr->extra)
- json_object_int_add(json_net, "weight", attr->extra->weight);
- else
- json_object_int_add(json_net, "weight", 0);
+ json_object_int_add(json_net, "weight", attr->weight);
/* Print aspath */
if (attr->aspath)
@@ -6614,7 +6754,7 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, struct attr *attr, safi_t
{
if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN)
vty_out (vty, "%-16s",
- inet_ntoa (attr->extra->mp_nexthop_global_in));
+ inet_ntoa (attr->mp_nexthop_global_in));
else
vty_out (vty, "%-16s", inet_ntoa (attr->nexthop));
}
@@ -6623,10 +6763,8 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, struct attr *attr, safi_t
int len;
char buf[BUFSIZ];
- assert (attr->extra);
-
len = vty_out (vty, "%s",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global,
buf, BUFSIZ));
len = 16 - len;
if (len < 1)
@@ -6644,7 +6782,7 @@ route_vty_out_tmp (struct vty *vty, struct prefix *p, struct attr *attr, safi_t
else
vty_out (vty, " ");
- vty_out (vty, "%7u ", (attr->extra ? attr->extra->weight : 0));
+ vty_out (vty, "%7u ", attr->weight);
/* Print aspath */
if (attr->aspath)
@@ -6703,9 +6841,9 @@ route_vty_out_tag (struct vty *vty, struct prefix *p,
if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN)
{
if (json)
- json_object_string_add(json_out, "mpNexthopGlobalIn", inet_ntoa (attr->extra->mp_nexthop_global_in));
+ json_object_string_add(json_out, "mpNexthopGlobalIn", inet_ntoa (attr->mp_nexthop_global_in));
else
- vty_out (vty, "%-16s", inet_ntoa (attr->extra->mp_nexthop_global_in));
+ vty_out (vty, "%-16s", inet_ntoa (attr->mp_nexthop_global_in));
}
else
{
@@ -6719,36 +6857,35 @@ route_vty_out_tag (struct vty *vty, struct prefix *p,
|| (safi == SAFI_EVPN && p->family == AF_ETHERNET && BGP_ATTR_NEXTHOP_AFI_IP6(attr))
|| (BGP_ATTR_NEXTHOP_AFI_IP6(attr)))
{
- assert (attr->extra);
char buf_a[BUFSIZ];
char buf_b[BUFSIZ];
char buf_c[BUFSIZ];
- if (attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL)
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL)
{
if (json)
json_object_string_add(json_out, "mpNexthopGlobalIn",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global, buf_a, BUFSIZ));
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global, buf_a, BUFSIZ));
else
vty_out (vty, "%s",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global,
buf_a, BUFSIZ));
}
- else if (attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ else if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
{
if (json)
{
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global,
buf_a, BUFSIZ);
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_local,
buf_b, BUFSIZ);
sprintf(buf_c, "%s(%s)", buf_a, buf_b);
json_object_string_add(json_out, "mpNexthopGlobalLocal", buf_c);
}
else
vty_out (vty, "%s(%s)",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global,
buf_a, BUFSIZ),
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_local,
buf_b, BUFSIZ));
}
@@ -6799,60 +6936,52 @@ route_vty_out_overlay (struct vty *vty, struct prefix *p,
attr = binfo->attr;
if (attr)
{
- if (attr->extra)
- {
- char buf1[BUFSIZ];
- int af = NEXTHOP_FAMILY(attr->extra->mp_nexthop_len);
+ char buf1[BUFSIZ];
+ int af = NEXTHOP_FAMILY(attr->mp_nexthop_len);
- switch (af) {
- case AF_INET:
- vty_out (vty, "%-16s", inet_ntop(af,
- &attr->extra->mp_nexthop_global_in, buf, BUFSIZ));
- break;
- case AF_INET6:
- vty_out (vty, "%s(%s)",
- inet_ntop (af,
- &attr->extra->mp_nexthop_global, buf, BUFSIZ),
- inet_ntop (af,
- &attr->extra->mp_nexthop_local, buf1, BUFSIZ));
- break;
- default:
- vty_out(vty, "?");
- }
- } else {
+ switch (af) {
+ case AF_INET:
+ vty_out (vty, "%-16s", inet_ntop(af,
+ &attr->mp_nexthop_global_in, buf, BUFSIZ));
+ break;
+ case AF_INET6:
+ vty_out (vty, "%s(%s)",
+ inet_ntop (af,
+ &attr->mp_nexthop_global, buf, BUFSIZ),
+ inet_ntop (af,
+ &attr->mp_nexthop_local, buf1, BUFSIZ));
+ break;
+ default:
vty_out(vty, "?");
}
}
- if(attr->extra)
+ struct eth_segment_id *id = &(attr->evpn_overlay.eth_s_id);
+ char *str = esi2str(id);
+ vty_out (vty, "%s", str);
+ XFREE (MTYPE_TMP, str);
+ if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p))
{
- struct eth_segment_id *id = &(attr->extra->evpn_overlay.eth_s_id);
- char *str = esi2str(id);
- vty_out (vty, "%s", str);
- XFREE (MTYPE_TMP, str);
- if (IS_EVPN_PREFIX_IPADDR_V4((struct prefix_evpn *)p))
- {
- vty_out (vty, "/%s", inet_ntoa (attr->extra->evpn_overlay.gw_ip.ipv4));
- }
- else if (IS_EVPN_PREFIX_IPADDR_V6((struct prefix_evpn *)p))
- {
- vty_out (vty, "/%s",
- inet_ntop (AF_INET6, &(attr->extra->evpn_overlay.gw_ip.ipv6),
- buf, BUFSIZ));
- }
- if(attr->extra->ecommunity)
- {
- char *mac = NULL;
- struct ecommunity_val *routermac = ecommunity_lookup (attr->extra->ecommunity,
- ECOMMUNITY_ENCODE_EVPN,
- ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC);
- if(routermac)
- mac = ecom_mac2str((char *)routermac->val);
- if(mac)
- {
- vty_out (vty, "/%s",(char *)mac);
- XFREE(MTYPE_TMP, mac);
- }
+ vty_out (vty, "/%s", inet_ntoa (attr->evpn_overlay.gw_ip.ipv4));
+ }
+ else if (IS_EVPN_PREFIX_IPADDR_V6((struct prefix_evpn *)p))
+ {
+ vty_out (vty, "/%s",
+ inet_ntop (AF_INET6, &(attr->evpn_overlay.gw_ip.ipv6),
+ buf, BUFSIZ));
+ }
+ if(attr->ecommunity)
+ {
+ char *mac = NULL;
+ struct ecommunity_val *routermac = ecommunity_lookup (attr->ecommunity,
+ ECOMMUNITY_ENCODE_EVPN,
+ ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC);
+ if(routermac)
+ mac = ecom_mac2str((char *)routermac->val);
+ if(mac)
+ {
+ vty_out (vty, "/%s",(char *)mac);
+ XFREE(MTYPE_TMP, mac);
}
}
vty_out (vty, "\n");
@@ -7073,13 +7202,16 @@ route_vty_out_advertised_to (struct vty *vty, struct peer *peer, int *first,
}
}
-static void
+void
route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
struct bgp_info *binfo, afi_t afi, safi_t safi,
json_object *json_paths)
{
char buf[INET6_ADDRSTRLEN];
char buf1[BUFSIZ];
+#if defined(HAVE_CUMULUS)
+ char buf2[EVPN_ROUTE_STRLEN];
+#endif
struct attr *attr;
int sockunion_vty_out (struct vty *, union sockunion *);
time_t tbuf;
@@ -7109,6 +7241,39 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
json_nexthop_global = json_object_new_object();
}
+#if defined (HAVE_CUMULUS)
+ if (!json_paths && safi == SAFI_EVPN)
+ {
+ char tag_buf[20];
+
+ bgp_evpn_route2str ((struct prefix_evpn *)p, buf2, sizeof (buf2));
+ vty_out (vty, " Route %s", buf2);
+ tag_buf[0] = '\0';
+ if (binfo->extra)
+ {
+ bgp_evpn_label2str (&binfo->extra->label, tag_buf, sizeof (tag_buf));
+ vty_out (vty, " VNI %s", tag_buf);
+ }
+ vty_out (vty, "\n");
+ if (binfo->extra && binfo->extra->parent)
+ {
+ struct bgp_info *parent_ri;
+ struct bgp_node *rn, *prn;
+
+ parent_ri = (struct bgp_info *)binfo->extra->parent;
+ rn = parent_ri->net;
+ if (rn && rn->prn)
+ {
+ prn = rn->prn;
+ vty_out (vty, " Imported from %s:%s\n",
+ prefix_rd2str ((struct prefix_rd *)&prn->p,
+ buf1, RD_ADDRSTRLEN),
+ buf2);
+ }
+ }
+ }
+#endif
+
attr = binfo->attr;
if (attr)
@@ -7150,14 +7315,14 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
{
if (json_paths)
{
- json_object_int_add(json_path, "aggregatorAs", attr->extra->aggregator_as);
- json_object_string_add(json_path, "aggregatorId", inet_ntoa (attr->extra->aggregator_addr));
+ json_object_int_add(json_path, "aggregatorAs", attr->aggregator_as);
+ json_object_string_add(json_path, "aggregatorId", inet_ntoa (attr->aggregator_addr));
}
else
{
vty_out (vty, ", (aggregated by %u %s)",
- attr->extra->aggregator_as,
- inet_ntoa (attr->extra->aggregator_addr));
+ attr->aggregator_as,
+ inet_ntoa (attr->aggregator_addr));
}
}
@@ -7207,9 +7372,9 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN)
{
if (json_paths)
- json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->extra->mp_nexthop_global_in));
+ json_object_string_add(json_nexthop_global, "ip", inet_ntoa (attr->mp_nexthop_global_in));
else
- vty_out (vty, " %s", inet_ntoa (attr->extra->mp_nexthop_global_in));
+ vty_out (vty, " %s", inet_ntoa (attr->mp_nexthop_global_in));
}
else
{
@@ -7224,11 +7389,10 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
}
else
{
- assert (attr->extra);
if (json_paths)
{
json_object_string_add(json_nexthop_global, "ip",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global,
buf, INET6_ADDRSTRLEN));
json_object_string_add(json_nexthop_global, "afi", "ipv6");
json_object_string_add(json_nexthop_global, "scope", "global");
@@ -7236,7 +7400,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
else
{
vty_out (vty, " %s",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_global,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global,
buf, INET6_ADDRSTRLEN));
}
}
@@ -7275,7 +7439,8 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
if (binfo->peer == bgp->peer_self)
{
- if (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr))
+ if (safi == SAFI_EVPN ||
+ (p->family == AF_INET && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)))
{
if (json_paths)
json_object_string_add(json_peer, "peerId", "0.0.0.0");
@@ -7336,7 +7501,7 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
}
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
- vty_out (vty, " (%s)", inet_ntoa (attr->extra->originator_id));
+ vty_out (vty, " (%s)", inet_ntoa (attr->originator_id));
else
vty_out (vty, " (%s)", inet_ntop (AF_INET, &binfo->peer->remote_id, buf1, BUFSIZ));
}
@@ -7346,20 +7511,20 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
vty_out (vty, "\n");
/* display the link-local nexthop */
- if (attr->extra && attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
{
if (json_paths)
{
json_nexthop_ll = json_object_new_object();
json_object_string_add(json_nexthop_ll, "ip",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_local,
buf, INET6_ADDRSTRLEN));
json_object_string_add(json_nexthop_ll, "afi", "ipv6");
json_object_string_add(json_nexthop_ll, "scope", "link-local");
json_object_boolean_true_add(json_nexthop_ll, "accessible");
- if (!attr->extra->mp_nexthop_prefer_global)
+ if (!attr->mp_nexthop_prefer_global)
json_object_boolean_true_add(json_nexthop_ll, "used");
else
json_object_boolean_true_add(json_nexthop_global, "used");
@@ -7367,9 +7532,9 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
else
{
vty_out (vty, " (%s) %s\n",
- inet_ntop (AF_INET6, &attr->extra->mp_nexthop_local,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_local,
buf, INET6_ADDRSTRLEN),
- attr->extra->mp_nexthop_prefer_global ? "(prefer-global)" : "(used)");
+ attr->mp_nexthop_prefer_global ? "(prefer-global)" : "(used)");
}
}
/* If we do not have a link-local nexthop then we must flag the global as "used" */
@@ -7408,20 +7573,20 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
vty_out (vty, ", localpref %u", bgp->default_local_pref);
}
- if (attr->extra && attr->extra->weight != 0)
+ if (attr->weight != 0)
{
if (json_paths)
- json_object_int_add(json_path, "weight", attr->extra->weight);
+ json_object_int_add(json_path, "weight", attr->weight);
else
- vty_out (vty, ", weight %u", attr->extra->weight);
+ vty_out (vty, ", weight %u", attr->weight);
}
- if (attr->extra && attr->extra->tag != 0)
+ if (attr->tag != 0)
{
if (json_paths)
- json_object_int_add(json_path, "tag", attr->extra->tag);
+ json_object_int_add(json_path, "tag", attr->tag);
else
- vty_out (vty, ", tag %"ROUTE_TAG_PRI, attr->extra->tag);
+ vty_out (vty, ", tag %"ROUTE_TAG_PRI, attr->tag);
}
if (! CHECK_FLAG (binfo->flags, BGP_INFO_VALID))
@@ -7584,33 +7749,32 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
if (json_paths)
{
json_ext_community = json_object_new_object();
- json_object_string_add(json_ext_community, "string", attr->extra->ecommunity->str);
+ json_object_string_add(json_ext_community, "string", attr->ecommunity->str);
json_object_object_add(json_path, "extendedCommunity", json_ext_community);
}
else
{
vty_out (vty, " Extended Community: %s\n",
- attr->extra->ecommunity->str);
+ attr->ecommunity->str);
}
}
/* Line 6 display Large community */
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))
vty_out (vty, " Large Community: %s\n",
- attr->extra->lcommunity->str);
+ attr->lcommunity->str);
/* Line 7 display Originator, Cluster-id */
if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) ||
(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)))
{
- assert (attr->extra);
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
{
if (json_paths)
- json_object_string_add(json_path, "originatorId", inet_ntoa (attr->extra->originator_id));
+ json_object_string_add(json_path, "originatorId", inet_ntoa (attr->originator_id));
else
vty_out (vty, " Originator: %s",
- inet_ntoa (attr->extra->originator_id));
+ inet_ntoa (attr->originator_id));
}
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))
@@ -7622,16 +7786,16 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
json_cluster_list = json_object_new_object();
json_cluster_list_list = json_object_new_array();
- for (i = 0; i < attr->extra->cluster->length / 4; i++)
+ for (i = 0; i < attr->cluster->length / 4; i++)
{
- json_string = json_object_new_string(inet_ntoa (attr->extra->cluster->list[i]));
+ json_string = json_object_new_string(inet_ntoa (attr->cluster->list[i]));
json_object_array_add(json_cluster_list_list, json_string);
}
/* struct cluster_list does not have "str" variable like
* aspath and community do. Add this someday if someone
* asks for it.
- json_object_string_add(json_cluster_list, "string", attr->extra->cluster->str);
+ json_object_string_add(json_cluster_list, "string", attr->cluster->str);
*/
json_object_object_add(json_cluster_list, "list", json_cluster_list_list);
json_object_object_add(json_path, "clusterList", json_cluster_list);
@@ -7640,10 +7804,10 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
{
vty_out (vty, ", Cluster list: ");
- for (i = 0; i < attr->extra->cluster->length / 4; i++)
+ for (i = 0; i < attr->cluster->length / 4; i++)
{
vty_out (vty, "%s ",
- inet_ntoa (attr->extra->cluster->list[i]));
+ inet_ntoa (attr->cluster->list[i]));
}
}
}
@@ -7656,7 +7820,11 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
bgp_damp_info_vty (vty, binfo, json_path);
/* Remote Label */
+#if defined (HAVE_CUMULUS)
+ if (binfo->extra && bgp_is_valid_label(&binfo->extra->label) && safi != SAFI_EVPN)
+#else
if (binfo->extra && bgp_is_valid_label(&binfo->extra->label))
+#endif
{
mpls_label_t label = label_pton(&binfo->extra->label);
if (json_paths)
@@ -7666,13 +7834,13 @@ route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
}
/* Label Index */
- if (attr->extra->label_index != BGP_INVALID_LABEL_INDEX)
+ if (attr->label_index != BGP_INVALID_LABEL_INDEX)
{
if (json_paths)
- json_object_int_add(json_path, "labelIndex", attr->extra->label_index);
+ json_object_int_add(json_path, "labelIndex", attr->label_index);
else
vty_out (vty, " Label Index: %d\n",
- attr->extra->label_index);
+ attr->label_index);
}
/* Line 8 display Addpath IDs */
@@ -7878,10 +8046,8 @@ bgp_show_table (struct vty *vty, struct bgp *bgp, struct bgp_table *table,
struct route_map *rmap = output_arg;
struct bgp_info binfo;
struct attr dummy_attr;
- struct attr_extra dummy_extra;
int ret;
- dummy_attr.extra = &dummy_extra;
bgp_attr_dup (&dummy_attr, ri->attr);
binfo.peer = ri->peer;
@@ -7959,21 +8125,20 @@ bgp_show_table (struct vty *vty, struct bgp *bgp, struct bgp_table *table,
{
struct lcommunity *lcom = output_arg;
- if (! ri->attr->extra || ! ri->attr->extra->lcommunity ||
- ! lcommunity_match (ri->attr->extra->lcommunity, lcom))
+ if (! ri->attr->lcommunity ||
+ ! lcommunity_match (ri->attr->lcommunity, lcom))
continue;
}
if (type == bgp_show_type_lcommunity_list)
{
struct community_list *list = output_arg;
- if (! ri->attr->extra ||
- ! lcommunity_list_match (ri->attr->extra->lcommunity, list))
+ if (! lcommunity_list_match (ri->attr->lcommunity, list))
continue;
}
if (type == bgp_show_type_lcommunity_all)
{
- if (! ri->attr->extra || ! ri->attr->extra->lcommunity)
+ if (! ri->attr->lcommunity)
continue;
}
if (type == bgp_show_type_dampend_paths
@@ -8120,7 +8285,7 @@ bgp_show_all_instances_routes_vty (struct vty *vty, afi_t afi, safi_t safi,
}
/* Header of detailed BGP route information */
-static void
+void
route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
struct bgp_node *rn,
struct prefix_rd *prd, afi_t afi, safi_t safi,
@@ -8132,6 +8297,9 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
struct listnode *node, *nnode;
char buf1[INET6_ADDRSTRLEN];
char buf2[INET6_ADDRSTRLEN];
+#if defined(HAVE_CUMULUS)
+ char buf3[EVPN_ROUTE_STRLEN];
+#endif
int count = 0;
int best = 0;
int suppress = 0;
@@ -8159,6 +8327,21 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
}
else
{
+#if defined (HAVE_CUMULUS)
+ if (safi == SAFI_EVPN)
+ vty_out (vty, "BGP routing table entry for %s%s%s\n",
+ prd ? prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : "",
+ prd ? ":" : "",
+ bgp_evpn_route2str ((struct prefix_evpn *)p,
+ buf3, sizeof (buf3)));
+ else
+ vty_out (vty, "BGP routing table entry for %s%s%s/%d\n",
+ ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) ?
+ prefix_rd2str (prd, buf1, RD_ADDRSTRLEN) : ""),
+ safi == SAFI_MPLS_VPN ? ":" : "",
+ inet_ntop (p->family, &p->u.prefix, buf2, INET6_ADDRSTRLEN),
+ p->prefixlen);
+#else
if (p->family == AF_ETHERNET)
prefix2str (p, buf2, INET6_ADDRSTRLEN);
else
@@ -8169,11 +8352,16 @@ route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
((safi == SAFI_MPLS_VPN) || (safi == SAFI_EVPN)) ? ":" : "",
buf2,
p->prefixlen);
+#endif
if (has_valid_label)
vty_out (vty, "Local label: %d\n", label);
- else if (bgp_labeled_safi(safi))
- vty_out (vty, "Local label: not allocated\n");
+#if defined (HAVE_CUMULUS)
+ if (bgp_labeled_safi(safi) && safi != SAFI_EVPN)
+#else
+ if (bgp_labeled_safi(safi))
+#endif
+ vty_out(vty, "not allocated\n");
}
for (ri = rn->info; ri; ri = ri->next)
@@ -9591,7 +9779,6 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
struct bgp *bgp;
int header2 = 1;
struct attr attr;
- struct attr_extra extra;
int ret;
struct update_subgroup *subgrp;
json_object *json_scode = NULL;
@@ -9663,7 +9850,6 @@ show_adj_route (struct vty *vty, struct peer *peer, afi_t afi, safi_t safi,
header1 = 0;
}
- attr.extra = &extra;
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
{
if (in)
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index 54a56456d..907690381 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -99,6 +99,9 @@ struct bgp_info_extra
} vnc;
#endif
+
+ /* For imported routes into a VNI (or VRF), this points to the parent. */
+ void *parent;
};
struct bgp_info
@@ -174,6 +177,13 @@ struct bgp_info
};
+/* Structure used in BGP path selection */
+struct bgp_info_pair
+{
+ struct bgp_info *old;
+ struct bgp_info *new;
+};
+
/* BGP static route configuration. */
struct bgp_static
{
@@ -222,8 +232,8 @@ struct bgp_static
#define BGP_ATTR_NEXTHOP_AFI_IP6(attr) \
(! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)) && \
- (attr)->extra && ((attr)->extra->mp_nexthop_len == 16 || \
- (attr)->extra->mp_nexthop_len == 32))
+ ((attr)->mp_nexthop_len == 16 || \
+ (attr)->mp_nexthop_len == 32))
#define BGP_INFO_COUNTABLE(BI) \
(! CHECK_FLAG ((BI)->flags, BGP_INFO_HISTORY) \
&& ! CHECK_FLAG ((BI)->flags, BGP_INFO_REMOVED))
@@ -309,6 +319,7 @@ extern struct bgp_node *bgp_afi_node_get (struct bgp_table *table, afi_t afi,
extern struct bgp_info *bgp_info_lock (struct bgp_info *);
extern struct bgp_info *bgp_info_unlock (struct bgp_info *);
extern void bgp_info_add (struct bgp_node *rn, struct bgp_info *ri);
+extern void bgp_info_reap (struct bgp_node *rn, struct bgp_info *ri);
extern void bgp_info_delete (struct bgp_node *rn, struct bgp_info *ri);
extern struct bgp_info_extra *bgp_info_extra_get (struct bgp_info *);
extern void bgp_info_set_flag (struct bgp_node *, struct bgp_info *, u_int32_t);
@@ -372,6 +383,10 @@ extern u_char bgp_distance_apply (struct prefix *, struct bgp_info *, afi_t, saf
extern afi_t bgp_node_afi (struct vty *);
extern safi_t bgp_node_safi (struct vty *);
+extern struct bgp_info *
+info_make (int type, int sub_type, u_short instance, struct peer *peer,
+ struct attr *attr, struct bgp_node *rn);
+
extern void route_vty_out (struct vty *, struct prefix *, struct bgp_info *, int, safi_t, json_object *);
extern void route_vty_out_tag (struct vty *, struct prefix *, struct bgp_info *, int, safi_t, json_object *);
extern void route_vty_out_tmp (struct vty *, struct prefix *, struct attr *, safi_t, u_char, json_object *);
@@ -396,10 +411,32 @@ extern void bgp_process_queues_drain_immediate (void);
extern struct bgp_node *
bgp_afi_node_get (struct bgp_table *, afi_t , safi_t , struct prefix *,
struct prefix_rd *);
+extern struct bgp_node *
+bgp_afi_node_lookup (struct bgp_table *table, afi_t afi, safi_t safi,
+ struct prefix *p, struct prefix_rd *prd);
extern struct bgp_info *bgp_info_new (void);
extern void bgp_info_restore (struct bgp_node *, struct bgp_info *);
-extern int bgp_info_cmp_compatible (struct bgp *, struct bgp_info *,
- struct bgp_info *, afi_t, safi_t );
+extern int
+bgp_info_cmp_compatible (struct bgp *, struct bgp_info *, struct bgp_info *,
+ char *pfx_buf, afi_t afi, safi_t safi);
+
+extern void
+bgp_best_selection (struct bgp *bgp, struct bgp_node *rn,
+ struct bgp_maxpaths_cfg *mpath_cfg,
+ struct bgp_info_pair *result,
+ afi_t afi, safi_t safi);
+extern void bgp_zebra_clear_route_change_flags (struct bgp_node *rn);
+extern int
+bgp_zebra_has_route_changed (struct bgp_node *rn, struct bgp_info *selected);
+extern void
+route_vty_out_detail_header (struct vty *vty, struct bgp *bgp,
+ struct bgp_node *rn,
+ struct prefix_rd *prd, afi_t afi, safi_t safi,
+ json_object *json);
+extern void
+route_vty_out_detail (struct vty *vty, struct bgp *bgp, struct prefix *p,
+ struct bgp_info *binfo, afi_t afi, safi_t safi,
+ json_object *json_paths);
#endif /* _QUAGGA_BGP_ROUTE_H */
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index 140c58f37..b53c112d9 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -868,8 +868,7 @@ route_match_lcommunity (void *rule, struct prefix *prefix,
if (! list)
return RMAP_NOMATCH;
- if (bgp_info->attr->extra &&
- lcommunity_list_match (bgp_info->attr->extra->lcommunity, list))
+ if (lcommunity_list_match (bgp_info->attr->lcommunity, list))
return RMAP_MATCH;
}
@@ -933,15 +932,12 @@ route_match_ecommunity (void *rule, struct prefix *prefix,
{
bgp_info = object;
- if (!bgp_info->attr->extra)
- return RMAP_NOMATCH;
-
list = community_list_lookup (bgp_clist, (char *) rule,
EXTCOMMUNITY_LIST_MASTER);
if (! list)
return RMAP_NOMATCH;
- if (ecommunity_list_match (bgp_info->attr->extra->ecommunity, list))
+ if (ecommunity_list_match (bgp_info->attr->ecommunity, list))
return RMAP_MATCH;
}
return RMAP_NOMATCH;
@@ -1149,10 +1145,7 @@ route_match_tag (void *rule, struct prefix *prefix,
tag = rule;
bgp_info = object;
- if (!bgp_info->attr->extra)
- return RMAP_NOMATCH;
-
- return ((bgp_info->attr->extra->tag == *tag)? RMAP_MATCH : RMAP_NOMATCH);
+ return ((bgp_info->attr->tag == *tag)? RMAP_MATCH : RMAP_NOMATCH);
}
return RMAP_NOMATCH;
@@ -1332,7 +1325,6 @@ route_set_weight (void *rule, struct prefix *prefix, route_map_object_t type,
{
struct rmap_value *rv;
struct bgp_info *bgp_info;
- u_int32_t weight;
if (type == RMAP_BGP)
{
@@ -1341,11 +1333,7 @@ route_set_weight (void *rule, struct prefix *prefix, route_map_object_t type,
bgp_info = object;
/* Set weight value. */
- weight = route_value_adjust(rv, 0, bgp_info->peer);
- if (weight)
- (bgp_attr_extra_get (bgp_info->attr))->weight = weight;
- else if (bgp_info->attr->extra)
- bgp_info->attr->extra->weight = 0;
+ bgp_info->attr->weight = route_value_adjust(rv, 0, bgp_info->peer);
}
return RMAP_OKAY;
@@ -1644,14 +1632,13 @@ route_set_lcommunity (void *rule, struct prefix *prefix,
rcs = rule;
binfo = object;
attr = binfo->attr;
- old = (attr->extra) ? attr->extra->lcommunity : NULL;
+ old = attr->lcommunity;
/* "none" case. */
if (rcs->none)
{
attr->flag &= ~(ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES));
- if (attr->extra)
- attr->extra->lcommunity = NULL;
+ attr->lcommunity = NULL;
/* See the longer comment down below. */
if (old && old->refcnt == 0)
@@ -1676,7 +1663,7 @@ route_set_lcommunity (void *rule, struct prefix *prefix,
new = lcommunity_dup (rcs->lcom);
/* will be intern()'d or attr_flush()'d by bgp_update_main() */
- (bgp_attr_extra_get (attr))->lcommunity = new;
+ attr->lcommunity = new;
attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
}
@@ -1766,7 +1753,7 @@ route_set_lcommunity_delete (void *rule, struct prefix *prefix,
binfo = object;
list = community_list_lookup (bgp_clist, rule,
LARGE_COMMUNITY_LIST_MASTER);
- old = ((binfo->attr->extra) ? binfo->attr->extra->lcommunity : NULL);
+ old = binfo->attr->lcommunity;
if (list && old)
{
@@ -1783,13 +1770,13 @@ route_set_lcommunity_delete (void *rule, struct prefix *prefix,
if (new->size == 0)
{
- binfo->attr->extra->lcommunity = NULL;
+ binfo->attr->lcommunity = NULL;
binfo->attr->flag &= ~ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
lcommunity_free (&new);
}
else
{
- binfo->attr->extra->lcommunity = new;
+ binfo->attr->lcommunity = new;
binfo->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LARGE_COMMUNITIES);
}
}
@@ -1946,7 +1933,7 @@ route_set_ecommunity (void *rule, struct prefix *prefix,
return RMAP_OKAY;
/* We assume additive for Extended Community. */
- old_ecom = (bgp_attr_extra_get (bgp_info->attr))->ecommunity;
+ old_ecom = bgp_info->attr->ecommunity;
if (old_ecom)
{
@@ -1961,7 +1948,7 @@ route_set_ecommunity (void *rule, struct prefix *prefix,
new_ecom = ecommunity_dup (ecom);
/* will be intern()'d or attr_flush()'d by bgp_update_main() */
- bgp_info->attr->extra->ecommunity = new_ecom;
+ bgp_info->attr->ecommunity = new_ecom;
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
}
@@ -2129,16 +2116,14 @@ route_set_aggregator_as (void *rule, struct prefix *prefix,
{
struct bgp_info *bgp_info;
struct aggregator *aggregator;
- struct attr_extra *ae;
if (type == RMAP_BGP)
{
bgp_info = object;
aggregator = rule;
- ae = bgp_attr_extra_get (bgp_info->attr);
- ae->aggregator_as = aggregator->as;
- ae->aggregator_addr = aggregator->address;
+ bgp_info->attr->aggregator_as = aggregator->as;
+ bgp_info->attr->aggregator_addr = aggregator->address;
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
}
@@ -2187,16 +2172,14 @@ route_set_tag (void *rule, struct prefix *prefix,
{
route_tag_t *tag;
struct bgp_info *bgp_info;
- struct attr_extra *ae;
if (type == RMAP_BGP)
{
tag = rule;
bgp_info = object;
- ae = bgp_attr_extra_get (bgp_info->attr);
/* Set tag value */
- ae->tag=*tag;
+ bgp_info->attr->tag=*tag;
}
@@ -2231,7 +2214,7 @@ route_set_label_index (void *rule, struct prefix *prefix,
label_index = rv->value;
if (label_index)
{
- (bgp_attr_extra_get (bgp_info->attr))->label_index = label_index;
+ bgp_info->attr->label_index = label_index;
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_PREFIX_SID);
}
}
@@ -2302,14 +2285,11 @@ route_match_ipv6_next_hop (void *rule, struct prefix *prefix,
{
bgp_info = object;
- if (!bgp_info->attr->extra)
- return RMAP_NOMATCH;
-
- if (IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_global, addr))
+ if (IPV6_ADDR_SAME (&bgp_info->attr->mp_nexthop_global, addr))
return RMAP_MATCH;
- if (bgp_info->attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
- IPV6_ADDR_SAME (&bgp_info->attr->extra->mp_nexthop_local, rule))
+ if (bgp_info->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL &&
+ IPV6_ADDR_SAME (&bgp_info->attr->mp_nexthop_local, rule))
return RMAP_MATCH;
return RMAP_NOMATCH;
@@ -2407,11 +2387,11 @@ route_set_ipv6_nexthop_global (void *rule, struct prefix *prefix,
bgp_info = object;
/* Set next hop value. */
- (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global = *address;
+ bgp_info->attr->mp_nexthop_global = *address;
/* Set nexthop length. */
- if (bgp_info->attr->extra->mp_nexthop_len == 0)
- bgp_info->attr->extra->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ if (bgp_info->attr->mp_nexthop_len == 0)
+ bgp_info->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
SET_FLAG(bgp_info->attr->rmap_change_flags,
BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED);
@@ -2477,13 +2457,13 @@ route_set_ipv6_nexthop_prefer_global (void *rule, struct prefix *prefix,
&& sockunion_family (peer->su_remote) == AF_INET6)
{
/* Set next hop preference to global */
- bgp_info->attr->extra->mp_nexthop_prefer_global = TRUE;
+ bgp_info->attr->mp_nexthop_prefer_global = TRUE;
SET_FLAG(bgp_info->attr->rmap_change_flags,
BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED);
}
else
{
- bgp_info->attr->extra->mp_nexthop_prefer_global = FALSE;
+ bgp_info->attr->mp_nexthop_prefer_global = FALSE;
SET_FLAG(bgp_info->attr->rmap_change_flags,
BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED);
}
@@ -2535,11 +2515,11 @@ route_set_ipv6_nexthop_local (void *rule, struct prefix *prefix,
bgp_info = object;
/* Set next hop value. */
- (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_local = *address;
+ bgp_info->attr->mp_nexthop_local = *address;
/* Set nexthop length. */
- if (bgp_info->attr->extra->mp_nexthop_len != BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
- bgp_info->attr->extra->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
+ if (bgp_info->attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ bgp_info->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
SET_FLAG(bgp_info->attr->rmap_change_flags,
BATTR_RMAP_IPV6_LL_NHOP_CHANGED);
@@ -2611,15 +2591,15 @@ route_set_ipv6_nexthop_peer (void *rule, struct prefix *prefix,
/* Set next hop value and length in attribute. */
if (IN6_IS_ADDR_LINKLOCAL(&peer_address))
{
- (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_local = peer_address;
- if (bgp_info->attr->extra->mp_nexthop_len != 32)
- bgp_info->attr->extra->mp_nexthop_len = 32;
+ bgp_info->attr->mp_nexthop_local = peer_address;
+ if (bgp_info->attr->mp_nexthop_len != 32)
+ bgp_info->attr->mp_nexthop_len = 32;
}
else
{
- (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global = peer_address;
- if (bgp_info->attr->extra->mp_nexthop_len == 0)
- bgp_info->attr->extra->mp_nexthop_len = 16;
+ bgp_info->attr->mp_nexthop_global = peer_address;
+ if (bgp_info->attr->mp_nexthop_len == 0)
+ bgp_info->attr->mp_nexthop_len = 16;
}
}
@@ -2633,9 +2613,9 @@ route_set_ipv6_nexthop_peer (void *rule, struct prefix *prefix,
SET_FLAG(bgp_info->attr->rmap_change_flags,
BATTR_RMAP_NEXTHOP_PEER_ADDRESS);
/* clear next hop value. */
- memset (&((bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global),
+ memset (&(bgp_info->attr->mp_nexthop_global),
0, sizeof (struct in6_addr));
- memset (&((bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_local),
+ memset (&(bgp_info->attr->mp_nexthop_local),
0, sizeof (struct in6_addr));
}
}
@@ -2688,8 +2668,8 @@ route_set_vpnv4_nexthop (void *rule, struct prefix *prefix,
bgp_info = object;
/* Set next hop value. */
- (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global_in = *address;
- (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_len = 4;
+ bgp_info->attr->mp_nexthop_global_in = *address;
+ bgp_info->attr->mp_nexthop_len = 4;
}
return RMAP_OKAY;
@@ -2730,8 +2710,8 @@ route_set_vpnv6_nexthop (void *rule, struct prefix *prefix,
bgp_info = object;
/* Set next hop value. */
- memcpy (&(bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_global, address, sizeof(struct in6_addr));
- (bgp_attr_extra_get (bgp_info->attr))->mp_nexthop_len = BGP_ATTR_NHLEN_VPNV6_GLOBAL;
+ memcpy (&bgp_info->attr->mp_nexthop_global, address, sizeof(struct in6_addr));
+ bgp_info->attr->mp_nexthop_len = BGP_ATTR_NHLEN_VPNV6_GLOBAL;
}
return RMAP_OKAY;
@@ -2794,7 +2774,7 @@ route_set_originator_id (void *rule, struct prefix *prefix, route_map_object_t t
bgp_info = object;
bgp_info->attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
- (bgp_attr_extra_get (bgp_info->attr))->originator_id = *address;
+ bgp_info->attr->originator_id = *address;
}
return RMAP_OKAY;
@@ -3112,6 +3092,9 @@ bgp_route_map_process_update (struct bgp *bgp, const char *rmap_name, int route_
for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
{
/* For table route-map updates. */
+ if (!bgp_fibupd_safi(safi))
+ continue;
+
if (bgp->table_map[afi][safi].name &&
(strcmp(rmap_name, bgp->table_map[afi][safi].name) == 0))
{
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
index db69400a6..c1a6ad589 100644
--- a/bgpd/bgp_snmp.c
+++ b/bgpd/bgp_snmp.c
@@ -802,16 +802,10 @@ bgp4PathAttrTable (struct variable *v, oid name[], size_t *length,
return SNMP_INTEGER (1);
break;
case BGP4PATHATTRAGGREGATORAS: /* 10 */
- if (binfo->attr->extra)
- return SNMP_INTEGER (binfo->attr->extra->aggregator_as);
- else
- return SNMP_INTEGER (0);
+ return SNMP_INTEGER (binfo->attr->aggregator_as);
break;
case BGP4PATHATTRAGGREGATORADDR: /* 11 */
- if (binfo->attr->extra)
- return SNMP_IPADDRESS (binfo->attr->extra->aggregator_addr);
- else
- return SNMP_INTEGER (0);
+ return SNMP_IPADDRESS (binfo->attr->aggregator_addr);
break;
case BGP4PATHATTRCALCLOCALPREF: /* 12 */
return SNMP_INTEGER (-1);
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index 6524e8435..5019830c0 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -586,7 +586,6 @@ subgroup_announce_table (struct update_subgroup *subgrp,
struct bgp_node *rn;
struct bgp_info *ri;
struct attr attr;
- struct attr_extra extra;
struct peer *peer;
afi_t afi;
safi_t safi;
@@ -609,9 +608,6 @@ subgroup_announce_table (struct update_subgroup *subgrp,
&& CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_DEFAULT_ORIGINATE))
subgroup_default_originate (subgrp, 0);
- /* It's initialized in bgp_announce_check() */
- attr.extra = &extra;
-
for (rn = bgp_table_top (table); rn; rn = bgp_route_next (rn))
for (ri = rn->info; ri; ri = ri->next)
@@ -715,18 +711,16 @@ subgroup_default_originate (struct update_subgroup *subgrp, int withdraw)
str2prefix ("0.0.0.0/0", &p);
else if (afi == AFI_IP6)
{
- struct attr_extra *ae = attr.extra;
-
str2prefix ("::/0", &p);
/* IPv6 global nexthop must be included. */
- ae->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
/* If the peer is on shared nextwork and we have link-local
nexthop set it. */
if (peer->shared_network
&& !IN6_IS_ADDR_UNSPECIFIED (&peer->nexthop.v6_local))
- ae->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
}
if (peer->default_rmap[afi][safi].name)
@@ -738,11 +732,9 @@ subgroup_default_originate (struct update_subgroup *subgrp, int withdraw)
for (ri = rn->info; ri; ri = ri->next)
{
struct attr dummy_attr;
- struct attr_extra dummy_extra;
struct bgp_info info;
/* Provide dummy so the route-map can't modify the attributes */
- dummy_attr.extra = &dummy_extra;
bgp_attr_dup (&dummy_attr, ri->attr);
info.peer = ri->peer;
info.attr = &dummy_attr;
@@ -793,7 +785,6 @@ subgroup_default_originate (struct update_subgroup *subgrp, int withdraw)
}
}
- bgp_attr_extra_free (&attr);
aspath_unintern (&aspath);
}
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
index 69debf7a4..0ef4d4c0c 100644
--- a/bgpd/bgp_updgrp_packet.c
+++ b/bgpd/bgp_updgrp_packet.c
@@ -423,12 +423,10 @@ bpacket_reformat_for_peer (struct bpacket *pkt, struct peer_af *paf)
afi_t nhafi = AFI_MAX; /* NH AFI is based on nhlen! */
int route_map_sets_nh;
nhlen = stream_getc_from (s, vec->offset);
- if (paf->afi == AFI_IP || paf->afi == AFI_IP6)
- {
- nhafi = BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen);
- if (peer_cap_enhe(peer, paf->afi, paf->safi))
- nhafi = AFI_IP6;
- }
+ if (peer_cap_enhe(peer, paf->afi, paf->safi))
+ nhafi = AFI_IP6;
+ else
+ nhafi = BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen);
if (nhafi == AFI_IP)
{
@@ -484,6 +482,7 @@ bpacket_reformat_for_peer (struct bpacket *pkt, struct peer_af *paf)
nh_modified = 1;
}
else if (peer->sort == BGP_PEER_EBGP &&
+ paf->safi != SAFI_EVPN &&
(bgp_multiaccess_check_v4 (v4nh, peer) == 0) &&
!CHECK_FLAG(vec->flags,
BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED) &&
@@ -609,6 +608,30 @@ bpacket_reformat_for_peer (struct bpacket *pkt, struct peer_af *paf)
(nhlen == 24 ? " and RD" : ""));
}
}
+ else if (paf->afi == AFI_L2VPN)
+ {
+ struct in_addr v4nh, *mod_v4nh;
+ int nh_modified = 0;
+
+ stream_get_from (&v4nh, s, vec->offset + 1, 4);
+ mod_v4nh = &v4nh;
+
+ /* No route-map changes allowed for EVPN nexthops. */
+ if (!v4nh.s_addr)
+ {
+ mod_v4nh = &peer->nexthop.v4;
+ nh_modified = 1;
+ }
+
+ if (nh_modified)
+ stream_put_in_addr_at (s, vec->offset + 1, mod_v4nh);
+
+ if (bgp_debug_update(peer, NULL, NULL, 0))
+ zlog_debug ("u%" PRIu64 ":s%" PRIu64 " %s send UPDATE w/ nexthop %s",
+ PAF_SUBGRP(paf)->update_group->id, PAF_SUBGRP(paf)->id,
+ peer->host, inet_ntoa (*mod_v4nh));
+
+ }
}
bgp_packet_add (peer, s);
@@ -681,7 +704,7 @@ subgroup_update_packet (struct update_subgroup *subgrp)
int addpath_encode = 0;
u_int32_t addpath_tx_id = 0;
struct prefix_rd *prd = NULL;
- char label_buf[20];
+ mpls_label_t label = MPLS_INVALID_LABEL;
if (!subgrp)
return NULL;
@@ -696,7 +719,6 @@ subgroup_update_packet (struct update_subgroup *subgrp)
stream_reset (s);
snlri = subgrp->scratch;
stream_reset (snlri);
- label_buf[0] = '\0';
bpacket_attr_vec_arr_reset (&vecarr);
@@ -785,8 +807,6 @@ subgroup_update_packet (struct update_subgroup *subgrp)
else
{
/* Encode the prefix in MP_REACH_NLRI attribute */
- mpls_label_t label = MPLS_INVALID_LABEL;
-
if (rn->prn)
prd = (struct prefix_rd *) &rn->prn->p;
@@ -796,9 +816,6 @@ subgroup_update_packet (struct update_subgroup *subgrp)
if (binfo && binfo->extra)
label = binfo->extra->label;
- if (bgp_labeled_safi(safi))
- sprintf (label_buf, "label %u", label_pton(&label));
-
if (stream_empty (snlri))
mpattrlen_pos = bgp_packet_mpattr_start (snlri, peer, afi, safi,
&vecarr, adv->baa->attr);
@@ -831,12 +848,11 @@ subgroup_update_packet (struct update_subgroup *subgrp)
send_attr_printed = 1;
}
- zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s %s",
- subgrp->update_group->id, subgrp->id,
- bgp_debug_rdpfxpath2str (prd, &rn->p, addpath_encode,
- addpath_tx_id,
- pfx_buf, sizeof (pfx_buf)),
- label_buf);
+ bgp_debug_rdpfxpath2str (afi, safi, prd, &rn->p, &label,
+ addpath_encode, addpath_tx_id,
+ pfx_buf, sizeof (pfx_buf));
+ zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s",
+ subgrp->update_group->id, subgrp->id, pfx_buf);
}
/* Synchnorize attribute. */
@@ -989,11 +1005,11 @@ subgroup_withdraw_packet (struct update_subgroup *subgrp)
{
char pfx_buf[BGP_PRD_PATH_STRLEN];
+ bgp_debug_rdpfxpath2str (afi, safi, prd, &rn->p, NULL,
+ addpath_encode, addpath_tx_id,
+ pfx_buf, sizeof (pfx_buf));
zlog_debug ("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s -- unreachable",
- subgrp->update_group->id, subgrp->id,
- bgp_debug_rdpfxpath2str (prd, &rn->p,
- addpath_encode, addpath_tx_id,
- pfx_buf, sizeof (pfx_buf)));
+ subgrp->update_group->id, subgrp->id, pfx_buf);
}
subgrp->scount--;
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index 903793624..9d0f6b44d 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -6711,10 +6711,6 @@ DEFUN (show_bgp_memory,
vty_out (vty, "%ld BGP attributes, using %s of memory\n", count,
mtype_memstr (memstrbuf, sizeof (memstrbuf),
count * sizeof(struct attr)));
- if ((count = mtype_stats_alloc (MTYPE_ATTR_EXTRA)))
- vty_out (vty, "%ld BGP extra attributes, using %s of memory\n", count,
- mtype_memstr (memstrbuf, sizeof (memstrbuf),
- count * sizeof(struct attr_extra)));
if ((count = attr_unknown_count()))
vty_out (vty, "%ld unknown attributes\n", count);
@@ -7261,7 +7257,7 @@ bgp_show_all_instances_summary_vty (struct vty *vty, afi_t afi, safi_t safi,
}
-static int
+int
bgp_show_summary_vty (struct vty *vty, const char *name,
afi_t afi, safi_t safi, u_char use_json)
{
@@ -10665,6 +10661,13 @@ static struct cmd_node bgp_evpn_node =
1
};
+static struct cmd_node bgp_evpn_vni_node =
+{
+ BGP_EVPN_VNI_NODE,
+ "%s(config-router-af-vni)# ",
+ 1
+};
+
static void community_list_vty (void);
static void
@@ -10734,6 +10737,7 @@ bgp_vty_init (void)
install_node (&bgp_vpnv4_node, NULL);
install_node (&bgp_vpnv6_node, NULL);
install_node (&bgp_evpn_node, NULL);
+ install_node (&bgp_evpn_vni_node, NULL);
/* Install default VTY commands to new nodes. */
install_default (BGP_NODE);
@@ -10746,6 +10750,7 @@ bgp_vty_init (void)
install_default (BGP_VPNV4_NODE);
install_default (BGP_VPNV6_NODE);
install_default (BGP_EVPN_NODE);
+ install_default (BGP_EVPN_VNI_NODE);
/* "bgp multiple-instance" commands. */
install_element (CONFIG_NODE, &bgp_multiple_instance_cmd);
@@ -11236,6 +11241,8 @@ bgp_vty_init (void)
install_element (BGP_VPNV4_NODE, &no_neighbor_route_reflector_client_cmd);
install_element (BGP_VPNV6_NODE, &neighbor_route_reflector_client_cmd);
install_element (BGP_VPNV6_NODE, &no_neighbor_route_reflector_client_cmd);
+ install_element (BGP_EVPN_NODE, &neighbor_route_reflector_client_cmd);
+ install_element (BGP_EVPN_NODE, &no_neighbor_route_reflector_client_cmd);
/* "neighbor route-server" commands.*/
install_element (BGP_NODE, &neighbor_route_server_client_hidden_cmd);
@@ -11609,6 +11616,8 @@ bgp_vty_init (void)
install_element (BGP_VPNV4_NODE, &no_neighbor_allowas_in_cmd);
install_element (BGP_VPNV6_NODE, &neighbor_allowas_in_cmd);
install_element (BGP_VPNV6_NODE, &no_neighbor_allowas_in_cmd);
+ install_element (BGP_EVPN_NODE, &neighbor_allowas_in_cmd);
+ install_element (BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd);
/* address-family commands. */
install_element (BGP_NODE, &address_family_ipv4_safi_cmd);
diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
index edc994a57..62228c64e 100644
--- a/bgpd/bgp_vty.h
+++ b/bgpd/bgp_vty.h
@@ -69,4 +69,7 @@ argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index, safi_t *
extern int
bgp_vty_find_and_parse_afi_safi_bgp (struct vty *vty, struct cmd_token **argv, int argc, int *idx,
afi_t *afi, safi_t *safi, struct bgp **bgp);
+extern int
+bgp_show_summary_vty (struct vty *vty, const char *name,
+ afi_t afi, safi_t safi, u_char use_json);
#endif /* _QUAGGA_BGP_VTY_H */
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
index a45dedd02..f267e7a07 100644
--- a/bgpd/bgp_zebra.c
+++ b/bgpd/bgp_zebra.c
@@ -35,6 +35,7 @@
#include "lib/bfd.h"
#include "filter.h"
#include "mpls.h"
+#include "vxlan.h"
#include "bgpd/bgpd.h"
#include "bgpd/bgp_route.h"
@@ -52,6 +53,7 @@
# include "bgpd/rfapi/rfapi_backend.h"
# include "bgpd/rfapi/vnc_export_bgp.h"
#endif
+#include "bgpd/bgp_evpn.h"
/* All information about zebra. */
struct zclient *zclient = NULL;
@@ -71,14 +73,12 @@ struct stream *bgp_label_buf = NULL;
2. use an array to avoid number of mallocs.
Number of supported next-hops are finite, use of arrays should be ok. */
struct attr attr_cp[MULTIPATH_NUM];
-struct attr_extra attr_extra_cp[MULTIPATH_NUM];
unsigned int attr_index = 0;
/* Once per address-family initialization of the attribute array */
#define BGP_INFO_ATTR_BUF_INIT()\
do {\
memset(attr_cp, 0, MULTIPATH_NUM * sizeof(struct attr));\
- memset(attr_extra_cp, 0, MULTIPATH_NUM * sizeof(struct attr_extra));\
attr_index = 0;\
} while (0)
@@ -86,7 +86,6 @@ do {\
do { \
*info_dst = *info_src; \
assert(attr_index != multipath_num);\
- attr_cp[attr_index].extra = &attr_extra_cp[attr_index]; \
bgp_attr_dup (&attr_cp[attr_index], info_src->attr); \
bgp_attr_deep_dup (&attr_cp[attr_index], info_src->attr); \
info_dst->attr = &attr_cp[attr_index]; \
@@ -1160,23 +1159,23 @@ bgp_info_to_ipv6_nexthop (struct bgp_info *info)
struct in6_addr *nexthop = NULL;
/* Only global address nexthop exists. */
- if (info->attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL)
- nexthop = &info->attr->extra->mp_nexthop_global;
+ if (info->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL)
+ nexthop = &info->attr->mp_nexthop_global;
/* If both global and link-local address present. */
- if (info->attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ if (info->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
{
/* Check if route-map is set to prefer global over link-local */
- if (info->attr->extra->mp_nexthop_prefer_global)
- nexthop = &info->attr->extra->mp_nexthop_global;
+ if (info->attr->mp_nexthop_prefer_global)
+ nexthop = &info->attr->mp_nexthop_global;
else
{
/* Workaround for Cisco's nexthop bug. */
- if (IN6_IS_ADDR_UNSPECIFIED (&info->attr->extra->mp_nexthop_global)
+ if (IN6_IS_ADDR_UNSPECIFIED (&info->attr->mp_nexthop_global)
&& info->peer->su_remote->sa.sa_family == AF_INET6)
nexthop = &info->peer->su_remote->sin6.sin6_addr;
else
- nexthop = &info->attr->extra->mp_nexthop_local;
+ nexthop = &info->attr->mp_nexthop_local;
}
}
@@ -1246,10 +1245,7 @@ bgp_zebra_announce (struct bgp_node *rn, struct prefix *p, struct bgp_info *info
flags = 0;
peer = info->peer;
- if ((info->attr->extra) && (info->attr->extra->tag != 0))
- tag = info->attr->extra->tag;
- else
- tag = 0;
+ tag = info->attr->tag;
/* When we create an aggregate route we must also install a Null0 route in
* the RIB */
@@ -1303,7 +1299,7 @@ bgp_zebra_announce (struct bgp_node *rn, struct prefix *p, struct bgp_info *info
{
/* Metric is currently based on the best-path only */
metric = info_cp->attr->med;
- tag = info_cp->attr->extra->tag;
+ tag = info_cp->attr->tag;
}
nexthop = &info_cp->attr->nexthop;
}
@@ -1418,8 +1414,6 @@ bgp_zebra_announce (struct bgp_node *rn, struct prefix *p, struct bgp_info *info
ifindex = 0;
nexthop = NULL;
- assert (info->attr->extra);
-
if (bgp->table_map[afi][safi].name)
BGP_INFO_ATTR_BUF_INIT();
@@ -1439,7 +1433,7 @@ bgp_zebra_announce (struct bgp_node *rn, struct prefix *p, struct bgp_info *info
if (mpinfo == info)
{
metric = info_cp->attr->med;
- tag = info_cp->attr->extra->tag;
+ tag = info_cp->attr->tag;
}
nexthop = bgp_info_to_ipv6_nexthop(info_cp);
}
@@ -1452,7 +1446,7 @@ bgp_zebra_announce (struct bgp_node *rn, struct prefix *p, struct bgp_info *info
continue;
if ((mpinfo == info) &&
- mpinfo->attr->extra->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ mpinfo->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
if (mpinfo->peer->nexthop.ifp)
ifindex = mpinfo->peer->nexthop.ifp->ifindex;
@@ -1672,10 +1666,10 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi)
api.metric = info->attr->med;
api.tag = 0;
- if ((info->attr->extra) && (info->attr->extra->tag != 0))
+ if (info->attr->tag != 0)
{
SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
- api.tag = info->attr->extra->tag;
+ api.tag = info->attr->tag;
}
if (bgp_debug_zebra(p))
@@ -1694,8 +1688,6 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi)
if (p->family == AF_INET6)
{
struct zapi_ipv6 api;
-
- assert (info->attr->extra);
api.vrf_id = peer->bgp->vrf_id;
api.flags = flags;
@@ -1712,10 +1704,10 @@ bgp_zebra_withdraw (struct prefix *p, struct bgp_info *info, safi_t safi)
api.metric = info->attr->med;
api.tag = 0;
- if ((info->attr->extra) && (info->attr->extra->tag != 0))
+ if (info->attr->tag != 0)
{
SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
- api.tag = info->attr->extra->tag;
+ api.tag = info->attr->tag;
}
if (bgp_debug_zebra(p))
@@ -1901,9 +1893,7 @@ bgp_redistribute_metric_set (struct bgp *bgp, struct bgp_redist *red, afi_t afi,
{
struct attr *old_attr;
struct attr new_attr;
- struct attr_extra new_extra;
- new_attr.extra = &new_extra;
bgp_attr_dup (&new_attr, ri->attr);
new_attr.med = red->redist_metric;
old_attr = ri->attr;
@@ -2033,6 +2023,11 @@ bgp_zebra_instance_register (struct bgp *bgp)
/* Register for router-id, interfaces, redistributed routes. */
zclient_send_reg_requests (zclient, bgp->vrf_id);
+
+ /* For default instance, register to learn about VNIs, if appropriate. */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
+ && bgp->advertise_all_vni)
+ bgp_zebra_advertise_all_vni (bgp, 1);
}
/* Deregister this instance with Zebra. Invoked upon the instance
@@ -2048,6 +2043,11 @@ bgp_zebra_instance_deregister (struct bgp *bgp)
if (BGP_DEBUG (zebra, ZEBRA))
zlog_debug("Deregistering VRF %u", bgp->vrf_id);
+ /* For default instance, unregister learning about VNIs, if appropriate. */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
+ && bgp->advertise_all_vni)
+ bgp_zebra_advertise_all_vni (bgp, 0);
+
/* Deregister for router-id, interfaces, redistributed routes. */
zclient_send_dereg_requests (zclient, bgp->vrf_id);
}
@@ -2080,6 +2080,29 @@ 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_all_vni (struct bgp *bgp, int advertise)
+{
+ struct stream *s;
+
+ /* 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_ALL_VNI, bgp->vrf_id);
+ stream_putc(s, advertise);
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ return zclient_send_message(zclient);
+}
+
/* BGP has established connection with Zebra. */
static void
bgp_zebra_connected (struct zclient *zclient)
@@ -2106,6 +2129,85 @@ bgp_zebra_connected (struct zclient *zclient)
*/
}
+static int
+bgp_zebra_process_local_vni (int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct stream *s;
+ vni_t vni;
+ struct bgp *bgp;
+ struct in_addr vtep_ip;
+
+ s = zclient->ibuf;
+ vni = stream_getl (s);
+ if (command == ZEBRA_VNI_ADD)
+ vtep_ip.s_addr = stream_get_ipv4 (s);
+ bgp = bgp_lookup_by_vrf_id (vrf_id);
+ if (!bgp)
+ return 0;
+
+ if (BGP_DEBUG (zebra, ZEBRA))
+ zlog_debug("Rx VNI %s VRF %u VNI %u",
+ (command == ZEBRA_VNI_ADD) ? "add" : "del", vrf_id, vni);
+
+ if (command == ZEBRA_VNI_ADD)
+ return bgp_evpn_local_vni_add (bgp, vni, vtep_ip.s_addr? vtep_ip : bgp->router_id);
+ else
+ return bgp_evpn_local_vni_del (bgp, vni);
+}
+
+static int
+bgp_zebra_process_local_macip (int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct stream *s;
+ vni_t vni;
+ struct bgp *bgp;
+ struct ethaddr mac;
+ struct ipaddr ip;
+ int ipa_len;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ u_char sticky;
+
+ memset (&ip, 0, sizeof (ip));
+ s = zclient->ibuf;
+ vni = stream_getl (s);
+ stream_get (&mac.octet, s, ETHER_ADDR_LEN);
+ ipa_len = stream_getl (s);
+ if (ipa_len != 0 &&
+ ipa_len != IPV4_MAX_BYTELEN &&
+ ipa_len != IPV6_MAX_BYTELEN)
+ {
+ zlog_err ("%u:Recv MACIP %s with invalid IP addr length %d",
+ vrf_id, (command == ZEBRA_MACIP_ADD) ? "Add" : "Del",
+ ipa_len);
+ return -1;
+ }
+
+ if (ipa_len)
+ {
+ ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4: IPADDR_V6;
+ stream_get (&ip.ip.addr, s, ipa_len);
+ }
+ sticky = 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)),
+ ipaddr2str (&ip, buf1, sizeof(buf1)), vni);
+
+ if (command == ZEBRA_MACIP_ADD)
+ return bgp_evpn_local_macip_add (bgp, vni, &mac, &ip, sticky);
+ else
+ return bgp_evpn_local_macip_del (bgp, vni, &mac, &ip);
+}
void
bgp_zebra_init (struct thread_master *master)
@@ -2133,6 +2235,10 @@ bgp_zebra_init (struct thread_master *master)
zclient->nexthop_update = bgp_read_nexthop_update;
zclient->import_check_update = bgp_read_import_check_update;
zclient->fec_update = bgp_read_fec_update;
+ zclient->local_vni_add = bgp_zebra_process_local_vni;
+ zclient->local_vni_del = bgp_zebra_process_local_vni;
+ zclient->local_macip_add = bgp_zebra_process_local_macip;
+ zclient->local_macip_del = bgp_zebra_process_local_macip;
bgp_nexthop_buf = stream_new(multipath_num * sizeof (struct in6_addr));
bgp_ifindices_buf = stream_new(multipath_num * sizeof (unsigned int));
diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h
index 3d634ed69..7ad1f706d 100644
--- a/bgpd/bgp_zebra.h
+++ b/bgpd/bgp_zebra.h
@@ -54,6 +54,8 @@ extern struct interface *if_lookup_by_ipv4_exact (struct in_addr *, vrf_id_t);
extern struct interface *if_lookup_by_ipv6 (struct in6_addr *, ifindex_t, vrf_id_t);
extern struct interface *if_lookup_by_ipv6_exact (struct in6_addr *, ifindex_t, vrf_id_t);
+extern int bgp_zebra_advertise_all_vni (struct bgp *, int);
+
extern int bgp_zebra_num_connects(void);
#endif /* _QUAGGA_BGP_ZEBRA_H */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index a0a86136f..1b0d78ff9 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -227,6 +227,10 @@ bgp_router_id_set (struct bgp *bgp, const struct in_addr *id)
if (IPV4_ADDR_SAME (&bgp->router_id, id))
return 0;
+ /* EVPN uses router id in RD, withdraw them */
+ if (bgp->advertise_all_vni)
+ bgp_evpn_handle_router_id_update (bgp, TRUE);
+
IPV4_ADDR_COPY (&bgp->router_id, id);
/* Set all peer's local identifier with this value. */
@@ -241,6 +245,11 @@ bgp_router_id_set (struct bgp *bgp, const struct in_addr *id)
BGP_NOTIFY_CEASE_CONFIG_CHANGE);
}
}
+
+ /* EVPN uses router id in RD, update them */
+ if (bgp->advertise_all_vni)
+ bgp_evpn_handle_router_id_update (bgp, FALSE);
+
return 0;
}
@@ -3004,6 +3013,7 @@ bgp_create (as_t *as, const char *name, enum bgp_instance_type inst_type)
QOBJ_REG (bgp, bgp);
update_bgp_group_init(bgp);
+ bgp_evpn_init(bgp);
return bgp;
}
@@ -3355,6 +3365,8 @@ bgp_free (struct bgp *bgp)
bgp_scan_finish (bgp);
bgp_address_destroy (bgp);
+ bgp_evpn_cleanup (bgp);
+
if (bgp->name)
XFREE(MTYPE_BGP, bgp->name);
@@ -7367,6 +7379,9 @@ bgp_config_write_family (struct vty *vty, struct bgp *bgp, afi_t afi,
bgp_config_write_maxpaths (vty, bgp, afi, safi, &write);
bgp_config_write_table_map (vty, bgp, afi, safi, &write);
+ if (safi == SAFI_EVPN)
+ bgp_config_write_evpn_info (vty, bgp, afi, safi, &write);
+
if (write)
vty_out (vty, " exit-address-family\n");
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index 5dc25d00a..31aa6163b 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -33,6 +33,7 @@
#include "linklist.h"
#include "defaults.h"
#include "bgp_memory.h"
+#include "bitfield.h"
#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
@@ -367,6 +368,20 @@ struct bgp
struct rfapi *rfapi;
#endif
+ /* EVPN related information */
+
+ /* EVI hash table */
+ struct hash *vnihash;
+
+ /* EVPN enable - advertise local VNIs and their MACs etc. */
+ int advertise_all_vni;
+
+ /* Hash table of Import RTs to EVIs */
+ struct hash *import_rt_hash;
+
+ /* Id space for automatic RD derivation for an EVI */
+ bitfield_t rd_idspace;
+
QOBJ_FIELDS
};
DECLARE_QOBJ_TYPE(bgp)
diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c
index 89f69637f..a959b9827 100644
--- a/bgpd/rfapi/rfapi.c
+++ b/bgpd/rfapi/rfapi.c
@@ -715,7 +715,6 @@ add_vnc_route (
/* Cripes, the memory management of attributes is byzantine */
bgp_attr_default_set (&attr, BGP_ORIGIN_INCOMPLETE);
- assert (attr.extra);
/*
* At this point:
@@ -772,7 +771,6 @@ add_vnc_route (
/* Encap SAFI not used with MPLS */
vnc_zlog_debug_verbose ("%s: mpls tunnel type, encap safi omitted", __func__);
aspath_unintern (&attr.aspath); /* Unintern original. */
- bgp_attr_extra_free (&attr);
return;
}
}
@@ -790,7 +788,7 @@ add_vnc_route (
}
/* override default weight assigned by bgp_attr_default_set() */
- attr.extra->weight = rfd->peer ? rfd->peer->weight[afi][safi] : 0;
+ attr.weight = rfd->peer ? rfd->peer->weight[afi][safi] : 0;
/*
* NB: ticket 81: do not reset attr.aspath here because it would
@@ -808,7 +806,7 @@ add_vnc_route (
if (type == ZEBRA_ROUTE_BGP_DIRECT || type == ZEBRA_ROUTE_BGP_DIRECT_EXT)
{
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
- attr.extra->originator_id = bgp->router_id;
+ attr.originator_id = bgp->router_id;
}
@@ -825,7 +823,7 @@ add_vnc_route (
encaptlv->length = 4;
lt = htonl (*lifetime);
memcpy (encaptlv->value, &lt, 4);
- attr.extra->vnc_subtlvs = encaptlv;
+ attr.vnc_subtlvs = encaptlv;
vnc_zlog_debug_verbose ("%s: set Encap Attr Prefix Lifetime to %d",
__func__, *lifetime);
}
@@ -845,13 +843,13 @@ add_vnc_route (
*/
encaptlv =
encap_tlv_dup ((struct bgp_attr_encap_subtlv *) rfp_options);
- if (attr.extra->vnc_subtlvs)
+ if (attr.vnc_subtlvs)
{
- attr.extra->vnc_subtlvs->next = encaptlv;
+ attr.vnc_subtlvs->next = encaptlv;
}
else
{
- attr.extra->vnc_subtlvs = encaptlv;
+ attr.vnc_subtlvs = encaptlv;
}
}
@@ -859,7 +857,7 @@ add_vnc_route (
{
struct bgp_tea_options *hop;
/* XXX max of one tlv present so far from above code */
- struct bgp_attr_encap_subtlv *tail = attr.extra->vnc_subtlvs;
+ struct bgp_attr_encap_subtlv *tail = attr.vnc_subtlvs;
for (hop = rfp_options; hop; hop = hop->next)
{
@@ -887,7 +885,7 @@ add_vnc_route (
}
else
{
- attr.extra->vnc_subtlvs = encaptlv;
+ attr.vnc_subtlvs = encaptlv;
}
tail = encaptlv;
}
@@ -903,8 +901,8 @@ add_vnc_route (
*/
- attr.extra->ecommunity = ecommunity_new ();
- assert (attr.extra->ecommunity);
+ attr.ecommunity = ecommunity_new ();
+ assert (attr.ecommunity);
if (TunnelType != BGP_ENCAP_TYPE_MPLS &&
TunnelType != BGP_ENCAP_TYPE_RESERVED)
@@ -921,7 +919,7 @@ add_vnc_route (
beec.val[1] = ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP;
beec.val[6] = ((TunnelType) >> 8) & 0xff;
beec.val[7] = (TunnelType) & 0xff;
- ecommunity_add_val (attr.extra->ecommunity, &beec);
+ ecommunity_add_val (attr.ecommunity, &beec);
}
/*
@@ -929,21 +927,21 @@ add_vnc_route (
*/
if (rt_export_list)
{
- attr.extra->ecommunity =
- ecommunity_merge (attr.extra->ecommunity, rt_export_list);
+ attr.ecommunity =
+ ecommunity_merge (attr.ecommunity, rt_export_list);
}
- if (attr.extra->ecommunity->size)
+ if (attr.ecommunity->size)
{
attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
}
else
{
- ecommunity_free (&attr.extra->ecommunity);
- attr.extra->ecommunity = NULL;
+ ecommunity_free (&attr.ecommunity);
+ attr.ecommunity = NULL;
}
- vnc_zlog_debug_verbose ("%s: attr.extra->ecommunity=%p", __func__,
- attr.extra->ecommunity);
+ vnc_zlog_debug_verbose ("%s: attr.ecommunity=%p", __func__,
+ attr.ecommunity);
/*
@@ -965,13 +963,13 @@ add_vnc_route (
*/
attr.nexthop.s_addr = nexthop->addr.v4.s_addr;
- attr.extra->mp_nexthop_global_in = nexthop->addr.v4;
- attr.extra->mp_nexthop_len = 4;
+ attr.mp_nexthop_global_in = nexthop->addr.v4;
+ attr.mp_nexthop_len = 4;
break;
case AF_INET6:
- attr.extra->mp_nexthop_global = nexthop->addr.v6;
- attr.extra->mp_nexthop_len = 16;
+ attr.mp_nexthop_global = nexthop->addr.v6;
+ attr.mp_nexthop_len = 16;
break;
default:
@@ -1016,7 +1014,6 @@ add_vnc_route (
new_attr = bgp_attr_intern (&attr);
aspath_unintern (&attr.aspath); /* Unintern original. */
- bgp_attr_extra_free (&attr);
/*
* At this point:
diff --git a/bgpd/rfapi/rfapi_encap_tlv.c b/bgpd/rfapi/rfapi_encap_tlv.c
index 04f8b249f..24bfb41bf 100644
--- a/bgpd/rfapi/rfapi_encap_tlv.c
+++ b/bgpd/rfapi/rfapi_encap_tlv.c
@@ -162,28 +162,24 @@ rfapi_tunneltype_option_to_tlv (
struct rfapi_un_option *
rfapi_encap_tlv_to_un_option (struct attr *attr)
{
- struct attr_extra *attre = attr->extra;
struct rfapi_un_option *uo = NULL;
struct rfapi_tunneltype_option *tto;
int rc;
struct bgp_attr_encap_subtlv *stlv;
- if (!attre)
- return NULL;
-
/* no tunnel encap attr stored */
- if (!attre->encap_tunneltype)
+ if (!attr->encap_tunneltype)
return NULL;
- stlv = attre->encap_subtlvs;
+ stlv = attr->encap_subtlvs;
uo = XCALLOC (MTYPE_RFAPI_UN_OPTION, sizeof (struct rfapi_un_option));
assert (uo);
uo->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE;
- uo->v.tunnel.type = attre->encap_tunneltype;
+ uo->v.tunnel.type = attr->encap_tunneltype;
tto = &uo->v.tunnel;
- switch (attre->encap_tunneltype)
+ switch (attr->encap_tunneltype)
{
case BGP_ENCAP_TYPE_L2TPV3_OVER_IP:
rc = tlv_to_bgp_encap_type_l2tpv3overip (stlv, &tto->bgpinfo.l2tpv3_ip);
@@ -249,7 +245,7 @@ rfapi_encap_tlv_to_un_option (struct attr *attr)
default:
vnc_zlog_debug_verbose ("%s: unknown tunnel type %d",
- __func__, attre->encap_tunneltype);
+ __func__, attr->encap_tunneltype);
rc = -1;
break;
}
diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c
index b0c6db2a1..7b0ca0cbb 100644
--- a/bgpd/rfapi/rfapi_import.c
+++ b/bgpd/rfapi/rfapi_import.c
@@ -1,4 +1,4 @@
-/*
+ /*
*
* Copyright 2009-2016, LabN Consulting, L.L.C.
*
@@ -308,12 +308,12 @@ rfapi_deferred_close_workfunc (struct work_queue *q, void *data)
int
rfapiGetL2o (struct attr *attr, struct rfapi_l2address_option *l2o)
{
- if (attr && attr->extra)
+ if (attr)
{
struct bgp_attr_encap_subtlv *pEncap;
- for (pEncap = attr->extra->vnc_subtlvs; pEncap; pEncap = pEncap->next)
+ for (pEncap = attr->vnc_subtlvs; pEncap; pEncap = pEncap->next)
{
if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION)
@@ -358,10 +358,10 @@ rfapiGetVncLifetime (struct attr *attr, uint32_t * lifetime)
*lifetime = RFAPI_INFINITE_LIFETIME; /* default to infinite */
- if (attr && attr->extra)
+ if (attr)
{
- for (pEncap = attr->extra->vnc_subtlvs; pEncap; pEncap = pEncap->next)
+ for (pEncap = attr->vnc_subtlvs; pEncap; pEncap = pEncap->next)
{
if (pEncap->type == BGP_VNC_SUBTLV_TYPE_LIFETIME)
@@ -387,9 +387,9 @@ rfapiGetTunnelType (struct attr *attr,
bgp_encap_types *type)
{
*type = BGP_ENCAP_TYPE_MPLS; /* default to MPLS */
- if (attr && attr->extra && attr->extra->ecommunity)
+ if (attr && attr->ecommunity)
{
- struct ecommunity *ecom = attr->extra->ecommunity;
+ struct ecommunity *ecom = attr->ecommunity;
int i;
for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE)
@@ -431,9 +431,9 @@ rfapiGetVncTunnelUnAddr (struct attr *attr, struct prefix *p)
return ENOENT;
}
- if (attr && attr->extra)
+ if (attr)
{
- for (pEncap = attr->extra->encap_subtlvs; pEncap; pEncap = pEncap->next)
+ for (pEncap = attr->encap_subtlvs; pEncap; pEncap = pEncap->next)
{
if (pEncap->type == BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT)
@@ -1184,7 +1184,7 @@ rfapiVpnBiNhEqualsPt (struct bgp_info *bi, struct rfapi_ip_addr *hpt)
if (!hpt || !bi)
return 0;
- family = BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len);
+ family = BGP_MP_NEXTHOP_FAMILY (bi->attr->mp_nexthop_len);
if (hpt->addr_family != family)
return 0;
@@ -1192,12 +1192,12 @@ rfapiVpnBiNhEqualsPt (struct bgp_info *bi, struct rfapi_ip_addr *hpt)
switch (family)
{
case AF_INET:
- if (bi->attr->extra->mp_nexthop_global_in.s_addr != hpt->addr.v4.s_addr)
+ if (bi->attr->mp_nexthop_global_in.s_addr != hpt->addr.v4.s_addr)
return 0;
break;
case AF_INET6:
- if (IPV6_ADDR_CMP (&bi->attr->extra->mp_nexthop_global, &hpt->addr.v6))
+ if (IPV6_ADDR_CMP (&bi->attr->mp_nexthop_global, &hpt->addr.v6))
return 0;
break;
@@ -1225,31 +1225,27 @@ rfapiVpnBiSamePtUn (struct bgp_info *bi1, struct bgp_info *bi2)
if (!bi1->attr || !bi2->attr)
return 0;
- if (!bi1->attr->extra || !bi2->attr->extra)
- return 0;
-
/*
* VN address comparisons
*/
- if (BGP_MP_NEXTHOP_FAMILY (bi1->attr->extra->mp_nexthop_len) !=
- BGP_MP_NEXTHOP_FAMILY (bi2->attr->extra->mp_nexthop_len))
+ if (BGP_MP_NEXTHOP_FAMILY (bi1->attr->mp_nexthop_len) !=
+ BGP_MP_NEXTHOP_FAMILY (bi2->attr->mp_nexthop_len))
{
return 0;
}
- switch (BGP_MP_NEXTHOP_FAMILY (bi1->attr->extra->mp_nexthop_len))
+ switch (BGP_MP_NEXTHOP_FAMILY (bi1->attr->mp_nexthop_len))
{
-
case AF_INET:
- if (bi1->attr->extra->mp_nexthop_global_in.s_addr !=
- bi2->attr->extra->mp_nexthop_global_in.s_addr)
+ if (bi1->attr->mp_nexthop_global_in.s_addr !=
+ bi2->attr->mp_nexthop_global_in.s_addr)
return 0;
break;
case AF_INET6:
- if (IPV6_ADDR_CMP (&bi1->attr->extra->mp_nexthop_global,
- &bi2->attr->extra->mp_nexthop_global))
+ if (IPV6_ADDR_CMP (&bi1->attr->mp_nexthop_global,
+ &bi2->attr->mp_nexthop_global))
return 0;
break;
@@ -1419,11 +1415,11 @@ rfapiRouteInfo2NextHopEntry (
memcpy (&vo->v.l2addr.macaddr, &rn->p.u.prefix_eth.octet,
ETHER_ADDR_LEN);
/* only low 3 bytes of this are significant */
- if (bi->attr && bi->attr->extra)
+ if (bi->attr)
{
- (void) rfapiEcommunityGetLNI (bi->attr->extra->ecommunity,
+ (void) rfapiEcommunityGetLNI (bi->attr->ecommunity,
&vo->v.l2addr.logical_net_id);
- (void) rfapiEcommunityGetEthernetTag (bi->attr->extra->ecommunity,
+ (void) rfapiEcommunityGetEthernetTag (bi->attr->ecommunity,
&vo->v.l2addr.tag_id);
}
@@ -1451,132 +1447,129 @@ rfapiRouteInfo2NextHopEntry (
bgp_encap_types tun_type;
new->prefix.cost = rfapiRfpCost (bi->attr);
- if (bi->attr->extra)
+ struct bgp_attr_encap_subtlv *pEncap;
+
+ switch (BGP_MP_NEXTHOP_FAMILY (bi->attr->mp_nexthop_len))
{
+ case AF_INET:
+ new->vn_address.addr_family = AF_INET;
+ new->vn_address.addr.v4 = bi->attr->mp_nexthop_global_in;
+ break;
- struct bgp_attr_encap_subtlv *pEncap;
+ case AF_INET6:
+ new->vn_address.addr_family = AF_INET6;
+ new->vn_address.addr.v6 = bi->attr->mp_nexthop_global;
+ break;
- switch (BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len))
- {
- case AF_INET:
- new->vn_address.addr_family = AF_INET;
- new->vn_address.addr.v4 = bi->attr->extra->mp_nexthop_global_in;
- break;
+ default:
+ zlog_warn ("%s: invalid vpn nexthop length: %d",
+ __func__, bi->attr->mp_nexthop_len);
+ rfapi_free_next_hop_list (new);
+ return NULL;
+ }
- case AF_INET6:
- new->vn_address.addr_family = AF_INET6;
- new->vn_address.addr.v6 = bi->attr->extra->mp_nexthop_global;
+ for (pEncap = bi->attr->vnc_subtlvs; pEncap;
+ pEncap = pEncap->next)
+ {
+ switch (pEncap->type)
+ {
+ case BGP_VNC_SUBTLV_TYPE_LIFETIME:
+ /* use configured lifetime, not attr lifetime */
break;
default:
- zlog_warn ("%s: invalid vpn nexthop length: %d",
- __func__, bi->attr->extra->mp_nexthop_len);
- rfapi_free_next_hop_list (new);
- return NULL;
- }
+ zlog_warn ("%s: unknown VNC option type %d",
+ __func__, pEncap->type);
- for (pEncap = bi->attr->extra->vnc_subtlvs; pEncap;
- pEncap = pEncap->next)
- {
- switch (pEncap->type)
- {
- case BGP_VNC_SUBTLV_TYPE_LIFETIME:
- /* use configured lifetime, not attr lifetime */
- break;
- default:
- zlog_warn ("%s: unknown VNC option type %d",
- __func__, pEncap->type);
-
-
- break;
- }
+ break;
}
+ }
- rfapiGetTunnelType (bi->attr, &tun_type);
- if (tun_type == BGP_ENCAP_TYPE_MPLS)
+ rfapiGetTunnelType (bi->attr, &tun_type);
+ if (tun_type == BGP_ENCAP_TYPE_MPLS)
+ {
+ struct prefix p;
+ /* MPLS carries UN address in next hop */
+ rfapiNexthop2Prefix (bi->attr, &p);
+ if (p.family != 0)
{
- struct prefix p;
- /* MPLS carries UN address in next hop */
- rfapiNexthop2Prefix (bi->attr, &p);
- if (p.family != 0)
- {
- rfapiQprefix2Raddr(&p, &new->un_address);
- have_vnc_tunnel_un = 1;
- }
+ rfapiQprefix2Raddr(&p, &new->un_address);
+ have_vnc_tunnel_un = 1;
}
+ }
- for (pEncap = bi->attr->extra->encap_subtlvs; pEncap;
- pEncap = pEncap->next)
+ for (pEncap = bi->attr->encap_subtlvs; pEncap;
+ pEncap = pEncap->next)
+ {
+ switch (pEncap->type)
{
- switch (pEncap->type)
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ /*
+ * Overrides ENCAP UN address, if any
+ */
+ switch (pEncap->length)
{
- case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
- /*
- * Overrides ENCAP UN address, if any
- */
- switch (pEncap->length)
- {
- case 8:
- new->un_address.addr_family = AF_INET;
- memcpy (&new->un_address.addr.v4, pEncap->value, 4);
- have_vnc_tunnel_un = 1;
- break;
-
- case 20:
- new->un_address.addr_family = AF_INET6;
- memcpy (&new->un_address.addr.v6, pEncap->value, 16);
- have_vnc_tunnel_un = 1;
- break;
-
- default:
- zlog_warn
- ("%s: invalid tunnel subtlv UN addr length (%d) for bi %p",
- __func__, pEncap->length, bi);
- }
+ case 8:
+ new->un_address.addr_family = AF_INET;
+ memcpy (&new->un_address.addr.v4, pEncap->value, 4);
+ have_vnc_tunnel_un = 1;
+ break;
+
+ case 20:
+ new->un_address.addr_family = AF_INET6;
+ memcpy (&new->un_address.addr.v6, pEncap->value, 16);
+ have_vnc_tunnel_un = 1;
break;
default:
- zlog_warn ("%s: unknown Encap Attribute option type %d",
- __func__, pEncap->type);
+ zlog_warn
+ ("%s: invalid tunnel subtlv UN addr length (%d) for bi %p",
+ __func__, pEncap->length, bi);
+ }
+ break;
+ default:
+ zlog_warn ("%s: unknown Encap Attribute option type %d",
+ __func__, pEncap->type);
- break;
- }
+
+ break;
}
+ }
- new->un_options = rfapi_encap_tlv_to_un_option (bi->attr);
+ new->un_options = rfapi_encap_tlv_to_un_option (bi->attr);
#if DEBUG_ENCAP_MONITOR
- vnc_zlog_debug_verbose ("%s: line %d: have_vnc_tunnel_un=%d",
- __func__, __LINE__, have_vnc_tunnel_un);
+ vnc_zlog_debug_verbose ("%s: line %d: have_vnc_tunnel_un=%d",
+ __func__, __LINE__, have_vnc_tunnel_un);
#endif
- if (!have_vnc_tunnel_un && bi && bi->extra)
+ if (!have_vnc_tunnel_un && bi && bi->extra)
+ {
+ /*
+ * use cached UN address from ENCAP route
+ */
+ new->un_address.addr_family = bi->extra->vnc.import.un_family;
+ switch (new->un_address.addr_family)
{
- /*
- * use cached UN address from ENCAP route
- */
- new->un_address.addr_family = bi->extra->vnc.import.un_family;
- switch (new->un_address.addr_family)
- {
- case AF_INET:
- new->un_address.addr.v4 = bi->extra->vnc.import.un.addr4;
- break;
- case AF_INET6:
- new->un_address.addr.v6 = bi->extra->vnc.import.un.addr6;
- break;
- default:
- zlog_warn ("%s: invalid UN addr family (%d) for bi %p",
- __func__, new->un_address.addr_family, bi);
- rfapi_free_next_hop_list (new);
- return NULL;
- break;
- }
+ case AF_INET:
+ new->un_address.addr.v4 = bi->extra->vnc.import.un.addr4;
+ break;
+ case AF_INET6:
+ new->un_address.addr.v6 = bi->extra->vnc.import.un.addr6;
+ break;
+ default:
+ zlog_warn ("%s: invalid UN addr family (%d) for bi %p",
+ __func__, new->un_address.addr_family, bi);
+ rfapi_free_next_hop_list (new);
+ return NULL;
+ break;
}
}
}
+
new->lifetime = lifetime;
return new;
}
@@ -2121,6 +2114,8 @@ rfapiBgpInfoAttachSorted (
struct bgp *bgp;
struct bgp_info *prev;
struct bgp_info *next;
+ char pfx_buf[PREFIX2STR_BUFFER];
+
bgp = bgp_get_default (); /* assume 1 instance for now */
@@ -2136,7 +2131,7 @@ rfapiBgpInfoAttachSorted (
if (!bgp ||
(!CHECK_FLAG (info_new->flags, BGP_INFO_REMOVED) &&
CHECK_FLAG (next->flags, BGP_INFO_REMOVED)) ||
- bgp_info_cmp_compatible (bgp, info_new, next, afi, safi) == -1)
+ bgp_info_cmp_compatible (bgp, info_new, next, pfx_buf, afi, safi) == -1)
{ /* -1 if 1st is better */
break;
}
@@ -2700,19 +2695,18 @@ rfapiNexthop2Prefix (struct attr *attr, struct prefix *p)
{
assert (p);
assert (attr);
- assert (attr->extra);
memset (p, 0, sizeof (struct prefix));
- switch (p->family = BGP_MP_NEXTHOP_FAMILY (attr->extra->mp_nexthop_len))
+ switch (p->family = BGP_MP_NEXTHOP_FAMILY (attr->mp_nexthop_len))
{
case AF_INET:
- p->u.prefix4 = attr->extra->mp_nexthop_global_in;
+ p->u.prefix4 = attr->mp_nexthop_global_in;
p->prefixlen = 32;
break;
case AF_INET6:
- p->u.prefix6 = attr->extra->mp_nexthop_global;
+ p->u.prefix6 = attr->mp_nexthop_global;
p->prefixlen = 128;
break;
@@ -2777,9 +2771,7 @@ rfapiAttrNexthopAddrDifferent (struct prefix *p1, struct prefix *p2)
static void
rfapiCopyUnEncap2VPN (struct bgp_info *encap_bi, struct bgp_info *vpn_bi)
{
- struct attr_extra *attre;
-
- if (!encap_bi->attr || !encap_bi->attr->extra)
+ if (!encap_bi->attr)
{
zlog_warn ("%s: no encap bi attr/extra, can't copy UN address",
__func__);
@@ -2793,9 +2785,7 @@ rfapiCopyUnEncap2VPN (struct bgp_info *encap_bi, struct bgp_info *vpn_bi)
return;
}
- attre = encap_bi->attr->extra;
-
- switch (BGP_MP_NEXTHOP_FAMILY (attre->mp_nexthop_len))
+ switch (BGP_MP_NEXTHOP_FAMILY (encap_bi->attr->mp_nexthop_len))
{
case AF_INET:
@@ -2809,17 +2799,17 @@ rfapiCopyUnEncap2VPN (struct bgp_info *encap_bi, struct bgp_info *vpn_bi)
}
vpn_bi->extra->vnc.import.un_family = AF_INET;
- vpn_bi->extra->vnc.import.un.addr4 = attre->mp_nexthop_global_in;
+ vpn_bi->extra->vnc.import.un.addr4 = encap_bi->attr->mp_nexthop_global_in;
break;
case AF_INET6:
vpn_bi->extra->vnc.import.un_family = AF_INET6;
- vpn_bi->extra->vnc.import.un.addr6 = attre->mp_nexthop_global;
+ vpn_bi->extra->vnc.import.un.addr6 = encap_bi->attr->mp_nexthop_global;
break;
default:
zlog_warn ("%s: invalid encap nexthop length: %d",
- __func__, attre->mp_nexthop_len);
+ __func__, encap_bi->attr->mp_nexthop_len);
vpn_bi->extra->vnc.import.un_family = 0;
break;
}
@@ -3100,21 +3090,21 @@ rfapiExpireEncapNow (
static int
rfapiGetNexthop (struct attr *attr, struct prefix *prefix)
{
- switch (BGP_MP_NEXTHOP_FAMILY (attr->extra->mp_nexthop_len))
+ switch (BGP_MP_NEXTHOP_FAMILY (attr->mp_nexthop_len))
{
case AF_INET:
prefix->family = AF_INET;
prefix->prefixlen = 32;
- prefix->u.prefix4 = attr->extra->mp_nexthop_global_in;
+ prefix->u.prefix4 = attr->mp_nexthop_global_in;
break;
case AF_INET6:
prefix->family = AF_INET6;
prefix->prefixlen = 128;
- prefix->u.prefix6 = attr->extra->mp_nexthop_global;
+ prefix->u.prefix6 = attr->mp_nexthop_global;
break;
default:
- vnc_zlog_debug_verbose ("%s: unknown attr->extra->mp_nexthop_len %d", __func__,
- attr->extra->mp_nexthop_len);
+ vnc_zlog_debug_verbose ("%s: unknown attr->mp_nexthop_len %d", __func__,
+ attr->mp_nexthop_len);
return EINVAL;
}
return 0;
@@ -3185,7 +3175,7 @@ rfapiBgpInfoFilteredImportEncap (
* On a withdraw, peer and RD are sufficient to determine if
* we should act.
*/
- if (!attr || !attr->extra || !attr->extra->ecommunity)
+ if (!attr || !attr->ecommunity)
{
vnc_zlog_debug_verbose ("%s: attr, extra, or ecommunity missing, not importing",
@@ -3193,7 +3183,7 @@ rfapiBgpInfoFilteredImportEncap (
return;
}
#if RFAPI_REQUIRE_ENCAP_BEEC
- if (!rfapiEcommunitiesMatchBeec (attr->extra->ecommunity))
+ if (!rfapiEcommunitiesMatchBeec (attr->ecommunity))
{
vnc_zlog_debug_verbose ("%s: it=%p: no match for BGP Encapsulation ecommunity",
__func__, import_table);
@@ -3201,7 +3191,7 @@ rfapiBgpInfoFilteredImportEncap (
}
#endif
if (!rfapiEcommunitiesIntersect (import_table->rt_import_list,
- attr->extra->ecommunity))
+ attr->ecommunity))
{
vnc_zlog_debug_verbose ("%s: it=%p: no ecommunity intersection",
@@ -3667,7 +3657,7 @@ rfapiBgpInfoFilteredImportVPN (
*/
if (action == FIF_ACTION_UPDATE)
{
- if (!attr || !attr->extra || !attr->extra->ecommunity)
+ if (!attr || !attr->ecommunity)
{
vnc_zlog_debug_verbose ("%s: attr, extra, or ecommunity missing, not importing",
@@ -3676,7 +3666,7 @@ rfapiBgpInfoFilteredImportVPN (
}
if ((import_table != bgp->rfapi->it_ce) &&
!rfapiEcommunitiesIntersect (import_table->rt_import_list,
- attr->extra->ecommunity))
+ attr->ecommunity))
{
vnc_zlog_debug_verbose ("%s: it=%p: no ecommunity intersection",
@@ -4160,12 +4150,12 @@ rfapiProcessUpdate (
* Find rt containing LNI (Logical Network ID), which
* _should_ always be present when mac address is present
*/
- rc = rfapiEcommunityGetLNI (attr->extra->ecommunity, &lni);
+ rc = rfapiEcommunityGetLNI (attr->ecommunity, &lni);
vnc_zlog_debug_verbose
- ("%s: rfapiEcommunityGetLNI returned %d, lni=%d, attr=%p, attr->extra=%p",
- __func__, rc, lni, attr, attr->extra);
- if (attr && attr->extra && !rc)
+ ("%s: rfapiEcommunityGetLNI returned %d, lni=%d, attr=%p",
+ __func__, rc, lni, attr);
+ if (attr && !rc)
{
it = rfapiMacImportTableGet (bgp, lni);
diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c
index 380b04757..63dbb04f4 100644
--- a/bgpd/rfapi/rfapi_rib.c
+++ b/bgpd/rfapi/rfapi_rib.c
@@ -664,7 +664,7 @@ rfapiRibBi2Ri(
ri->lifetime = lifetime;
/* This loop based on rfapiRouteInfo2NextHopEntry() */
- for (pEncap = bi->attr->extra->vnc_subtlvs; pEncap; pEncap = pEncap->next)
+ for (pEncap = bi->attr->vnc_subtlvs; pEncap; pEncap = pEncap->next)
{
struct bgp_tea_options *hop;
@@ -723,11 +723,11 @@ rfapiRibBi2Ri(
memcpy (&vo->v.l2addr.macaddr, bi->extra->vnc.import.rd.val+2,
ETHER_ADDR_LEN);
- if (bi->attr && bi->attr->extra)
+ if (bi->attr)
{
- (void) rfapiEcommunityGetLNI (bi->attr->extra->ecommunity,
+ (void) rfapiEcommunityGetLNI (bi->attr->ecommunity,
&vo->v.l2addr.logical_net_id);
- (void) rfapiEcommunityGetEthernetTag (bi->attr->extra->ecommunity,
+ (void) rfapiEcommunityGetEthernetTag (bi->attr->ecommunity,
&vo->v.l2addr.tag_id);
}
diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c
index 3b7d08f91..103f87ed0 100644
--- a/bgpd/rfapi/rfapi_vty.c
+++ b/bgpd/rfapi/rfapi_vty.c
@@ -469,9 +469,9 @@ rfapi_vty_out_vncinfo (
}
}
- if (bi->attr && bi->attr->extra && bi->attr->extra->ecommunity)
+ if (bi->attr && bi->attr->ecommunity)
{
- s = ecommunity_ecom2str (bi->attr->extra->ecommunity,
+ s = ecommunity_ecom2str (bi->attr->ecommunity,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
vty_out (vty, " EC{%s}", s);
XFREE (MTYPE_ECOMMUNITY_STR, s);
@@ -499,7 +499,6 @@ rfapiPrintAttrPtrs (void *stream, struct attr *attr)
void *out;
const char *vty_newline;
- struct attr_extra *ae;
char buf[BUFSIZ];
if (rfapiStream2Vty (stream, &fp, &vty, &out, &vty_newline) == 0)
@@ -518,15 +517,12 @@ rfapiPrintAttrPtrs (void *stream, struct attr *attr)
fp (out, " community=%p, refcnt=%d%s", attr->community,
(attr->community ? attr->community->refcnt : 0), HVTYNL);
- if ((ae = attr->extra))
- {
- fp (out, " ecommunity=%p, refcnt=%d%s", ae->ecommunity,
- (ae->ecommunity ? ae->ecommunity->refcnt : 0), HVTYNL);
- fp (out, " cluster=%p, refcnt=%d%s", ae->cluster,
- (ae->cluster ? ae->cluster->refcnt : 0), HVTYNL);
- fp (out, " transit=%p, refcnt=%d%s", ae->transit,
- (ae->transit ? ae->transit->refcnt : 0), HVTYNL);
- }
+ fp (out, " ecommunity=%p, refcnt=%d%s", attr->ecommunity,
+ (attr->ecommunity ? attr->ecommunity->refcnt : 0), HVTYNL);
+ fp (out, " cluster=%p, refcnt=%d%s", attr->cluster,
+ (attr->cluster ? attr->cluster->refcnt : 0), HVTYNL);
+ fp (out, " transit=%p, refcnt=%d%s", attr->transit,
+ (attr->transit ? attr->transit->refcnt : 0), HVTYNL);
}
/*
@@ -593,26 +589,26 @@ rfapiPrintBi (void *stream, struct bgp_info *bi)
* RFP option sizes (they are opaque values)
* extended communities (RTs)
*/
- if (bi->attr && bi->attr->extra)
+ if (bi->attr)
{
uint32_t lifetime;
int printed_1st_gol = 0;
struct bgp_attr_encap_subtlv *pEncap;
struct prefix pfx_un;
- int af = BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len);
+ int af = BGP_MP_NEXTHOP_FAMILY (bi->attr->mp_nexthop_len);
/* Nexthop */
if (af == AF_INET)
{
r = snprintf (p, REMAIN, "%s", inet_ntop (AF_INET,
- &bi->attr->extra->mp_nexthop_global_in,
+ &bi->attr->mp_nexthop_global_in,
buf, BUFSIZ));
INCP;
}
else if (af == AF_INET6)
{
r = snprintf (p, REMAIN, "%s", inet_ntop (AF_INET6,
- &bi->attr->extra->mp_nexthop_global,
+ &bi->attr->mp_nexthop_global,
buf, BUFSIZ));
INCP;
}
@@ -650,7 +646,7 @@ rfapiPrintBi (void *stream, struct bgp_info *bi)
}
/* RFP option lengths */
- for (pEncap = bi->attr->extra->vnc_subtlvs; pEncap;
+ for (pEncap = bi->attr->vnc_subtlvs; pEncap;
pEncap = pEncap->next)
{
@@ -673,9 +669,9 @@ rfapiPrintBi (void *stream, struct bgp_info *bi)
}
/* RT list */
- if (bi->attr->extra->ecommunity)
+ if (bi->attr->ecommunity)
{
- s = ecommunity_ecom2str (bi->attr->extra->ecommunity,
+ s = ecommunity_ecom2str (bi->attr->ecommunity,
ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
r = snprintf (p, REMAIN, " %s", s);
INCP;
@@ -704,9 +700,9 @@ rfapiPrintBi (void *stream, struct bgp_info *bi)
if (bi->attr)
{
- if (bi->attr->extra)
+ if (bi->attr->weight)
{
- r = snprintf (p, REMAIN, " W=%d", bi->attr->extra->weight);
+ r = snprintf (p, REMAIN, " W=%d", bi->attr->weight);
INCP;
}
diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c
index bca95e47c..342dc6a19 100644
--- a/bgpd/rfapi/vnc_export_bgp.c
+++ b/bgpd/rfapi/vnc_export_bgp.c
@@ -74,7 +74,6 @@ encap_attr_export_ce (
*/
memset (new, 0, sizeof (struct attr));
bgp_attr_dup (new, orig);
- bgp_attr_extra_get (new);
/*
* Set nexthop
@@ -83,17 +82,13 @@ encap_attr_export_ce (
{
case AF_INET:
new->nexthop = use_nexthop->u.prefix4;
- new->extra->mp_nexthop_len = 4; /* bytes */
+ new->mp_nexthop_len = 4; /* bytes */
new->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
break;
case AF_INET6:
- if (!new->extra)
- {
- new->extra = XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra));
- }
- new->extra->mp_nexthop_global = use_nexthop->u.prefix6;
- new->extra->mp_nexthop_len = 16; /* bytes */
+ new->mp_nexthop_global = use_nexthop->u.prefix6;
+ new->mp_nexthop_len = 16; /* bytes */
break;
default:
@@ -133,7 +128,6 @@ encap_attr_export_ce (
*
* Caller should, after using the attr, call:
* - bgp_attr_flush() to free non-interned parts
- * - call bgp_attr_extra_free() to free extra
*/
}
@@ -144,8 +138,8 @@ getce (struct bgp *bgp, struct attr *attr, struct prefix *pfx_ce)
int i;
uint16_t localadmin = bgp->rfapi_cfg->resolve_nve_roo_local_admin;
- for (ecp = attr->extra->ecommunity->val, i = 0;
- i < attr->extra->ecommunity->size; ++i, ecp += ECOMMUNITY_SIZE)
+ for (ecp = attr->ecommunity->val, i = 0;
+ i < attr->ecommunity->size; ++i, ecp += ECOMMUNITY_SIZE)
{
if (VNC_DEBUG(EXPORT_BGP_GETCE))
@@ -309,14 +303,12 @@ vnc_direct_bgp_add_route_ce (
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
return;
}
}
iattr = bgp_attr_intern (&hattr);
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
/*
* Rule: disallow route-map alteration of next-hop, because it
@@ -563,14 +555,14 @@ vnc_route_origin_ecom (struct route_node *rn)
struct ecommunity_val roec;
- switch (BGP_MP_NEXTHOP_FAMILY (bi->attr->extra->mp_nexthop_len))
+ switch (BGP_MP_NEXTHOP_FAMILY (bi->attr->mp_nexthop_len))
{
case AF_INET:
memset (&roec, 0, sizeof (roec));
roec.val[0] = 0x01;
roec.val[1] = 0x03;
memcpy (roec.val + 2,
- &bi->attr->extra->mp_nexthop_global_in.s_addr, 4);
+ &bi->attr->mp_nexthop_global_in.s_addr, 4);
roec.val[6] = 0;
roec.val[7] = 0;
ecommunity_add_val (new, &roec);
@@ -642,16 +634,16 @@ encap_attr_export (
{
use_nexthop = &orig_nexthop;
orig_nexthop.family =
- BGP_MP_NEXTHOP_FAMILY (orig->extra->mp_nexthop_len);
+ BGP_MP_NEXTHOP_FAMILY (orig->mp_nexthop_len);
if (orig_nexthop.family == AF_INET)
{
orig_nexthop.prefixlen = 32;
- orig_nexthop.u.prefix4 = orig->extra->mp_nexthop_global_in;
+ orig_nexthop.u.prefix4 = orig->mp_nexthop_global_in;
}
else if (orig_nexthop.family == AF_INET6)
{
orig_nexthop.prefixlen = 128;
- orig_nexthop.u.prefix6 = orig->extra->mp_nexthop_global;
+ orig_nexthop.u.prefix6 = orig->mp_nexthop_global;
}
else
{
@@ -673,17 +665,13 @@ encap_attr_export (
{
case AF_INET:
new->nexthop = use_nexthop->u.prefix4;
- new->extra->mp_nexthop_len = 4; /* bytes */
+ new->mp_nexthop_len = 4; /* bytes */
new->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
break;
case AF_INET6:
- if (!new->extra)
- {
- new->extra = XCALLOC (MTYPE_ATTR_EXTRA, sizeof (struct attr_extra));
- }
- new->extra->mp_nexthop_global = use_nexthop->u.prefix6;
- new->extra->mp_nexthop_len = 16; /* bytes */
+ new->mp_nexthop_global = use_nexthop->u.prefix6;
+ new->mp_nexthop_len = 16; /* bytes */
break;
default:
@@ -691,7 +679,6 @@ encap_attr_export (
break;
}
- bgp_attr_extra_get (new);
if (rn)
{
ecom_ro = vnc_route_origin_ecom (rn);
@@ -701,17 +688,14 @@ encap_attr_export (
/* TBD test/assert for IPv6 */
ecom_ro = vnc_route_origin_ecom_single (&use_nexthop->u.prefix4);
}
- if (new->extra->ecommunity)
+ if (new->ecommunity)
{
if (ecom_ro)
- {
- new->extra->ecommunity =
- ecommunity_merge (ecom_ro, new->extra->ecommunity);
- }
+ new->ecommunity = ecommunity_merge (ecom_ro, new->ecommunity);
}
else
{
- new->extra->ecommunity = ecom_ro;
+ new->ecommunity = ecom_ro;
}
if (ecom_ro)
{
@@ -750,7 +734,6 @@ encap_attr_export (
*
* Caller should, after using the attr, call:
* - bgp_attr_flush() to free non-interned parts
- * - call bgp_attr_extra_free() to free extra
*/
return 0;
@@ -887,7 +870,6 @@ vnc_direct_bgp_add_prefix (
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
vnc_zlog_debug_verbose
("%s: route map says DENY, so not calling bgp_update",
__func__);
@@ -903,7 +885,6 @@ vnc_direct_bgp_add_prefix (
iattr = bgp_attr_intern (&hattr);
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
bgp_update (irfd->peer, &rn->p, /* prefix */
0, /* addpath_id */
@@ -917,7 +898,6 @@ vnc_direct_bgp_add_prefix (
}
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
}
/*
@@ -1134,7 +1114,6 @@ vnc_direct_bgp_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd)
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
continue;
}
@@ -1142,7 +1121,6 @@ vnc_direct_bgp_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd)
iattr = bgp_attr_intern (&hattr);
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
bgp_update (irfd->peer, &rn->p, /* prefix */
0, /* addpath_id */
@@ -1157,7 +1135,6 @@ vnc_direct_bgp_add_nve (struct bgp *bgp, struct rfapi_descriptor *rfd)
}
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
}
}
}
@@ -1361,7 +1338,6 @@ vnc_direct_bgp_add_group_afi (
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
continue;
}
@@ -1369,7 +1345,6 @@ vnc_direct_bgp_add_group_afi (
iattr = bgp_attr_intern (&hattr);
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
bgp_update (irfd->peer, &rn->p, /* prefix */
0, /* addpath_id */
@@ -1384,7 +1359,6 @@ vnc_direct_bgp_add_group_afi (
}
aspath_unintern (&attr.aspath);
- bgp_attr_extra_free (&attr);
}
@@ -1744,14 +1718,12 @@ vnc_direct_bgp_rh_add_route (
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
return;
}
}
iattr = bgp_attr_intern (&hattr);
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
/*
* record route information that we will need to expire
@@ -1983,7 +1955,6 @@ vnc_direct_bgp_rh_vpn_enable (struct bgp *bgp, afi_t afi)
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
vnc_zlog_debug_verbose ("%s: route map says DENY", __func__);
continue;
}
@@ -1991,7 +1962,6 @@ vnc_direct_bgp_rh_vpn_enable (struct bgp *bgp, afi_t afi)
iattr = bgp_attr_intern (&hattr);
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
/*
* record route information that we will need to expire
diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c
index 47203cd23..1daf02a6b 100644
--- a/bgpd/rfapi/vnc_import_bgp.c
+++ b/bgpd/rfapi/vnc_import_bgp.c
@@ -393,7 +393,6 @@ process_unicast_route (
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
vnc_zlog_debug_verbose ("%s: route map \"%s\" says DENY, returning", __func__,
rmap->name);
return -1;
@@ -406,8 +405,8 @@ process_unicast_route (
*/
rfapiUnicastNexthop2Prefix (afi, &hattr, unicast_nexthop);
- if (hattr.extra && hattr.extra->ecommunity)
- *ecom = ecommunity_dup (hattr.extra->ecommunity);
+ if (hattr.ecommunity)
+ *ecom = ecommunity_dup (hattr.ecommunity);
else
*ecom = ecommunity_new ();
@@ -415,7 +414,6 @@ process_unicast_route (
* Done with hattr, clean up
*/
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
/*
* Add EC that carries original NH of iBGP route (2 bytes = magic
@@ -510,18 +508,18 @@ vnc_import_bgp_add_route_mode_resolve_nve_one_bi (
plifetime = &lifetime;
}
- if (bi->attr && bi->attr->extra)
+ if (bi->attr)
{
- encaptlvs = bi->attr->extra->vnc_subtlvs;
- if (bi->attr->extra->encap_tunneltype != BGP_ENCAP_TYPE_RESERVED &&
- bi->attr->extra->encap_tunneltype != BGP_ENCAP_TYPE_MPLS)
+ encaptlvs = bi->attr->vnc_subtlvs;
+ if (bi->attr->encap_tunneltype != BGP_ENCAP_TYPE_RESERVED &&
+ bi->attr->encap_tunneltype != BGP_ENCAP_TYPE_MPLS)
{
if (opt != NULL)
opt->next = &optary[cur_opt];
opt = &optary[cur_opt++];
memset (opt, 0, sizeof (struct rfapi_un_option));
opt->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE;
- opt->v.tunnel.type = bi->attr->extra->encap_tunneltype;
+ opt->v.tunnel.type = bi->attr->encap_tunneltype;
/* TBD parse bi->attr->extra->encap_subtlvs */
}
}
@@ -532,8 +530,8 @@ vnc_import_bgp_add_route_mode_resolve_nve_one_bi (
struct ecommunity *new_ecom = ecommunity_dup (ecom);
- if (bi->attr && bi->attr->extra && bi->attr->extra->ecommunity)
- ecommunity_merge (new_ecom, bi->attr->extra->ecommunity);
+ if (bi->attr && bi->attr->ecommunity)
+ ecommunity_merge (new_ecom, bi->attr->ecommunity);
if (bi->extra)
label = decode_label (&bi->extra->label);
@@ -891,7 +889,6 @@ vnc_import_bgp_add_route_mode_plain (struct bgp *bgp,
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
vnc_zlog_debug_verbose ("%s: route map \"%s\" says DENY, returning", __func__,
rmap->name);
return;
@@ -900,7 +897,6 @@ vnc_import_bgp_add_route_mode_plain (struct bgp *bgp,
iattr = bgp_attr_intern (&hattr);
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
/* Now iattr is an allocated interned attr */
@@ -925,8 +921,8 @@ vnc_import_bgp_add_route_mode_plain (struct bgp *bgp,
memset (&prd, 0, sizeof (prd));
rfapi_set_autord_from_vn (&prd, &vnaddr);
- if (iattr && iattr->extra && iattr->extra->ecommunity)
- ecom = ecommunity_dup (iattr->extra->ecommunity);
+ if (iattr && iattr->ecommunity)
+ ecom = ecommunity_dup (iattr->ecommunity);
}
@@ -1103,7 +1099,6 @@ vnc_import_bgp_add_route_mode_nvegroup (struct bgp *bgp,
if (ret == RMAP_DENYMATCH)
{
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
vnc_zlog_debug_verbose ("%s: route map \"%s\" says DENY, returning", __func__,
rmap->name);
return;
@@ -1112,7 +1107,6 @@ vnc_import_bgp_add_route_mode_nvegroup (struct bgp *bgp,
iattr = bgp_attr_intern (&hattr);
bgp_attr_flush (&hattr);
- bgp_attr_extra_free (&hattr);
/* Now iattr is an allocated interned attr */
@@ -1139,8 +1133,8 @@ vnc_import_bgp_add_route_mode_nvegroup (struct bgp *bgp,
else
ecom = ecommunity_new ();
- if (iattr && iattr->extra && iattr->extra->ecommunity)
- ecom = ecommunity_merge (ecom, iattr->extra->ecommunity);
+ if (iattr && iattr->ecommunity)
+ ecom = ecommunity_merge (ecom, iattr->ecommunity);
}
local_pref = calc_local_pref (iattr, peer);
@@ -1942,7 +1936,6 @@ vnc_import_bgp_exterior_add_route_it (
ZEBRA_ROUTE_BGP_DIRECT_EXT,
BGP_ROUTE_REDISTRIBUTE, &label);
- bgp_attr_extra_free (&new_attr);
}
if (have_usable_route)
@@ -2273,7 +2266,6 @@ vnc_import_bgp_exterior_add_route_interior (
ZEBRA_ROUTE_BGP_DIRECT_EXT,
BGP_ROUTE_REDISTRIBUTE, &label);
- bgp_attr_extra_free (&new_attr);
}
vnc_zlog_debug_verbose
("%s: finished constructing exteriors based on existing monitors",
@@ -2412,7 +2404,6 @@ vnc_import_bgp_exterior_add_route_interior (
ZEBRA_ROUTE_BGP_DIRECT_EXT,
BGP_ROUTE_REDISTRIBUTE, &label);
- bgp_attr_extra_free (&new_attr);
}
}
@@ -2536,7 +2527,6 @@ vnc_import_bgp_exterior_add_route_interior (
ZEBRA_ROUTE_BGP_DIRECT_EXT,
BGP_ROUTE_REDISTRIBUTE, &label);
- bgp_attr_extra_free (&new_attr);
}
}
if (list_adopted)
@@ -2740,7 +2730,6 @@ vnc_import_bgp_exterior_del_route_interior (
ZEBRA_ROUTE_BGP_DIRECT_EXT,
BGP_ROUTE_REDISTRIBUTE, &label);
- bgp_attr_extra_free (&new_attr);
}
}
diff --git a/lib/command.c b/lib/command.c
index 6d304fc12..a631cf1c6 100644
--- a/lib/command.c
+++ b/lib/command.c
@@ -909,6 +909,9 @@ node_parent ( enum node_type node )
case BGP_IPV6L_NODE:
ret = BGP_NODE;
break;
+ case BGP_EVPN_VNI_NODE:
+ ret = BGP_EVPN_NODE;
+ break;
case KEYCHAIN_KEY_NODE:
ret = KEYCHAIN_NODE;
break;
@@ -1278,6 +1281,9 @@ cmd_exit (struct vty *vty)
case BGP_IPV6L_NODE:
vty->node = BGP_NODE;
break;
+ case BGP_EVPN_VNI_NODE:
+ vty->node = BGP_EVPN_NODE;
+ break;
case LDP_IPV4_NODE:
case LDP_IPV6_NODE:
vty->node = LDP_NODE;
@@ -1346,6 +1352,7 @@ DEFUN (config_end,
case BGP_IPV6_NODE:
case BGP_IPV6M_NODE:
case BGP_EVPN_NODE:
+ case BGP_EVPN_VNI_NODE:
case BGP_IPV6L_NODE:
case RMAP_NODE:
case OSPF_NODE:
diff --git a/lib/command.h b/lib/command.h
index 5bde83001..4c2c5716b 100644
--- a/lib/command.h
+++ b/lib/command.h
@@ -136,6 +136,7 @@ enum node_type
MPLS_NODE, /* MPLS config node */
VTY_NODE, /* Vty node. */
LINK_PARAMS_NODE, /* Link-parameters node */
+ BGP_EVPN_VNI_NODE, /* BGP EVPN VNI */
};
/* Node which has some commands and prompt string and configuration
diff --git a/lib/log.c b/lib/log.c
index 1c61d7216..0628b163b 100644
--- a/lib/log.c
+++ b/lib/log.c
@@ -946,6 +946,15 @@ static const struct zebra_desc_table command_types[] = {
DESC_ENTRY (ZEBRA_LABEL_MANAGER_CONNECT),
DESC_ENTRY (ZEBRA_GET_LABEL_CHUNK),
DESC_ENTRY (ZEBRA_RELEASE_LABEL_CHUNK),
+ DESC_ENTRY (ZEBRA_ADVERTISE_ALL_VNI),
+ DESC_ENTRY (ZEBRA_VNI_ADD),
+ DESC_ENTRY (ZEBRA_VNI_DEL),
+ DESC_ENTRY (ZEBRA_REMOTE_VTEP_ADD),
+ DESC_ENTRY (ZEBRA_REMOTE_VTEP_DEL),
+ DESC_ENTRY (ZEBRA_MACIP_ADD),
+ DESC_ENTRY (ZEBRA_MACIP_DEL),
+ DESC_ENTRY (ZEBRA_REMOTE_MACIP_ADD),
+ DESC_ENTRY (ZEBRA_REMOTE_MACIP_DEL),
};
#undef DESC_ENTRY
diff --git a/lib/vty.c b/lib/vty.c
index 378507086..be2f2a6e5 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -768,6 +768,7 @@ vty_end_config (struct vty *vty)
case MASC_NODE:
case PIM_NODE:
case VTY_NODE:
+ case BGP_EVPN_VNI_NODE:
vty_config_unlock (vty);
vty->node = ENABLE_NODE;
break;
diff --git a/lib/zclient.c b/lib/zclient.c
index 679b7004c..efe8f5662 100644
--- a/lib/zclient.c
+++ b/lib/zclient.c
@@ -2047,6 +2047,22 @@ zclient_read (struct thread *thread)
if (zclient->fec_update)
(*zclient->fec_update) (command, zclient, length);
break;
+ case ZEBRA_VNI_ADD:
+ if (zclient->local_vni_add)
+ (*zclient->local_vni_add) (command, zclient, length, vrf_id);
+ break;
+ case ZEBRA_VNI_DEL:
+ if (zclient->local_vni_del)
+ (*zclient->local_vni_del) (command, zclient, length, vrf_id);
+ break;
+ case ZEBRA_MACIP_ADD:
+ if (zclient->local_macip_add)
+ (*zclient->local_macip_add) (command, zclient, length, vrf_id);
+ break;
+ case ZEBRA_MACIP_DEL:
+ if (zclient->local_macip_del)
+ (*zclient->local_macip_del) (command, zclient, length, vrf_id);
+ break;
default:
break;
}
diff --git a/lib/zclient.h b/lib/zclient.h
index 59412fdd4..c7cc857ef 100644
--- a/lib/zclient.h
+++ b/lib/zclient.h
@@ -96,6 +96,15 @@ typedef enum {
ZEBRA_FEC_REGISTER,
ZEBRA_FEC_UNREGISTER,
ZEBRA_FEC_UPDATE,
+ ZEBRA_ADVERTISE_ALL_VNI,
+ ZEBRA_VNI_ADD,
+ ZEBRA_VNI_DEL,
+ ZEBRA_REMOTE_VTEP_ADD,
+ ZEBRA_REMOTE_VTEP_DEL,
+ ZEBRA_MACIP_ADD,
+ ZEBRA_MACIP_DEL,
+ ZEBRA_REMOTE_MACIP_ADD,
+ ZEBRA_REMOTE_MACIP_DEL,
} zebra_message_types_t;
struct redist_proto
@@ -167,6 +176,10 @@ struct zclient
int (*redistribute_route_ipv6_add) (int, struct zclient *, uint16_t, vrf_id_t);
int (*redistribute_route_ipv6_del) (int, struct zclient *, uint16_t, vrf_id_t);
int (*fec_update) (int, struct zclient *, uint16_t);
+ int (*local_vni_add) (int, struct zclient *, uint16_t, vrf_id_t);
+ int (*local_vni_del) (int, struct zclient *, uint16_t, vrf_id_t);
+ int (*local_macip_add) (int, struct zclient *, uint16_t, vrf_id_t);
+ int (*local_macip_del) (int, struct zclient *, uint16_t, vrf_id_t);
};
/* Zebra API message flag. */
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index e4bf5b257..ec8a365c3 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -314,6 +314,10 @@ vtysh_execute_func (const char *line, int pager)
{
vtysh_execute("exit-address-family");
}
+ else if ((saved_node == BGP_EVPN_VNI_NODE) && (tried == 1))
+ {
+ vtysh_execute("exit-vni");
+ }
else if (saved_node == BGP_VRF_POLICY_NODE && (tried == 1))
{
vtysh_execute("exit-vrf-policy");
@@ -576,6 +580,10 @@ vtysh_mark_file (const char *filename)
{
fprintf(stdout, "exit-address-family\n");
}
+ else if ((prev_node == BGP_EVPN_VNI_NODE) && (tried == 1))
+ {
+ fprintf(stdout, "exit-vni\n");
+ }
else if ((prev_node == KEYCHAIN_KEY_NODE) && (tried == 1))
{
fprintf(stdout, "exit\n");
@@ -989,6 +997,12 @@ static struct cmd_node bgp_evpn_node =
"%s(config-router-af)# "
};
+static struct cmd_node bgp_evpn_vni_node =
+{
+ BGP_EVPN_VNI_NODE,
+ "%s(config-router-af-vni)# "
+};
+
static struct cmd_node bgp_ipv6l_node =
{
BGP_IPV6L_NODE,
@@ -1275,13 +1289,37 @@ DEFUNSH (VTYSH_BGPD,
address_family_evpn,
address_family_evpn_cmd,
"address-family <l2vpn evpn>",
- "Enter Address Family command mode\n"
- "Address Family\n"
- "Address Family modifier\n")
+ "Enter Address Family command mode\n"
+ "Address Family\n"
+ "Address Family modifier\n")
+{
+ vty->node = BGP_EVPN_NODE;
+ return CMD_SUCCESS;
+}
+
+#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")
{
vty->node = BGP_EVPN_NODE;
return CMD_SUCCESS;
}
+#endif
+
+DEFUNSH (VTYSH_BGPD,
+ bgp_evpn_vni,
+ bgp_evpn_vni_cmd,
+ "vni (1-16777215)",
+ "VXLAN Network Identifier\n"
+ "VNI number\n")
+{
+ vty->node = BGP_EVPN_VNI_NODE;
+ return CMD_SUCCESS;
+}
#if defined (ENABLE_BGP_VNC)
DEFUNSH (VTYSH_BGPD,
@@ -1622,6 +1660,9 @@ vtysh_exit (struct vty *vty)
case BGP_VNC_L2_GROUP_NODE:
vty->node = BGP_NODE;
break;
+ case BGP_EVPN_VNI_NODE:
+ vty->node = BGP_EVPN_NODE;
+ break;
case LDP_IPV4_NODE:
case LDP_IPV6_NODE:
vty->node = LDP_NODE;
@@ -1678,12 +1719,24 @@ DEFUNSH (VTYSH_BGPD,
|| vty->node == BGP_VPNV6_NODE
|| vty->node == BGP_IPV6_NODE
|| vty->node == BGP_IPV6L_NODE
- || vty->node == BGP_IPV6M_NODE)
+ || vty->node == BGP_IPV6M_NODE
+ || vty->node == BGP_EVPN_NODE)
vty->node = BGP_NODE;
return CMD_SUCCESS;
}
DEFUNSH (VTYSH_BGPD,
+ exit_vni,
+ exit_vni_cmd,
+ "exit-vni",
+ "Exit from VNI mode\n")
+{
+ if (vty->node == BGP_EVPN_VNI_NODE)
+ vty->node = BGP_EVPN_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUNSH (VTYSH_BGPD,
exit_vnc_config,
exit_vnc_config_cmd,
"exit-vnc",
@@ -3289,6 +3342,7 @@ vtysh_init_vty (void)
install_node (&bgp_ipv6l_node, NULL);
install_node (&bgp_vrf_policy_node, NULL);
install_node (&bgp_evpn_node, NULL);
+ install_node (&bgp_evpn_vni_node, NULL);
install_node (&bgp_vnc_defaults_node, NULL);
install_node (&bgp_vnc_nve_group_node, NULL);
install_node (&bgp_vnc_l2_group_node, NULL);
@@ -3327,6 +3381,7 @@ vtysh_init_vty (void)
vtysh_install_default (BGP_IPV6_NODE);
vtysh_install_default (BGP_IPV6M_NODE);
vtysh_install_default (BGP_EVPN_NODE);
+ vtysh_install_default (BGP_EVPN_VNI_NODE);
vtysh_install_default (BGP_IPV6L_NODE);
#if ENABLE_BGP_VNC
vtysh_install_default (BGP_VRF_POLICY_NODE);
@@ -3405,6 +3460,9 @@ vtysh_init_vty (void)
install_element (BGP_IPV6M_NODE, &vtysh_exit_bgpd_cmd);
install_element (BGP_IPV6M_NODE, &vtysh_quit_bgpd_cmd);
install_element (BGP_EVPN_NODE, &vtysh_quit_bgpd_cmd);
+ install_element (BGP_EVPN_NODE, &vtysh_exit_bgpd_cmd);
+ install_element (BGP_EVPN_VNI_NODE, &vtysh_exit_bgpd_cmd);
+ install_element (BGP_EVPN_VNI_NODE, &vtysh_quit_bgpd_cmd);
install_element (BGP_IPV6L_NODE, &vtysh_exit_bgpd_cmd);
install_element (BGP_IPV6L_NODE, &vtysh_quit_bgpd_cmd);
#if defined (ENABLE_BGP_VNC)
@@ -3455,6 +3513,7 @@ vtysh_init_vty (void)
install_element (BGP_IPV6L_NODE, &vtysh_end_all_cmd);
install_element (BGP_VRF_POLICY_NODE, &vtysh_end_all_cmd);
install_element (BGP_EVPN_NODE, &vtysh_end_all_cmd);
+ install_element (BGP_EVPN_VNI_NODE, &vtysh_end_all_cmd);
install_element (BGP_VNC_DEFAULTS_NODE, &vtysh_end_all_cmd);
install_element (BGP_VNC_NVE_GROUP_NODE, &vtysh_end_all_cmd);
install_element (BGP_VNC_L2_GROUP_NODE, &vtysh_end_all_cmd);
@@ -3517,6 +3576,9 @@ vtysh_init_vty (void)
install_element (BGP_NODE, &address_family_ipv6_vpn_cmd);
install_element (BGP_NODE, &address_family_ipv6_labeled_unicast_cmd);
install_element (BGP_NODE, &address_family_evpn_cmd);
+#if defined (HAVE_CUMULUS)
+ install_element (BGP_NODE, &address_family_evpn2_cmd);
+#endif
install_element (BGP_VPNV4_NODE, &exit_address_family_cmd);
install_element (BGP_VPNV6_NODE, &exit_address_family_cmd);
install_element (BGP_IPV4_NODE, &exit_address_family_cmd);
@@ -3527,6 +3589,10 @@ vtysh_init_vty (void)
install_element (BGP_EVPN_NODE, &exit_address_family_cmd);
install_element (BGP_IPV6L_NODE, &exit_address_family_cmd);
+ /* EVPN commands */
+ install_element (BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
+ install_element (BGP_EVPN_VNI_NODE, &exit_vni_cmd);
+
install_element (BGP_VRF_POLICY_NODE, &exit_vrf_policy_cmd);
install_element (BGP_VNC_DEFAULTS_NODE, &exit_vnc_config_cmd);
install_element (BGP_VNC_NVE_GROUP_NODE, &exit_vnc_config_cmd);
diff --git a/zebra/Makefile.am b/zebra/Makefile.am
index f70d603d3..46ecad5e4 100644
--- a/zebra/Makefile.am
+++ b/zebra/Makefile.am
@@ -33,6 +33,8 @@ zebra_SOURCES = \
zebra_ns.c zebra_vrf.c zebra_static.c zebra_mpls.c zebra_mpls_vty.c \
zebra_mroute.c \
label_manager.c \
+ zebra_l2.c \
+ zebra_vxlan.c \
# end
noinst_HEADERS = \
@@ -42,7 +44,8 @@ noinst_HEADERS = \
rt_netlink.h zebra_fpm_private.h zebra_rnh.h \
zebra_ptm_redistribute.h zebra_ptm.h zebra_routemap.h \
zebra_ns.h zebra_vrf.h ioctl_solaris.h zebra_static.h zebra_mpls.h \
- kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h
+ kernel_netlink.h if_netlink.h zebra_mroute.h label_manager.h \
+ zebra_l2.h zebra_vxlan_private.h zebra_vxlan.h
zebra_LDADD = $(otherobj) ../lib/libfrr.la $(LIBCAP)
diff --git a/zebra/debug.c b/zebra/debug.c
index f99e06bbb..1c3cf9a3d 100644
--- a/zebra/debug.c
+++ b/zebra/debug.c
@@ -31,6 +31,7 @@ unsigned long zebra_debug_rib;
unsigned long zebra_debug_fpm;
unsigned long zebra_debug_nht;
unsigned long zebra_debug_mpls;
+unsigned long zebra_debug_vxlan;
DEFUN (show_debugging_zebra,
show_debugging_zebra_cmd,
@@ -118,6 +119,17 @@ DEFUN (debug_zebra_mpls,
return CMD_WARNING_CONFIG_FAILED;
}
+DEFUN (debug_zebra_vxlan,
+ debug_zebra_vxlan_cmd,
+ "debug zebra vxlan",
+ DEBUG_STR
+ "Zebra configuration\n"
+ "Debug option set for zebra VxLAN (EVPN)\n")
+{
+ zebra_debug_vxlan = ZEBRA_DEBUG_VXLAN;
+ return CMD_WARNING;
+}
+
DEFUN (debug_zebra_packet,
debug_zebra_packet_cmd,
"debug zebra packet [<recv|send>] [detail]",
@@ -248,6 +260,18 @@ DEFUN (no_debug_zebra_mpls,
return CMD_SUCCESS;
}
+DEFUN (no_debug_zebra_vxlan,
+ no_debug_zebra_vxlan_cmd,
+ "no debug zebra vxlan",
+ NO_STR
+ DEBUG_STR
+ "Zebra configuration\n"
+ "Debug option set for zebra VxLAN (EVPN)\n")
+{
+ zebra_debug_vxlan = 0;
+ return CMD_SUCCESS;
+}
+
DEFUN (no_debug_zebra_packet,
no_debug_zebra_packet_cmd,
"no debug zebra packet [<recv|send>]",
@@ -413,6 +437,11 @@ config_write_debug (struct vty *vty)
vty_out (vty, "debug zebra mpls\n");
write++;
}
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ {
+ vty_out (vty, "debug zebra vxlan\n");
+ write++;
+ }
return write;
}
@@ -425,6 +454,7 @@ zebra_debug_init (void)
zebra_debug_rib = 0;
zebra_debug_fpm = 0;
zebra_debug_mpls = 0;
+ zebra_debug_vxlan = 0;
install_node (&debug_node, config_write_debug);
@@ -433,6 +463,7 @@ zebra_debug_init (void)
install_element (ENABLE_NODE, &debug_zebra_events_cmd);
install_element (ENABLE_NODE, &debug_zebra_nht_cmd);
install_element (ENABLE_NODE, &debug_zebra_mpls_cmd);
+ install_element (ENABLE_NODE, &debug_zebra_vxlan_cmd);
install_element (ENABLE_NODE, &debug_zebra_packet_cmd);
install_element (ENABLE_NODE, &debug_zebra_kernel_cmd);
install_element (ENABLE_NODE, &debug_zebra_kernel_msgdump_cmd);
@@ -442,6 +473,7 @@ zebra_debug_init (void)
install_element (ENABLE_NODE, &no_debug_zebra_events_cmd);
install_element (ENABLE_NODE, &no_debug_zebra_nht_cmd);
install_element (ENABLE_NODE, &no_debug_zebra_mpls_cmd);
+ install_element (ENABLE_NODE, &no_debug_zebra_vxlan_cmd);
install_element (ENABLE_NODE, &no_debug_zebra_packet_cmd);
install_element (ENABLE_NODE, &no_debug_zebra_kernel_cmd);
install_element (ENABLE_NODE, &no_debug_zebra_kernel_msgdump_cmd);
@@ -452,6 +484,7 @@ zebra_debug_init (void)
install_element (CONFIG_NODE, &debug_zebra_events_cmd);
install_element (CONFIG_NODE, &debug_zebra_nht_cmd);
install_element (CONFIG_NODE, &debug_zebra_mpls_cmd);
+ install_element (CONFIG_NODE, &debug_zebra_vxlan_cmd);
install_element (CONFIG_NODE, &debug_zebra_packet_cmd);
install_element (CONFIG_NODE, &debug_zebra_kernel_cmd);
install_element (CONFIG_NODE, &debug_zebra_kernel_msgdump_cmd);
@@ -461,6 +494,7 @@ zebra_debug_init (void)
install_element (CONFIG_NODE, &no_debug_zebra_events_cmd);
install_element (CONFIG_NODE, &no_debug_zebra_nht_cmd);
install_element (CONFIG_NODE, &no_debug_zebra_mpls_cmd);
+ install_element (CONFIG_NODE, &no_debug_zebra_vxlan_cmd);
install_element (CONFIG_NODE, &no_debug_zebra_packet_cmd);
install_element (CONFIG_NODE, &no_debug_zebra_kernel_cmd);
install_element (CONFIG_NODE, &no_debug_zebra_kernel_msgdump_cmd);
diff --git a/zebra/debug.h b/zebra/debug.h
index 0a50da817..5687a3516 100644
--- a/zebra/debug.h
+++ b/zebra/debug.h
@@ -42,6 +42,8 @@
#define ZEBRA_DEBUG_MPLS 0x01
+#define ZEBRA_DEBUG_VXLAN 0x01
+
/* Debug related macro. */
#define IS_ZEBRA_DEBUG_EVENT (zebra_debug_event & ZEBRA_DEBUG_EVENT)
@@ -63,6 +65,7 @@
#define IS_ZEBRA_DEBUG_FPM (zebra_debug_fpm & ZEBRA_DEBUG_FPM)
#define IS_ZEBRA_DEBUG_NHT (zebra_debug_nht & ZEBRA_DEBUG_NHT)
#define IS_ZEBRA_DEBUG_MPLS (zebra_debug_mpls & ZEBRA_DEBUG_MPLS)
+#define IS_ZEBRA_DEBUG_VXLAN (zebra_debug_vxlan & ZEBRA_DEBUG_VXLAN)
extern unsigned long zebra_debug_event;
extern unsigned long zebra_debug_packet;
@@ -71,6 +74,7 @@ extern unsigned long zebra_debug_rib;
extern unsigned long zebra_debug_fpm;
extern unsigned long zebra_debug_nht;
extern unsigned long zebra_debug_mpls;
+extern unsigned long zebra_debug_vxlan;
extern void zebra_debug_init (void);
diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c
index 5af6c3a08..d0907a267 100644
--- a/zebra/if_netlink.c
+++ b/zebra/if_netlink.c
@@ -20,6 +20,15 @@
*/
#include <zebra.h>
+
+/* The following definition is to workaround an issue in the Linux kernel
+ * header files with redefinition of 'struct in6_addr' in both
+ * netinet/in.h and linux/in6.h.
+ * Reference - https://sourceware.org/ml/libc-alpha/2013-01/msg00599.html
+ */
+#define _LINUX_IN6_H
+
+#include <linux/if_bridge.h>
#include <net/if_arp.h>
#include <linux/sockios.h>
#include <linux/ethtool.h>
@@ -175,6 +184,23 @@ netlink_to_zebra_link_type (unsigned int hwt)
}
}
+static void
+netlink_determine_zebra_iftype (char *kind, zebra_iftype_t *zif_type)
+{
+ *zif_type = ZEBRA_IF_OTHER;
+
+ if (!kind)
+ return;
+
+ if (strcmp(kind, "vrf") == 0)
+ *zif_type = ZEBRA_IF_VRF;
+ else if (strcmp(kind, "bridge") == 0)
+ *zif_type = ZEBRA_IF_BRIDGE;
+ else if (strcmp(kind, "vlan") == 0)
+ *zif_type = ZEBRA_IF_VLAN;
+ else if (strcmp(kind, "vxlan") == 0)
+ *zif_type = ZEBRA_IF_VXLAN;
+}
//Temporary Assignments to compile on older platforms.
#ifndef IFLA_BR_MAX
@@ -341,6 +367,173 @@ get_iflink_speed (const char *ifname)
return (ecmd.speed_hi << 16 ) | ecmd.speed;
}
+static int
+netlink_extract_bridge_info (struct rtattr *link_data,
+ struct zebra_l2info_bridge *bridge_info)
+{
+ struct rtattr *attr[IFLA_BR_MAX+1];
+
+ memset (bridge_info, 0, sizeof (*bridge_info));
+ memset (attr, 0, sizeof attr);
+ parse_rtattr_nested(attr, IFLA_BR_MAX, link_data);
+ if (attr[IFLA_BR_VLAN_FILTERING])
+ bridge_info->vlan_aware = *(u_char *)RTA_DATA(attr[IFLA_BR_VLAN_FILTERING]);
+ return 0;
+}
+
+static int
+netlink_extract_vlan_info (struct rtattr *link_data,
+ struct zebra_l2info_vlan *vlan_info)
+{
+ struct rtattr *attr[IFLA_VLAN_MAX+1];
+ vlanid_t vid_in_msg;
+
+ memset (vlan_info, 0, sizeof (*vlan_info));
+ memset (attr, 0, sizeof attr);
+ parse_rtattr_nested(attr, IFLA_VLAN_MAX, link_data);
+ if (!attr[IFLA_VLAN_ID])
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("IFLA_VLAN_ID missing from VLAN IF message");
+ return -1;
+ }
+
+ vid_in_msg = *(vlanid_t *)RTA_DATA(attr[IFLA_VLAN_ID]);
+ vlan_info->vid = vid_in_msg;
+ return 0;
+}
+
+static int
+netlink_extract_vxlan_info (struct rtattr *link_data,
+ struct zebra_l2info_vxlan *vxl_info)
+{
+ struct rtattr *attr[IFLA_VXLAN_MAX+1];
+ vni_t vni_in_msg;
+ struct in_addr vtep_ip_in_msg;
+
+ memset (vxl_info, 0, sizeof (*vxl_info));
+ memset (attr, 0, sizeof attr);
+ parse_rtattr_nested(attr, IFLA_VXLAN_MAX, link_data);
+ if (!attr[IFLA_VXLAN_ID])
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("IFLA_VXLAN_ID missing from VXLAN IF message");
+ return -1;
+ }
+
+ vni_in_msg = *(vni_t *)RTA_DATA(attr[IFLA_VXLAN_ID]);
+ vxl_info->vni = vni_in_msg;
+ if (!attr[IFLA_VXLAN_LOCAL])
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("IFLA_VXLAN_LOCAL missing from VXLAN IF message");
+ }
+ else
+ {
+ vtep_ip_in_msg = *(struct in_addr *)RTA_DATA(attr[IFLA_VXLAN_LOCAL]);
+ vxl_info->vtep_ip = vtep_ip_in_msg;
+ }
+
+ return 0;
+}
+
+/*
+ * Extract and save L2 params (of interest) for an interface. When a
+ * bridge interface is added or updated, take further actions to map
+ * its members. Likewise, for VxLAN interface.
+ */
+static void
+netlink_interface_update_l2info (struct interface *ifp,
+ struct rtattr *link_data,
+ int add)
+{
+ if (!link_data)
+ return;
+
+ if (IS_ZEBRA_IF_BRIDGE(ifp))
+ {
+ struct zebra_l2info_bridge bridge_info;
+
+ netlink_extract_bridge_info (link_data, &bridge_info);
+ zebra_l2_bridge_add_update (ifp, &bridge_info, add);
+ }
+ else if (IS_ZEBRA_IF_VLAN(ifp))
+ {
+ struct zebra_l2info_vlan vlan_info;
+
+ netlink_extract_vlan_info (link_data, &vlan_info);
+ zebra_l2_vlanif_update (ifp, &vlan_info);
+ }
+ else if (IS_ZEBRA_IF_VXLAN(ifp))
+ {
+ struct zebra_l2info_vxlan vxlan_info;
+
+ netlink_extract_vxlan_info (link_data, &vxlan_info);
+ zebra_l2_vxlanif_add_update (ifp, &vxlan_info, add);
+ }
+}
+
+static int
+netlink_bridge_interface (struct nlmsghdr *h, int len,
+ ns_id_t ns_id, int startup)
+{
+ char *name = NULL;
+ struct ifinfomsg *ifi;
+ struct rtattr *tb[IFLA_MAX + 1];
+ struct interface *ifp;
+ struct rtattr *aftb[IFLA_BRIDGE_MAX + 1];
+ struct
+ {
+ u_int16_t flags;
+ u_int16_t vid;
+ } *vinfo;
+ vlanid_t access_vlan;
+
+ /* Fetch name and ifindex */
+ ifi = NLMSG_DATA (h);
+ memset (tb, 0, sizeof tb);
+ netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len);
+
+ if (tb[IFLA_IFNAME] == NULL)
+ return -1;
+ name = (char *) RTA_DATA (tb[IFLA_IFNAME]);
+
+ /* The interface should already be known, if not discard. */
+ ifp = if_lookup_by_index_per_ns (zebra_ns_lookup (ns_id),
+ ifi->ifi_index);
+ if (!ifp)
+ {
+ zlog_warn ("Cannot find bridge IF %s(%u)",
+ name, ifi->ifi_index);
+ return 0;
+ }
+ if (!IS_ZEBRA_IF_VXLAN(ifp))
+ return 0;
+
+ /* We are only interested in the access VLAN i.e., AF_SPEC */
+ if (!tb[IFLA_AF_SPEC])
+ return 0;
+
+ /* There is a 1-to-1 mapping of VLAN to VxLAN - hence
+ * only 1 access VLAN is accepted.
+ */
+ memset (aftb, 0, sizeof aftb);
+ parse_rtattr_nested(aftb, IFLA_BRIDGE_MAX, tb[IFLA_AF_SPEC]);
+ if (!aftb[IFLA_BRIDGE_VLAN_INFO])
+ return 0;
+
+ vinfo = RTA_DATA(aftb[IFLA_BRIDGE_VLAN_INFO]);
+ if (!(vinfo->flags & BRIDGE_VLAN_INFO_PVID))
+ return 0;
+
+ access_vlan = (vlanid_t) vinfo->vid;
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("Access VLAN %u for VxLAN IF %s(%u)",
+ access_vlan, name, ifi->ifi_index);
+ zebra_l2_vxlanif_update_access_vlan (ifp, access_vlan);
+ return 0;
+}
+
/* Called from interface_lookup_netlink(). This function is only used
during bootstrap. */
static int
@@ -355,9 +548,12 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h,
char *name = NULL;
char *kind = NULL;
char *slave_kind = NULL;
- int vrf_device = 0;
struct zebra_ns *zns;
vrf_id_t vrf_id = VRF_DEFAULT;
+ zebra_iftype_t zif_type = ZEBRA_IF_OTHER;
+ zebra_slave_iftype_t zif_slave_type = ZEBRA_IF_SLAVE_NONE;
+ ifindex_t bridge_ifindex = IFINDEX_INTERNAL;
+ ifindex_t link_ifindex = IFINDEX_INTERNAL;
zns = zebra_ns_lookup (ns_id);
ifi = NLMSG_DATA (h);
@@ -369,11 +565,13 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (len < 0)
return -1;
+ /* We are interested in some AF_BRIDGE notifications. */
if (ifi->ifi_family == AF_BRIDGE)
- return 0;
+ return netlink_bridge_interface (h, len, ns_id, startup);
/* Looking up interface name. */
memset (tb, 0, sizeof tb);
+ memset (linkinfo, 0, sizeof linkinfo);
netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len);
#ifdef IFLA_WIRELESS
@@ -392,7 +590,6 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (tb[IFLA_LINKINFO])
{
- memset (linkinfo, 0, sizeof linkinfo);
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
if (linkinfo[IFLA_INFO_KIND])
@@ -403,37 +600,64 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h,
slave_kind = RTA_DATA(linkinfo[IFLA_INFO_SLAVE_KIND]);
#endif
- if (kind && strcmp(kind, "vrf") == 0)
- {
- vrf_device = 1;
- netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
- vrf_id = (vrf_id_t)ifi->ifi_index;
- }
+ netlink_determine_zebra_iftype (kind, &zif_type);
+ }
+
+ /* If VRF, create the VRF structure itself. */
+ if (zif_type == ZEBRA_IF_VRF)
+ {
+ netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
+ vrf_id = (vrf_id_t)ifi->ifi_index;
}
if (tb[IFLA_MASTER])
{
if (slave_kind && (strcmp(slave_kind, "vrf") == 0))
- vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
+ {
+ zif_slave_type = ZEBRA_IF_SLAVE_VRF;
+ vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
+ }
+ else if (slave_kind && (strcmp(slave_kind, "bridge") == 0))
+ {
+ zif_slave_type = ZEBRA_IF_SLAVE_BRIDGE;
+ bridge_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]);
+ }
+ else
+ zif_slave_type = ZEBRA_IF_SLAVE_OTHER;
}
+ /* If linking to another interface, note it. */
+ if (tb[IFLA_LINK])
+ link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]);
+
/* Add interface. */
ifp = if_get_by_name (name, vrf_id);
set_ifindex(ifp, ifi->ifi_index, zns);
ifp->flags = ifi->ifi_flags & 0x0000fffff;
- if (vrf_device)
+ if (IS_ZEBRA_IF_VRF(ifp))
SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
ifp->mtu6 = ifp->mtu = *(uint32_t *) RTA_DATA (tb[IFLA_MTU]);
ifp->metric = 0;
ifp->speed = get_iflink_speed (name);
ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN;
+ /* Set zebra interface type */
+ zebra_if_set_ziftype (ifp, zif_type, zif_slave_type);
+
+ /* Update link. */
+ zebra_if_update_link (ifp, link_ifindex);
+
/* Hardware type and address. */
ifp->ll_type = netlink_to_zebra_link_type (ifi->ifi_type);
netlink_interface_update_hw_addr (tb, ifp);
if_add_update (ifp);
+ /* Extract and save L2 interface information, take additional actions. */
+ netlink_interface_update_l2info (ifp, linkinfo[IFLA_INFO_DATA], 1);
+ if (IS_ZEBRA_IF_BRIDGE_SLAVE (ifp))
+ zebra_l2if_update_bridge_slave (ifp, bridge_ifindex);
+
return 0;
}
@@ -477,6 +701,24 @@ interface_lookup_netlink (struct zebra_ns *zns)
if (ret < 0)
return ret;
+ /* Get interface information - for bridge interfaces. */
+ ret = netlink_request_intf_addr (zns, AF_BRIDGE, RTM_GETLINK,
+ RTEXT_FILTER_BRVLAN);
+ if (ret < 0)
+ return ret;
+ ret = netlink_parse_info (netlink_interface, &zns->netlink_cmd, zns, 0, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Get interface information - for bridge interfaces. */
+ ret = netlink_request_intf_addr (zns, AF_BRIDGE, RTM_GETLINK,
+ RTEXT_FILTER_BRVLAN);
+ if (ret < 0)
+ return ret;
+ ret = netlink_parse_info (netlink_interface, &zns->netlink_cmd, zns, 0, 0);
+ if (ret < 0)
+ return ret;
+
/* Get IPv4 address of the interfaces. */
ret = netlink_request_intf_addr (zns, AF_INET, RTM_GETADDR, 0);
if (ret < 0)
@@ -712,9 +954,13 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
char *name = NULL;
char *kind = NULL;
char *slave_kind = NULL;
- int vrf_device = 0;
struct zebra_ns *zns;
vrf_id_t vrf_id = VRF_DEFAULT;
+ zebra_iftype_t zif_type = ZEBRA_IF_OTHER;
+ zebra_slave_iftype_t zif_slave_type = ZEBRA_IF_SLAVE_NONE;
+ ifindex_t bridge_ifindex = IFINDEX_INTERNAL;
+ ifindex_t link_ifindex = IFINDEX_INTERNAL;
+
zns = zebra_ns_lookup (ns_id);
ifi = NLMSG_DATA (h);
@@ -731,11 +977,13 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (len < 0)
return -1;
+ /* We are interested in some AF_BRIDGE notifications. */
if (ifi->ifi_family == AF_BRIDGE)
- return 0;
+ return netlink_bridge_interface (h, len, ns_id, startup);
/* Looking up interface name. */
memset (tb, 0, sizeof tb);
+ memset (linkinfo, 0, sizeof linkinfo);
netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len);
#ifdef IFLA_WIRELESS
@@ -754,7 +1002,6 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (tb[IFLA_LINKINFO])
{
- memset (linkinfo, 0, sizeof linkinfo);
parse_rtattr_nested(linkinfo, IFLA_INFO_MAX, tb[IFLA_LINKINFO]);
if (linkinfo[IFLA_INFO_KIND])
@@ -765,12 +1012,18 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
slave_kind = RTA_DATA(linkinfo[IFLA_INFO_SLAVE_KIND]);
#endif
- if (kind && strcmp(kind, "vrf") == 0)
- {
- vrf_device = 1;
- netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
- vrf_id = (vrf_id_t)ifi->ifi_index;
- }
+ netlink_determine_zebra_iftype (kind, &zif_type);
+ }
+
+ /* If linking to another interface, note it. */
+ if (tb[IFLA_LINK])
+ link_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_LINK]);
+
+ /* If VRF, create or update the VRF structure itself. */
+ if (zif_type == ZEBRA_IF_VRF)
+ {
+ netlink_vrf_change(h, tb[IFLA_LINKINFO], name);
+ vrf_id = (vrf_id_t)ifi->ifi_index;
}
/* See if interface is present. */
@@ -781,15 +1034,27 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
if (tb[IFLA_MASTER])
{
if (slave_kind && (strcmp(slave_kind, "vrf") == 0))
- vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
+ {
+ zif_slave_type = ZEBRA_IF_SLAVE_VRF;
+ vrf_id = *(u_int32_t *)RTA_DATA(tb[IFLA_MASTER]);
+ }
+ else if (slave_kind && (strcmp(slave_kind, "bridge") == 0))
+ {
+ zif_slave_type = ZEBRA_IF_SLAVE_BRIDGE;
+ bridge_ifindex = *(ifindex_t *)RTA_DATA(tb[IFLA_MASTER]);
+ }
+ else
+ zif_slave_type = ZEBRA_IF_SLAVE_OTHER;
}
if (ifp == NULL || !CHECK_FLAG (ifp->status, ZEBRA_INTERFACE_ACTIVE))
{
/* Add interface notification from kernel */
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug ("RTM_NEWLINK for %s(%u) (ifp %p) vrf_id %u flags 0x%x",
- name, ifi->ifi_index, ifp, vrf_id, ifi->ifi_flags);
+ zlog_debug ("RTM_NEWLINK ADD for %s(%u) vrf_id %u type %d "
+ "sl_type %d master %u flags 0x%x",
+ name, ifi->ifi_index, vrf_id, zif_type, zif_slave_type,
+ bridge_ifindex, ifi->ifi_flags);
if (ifp == NULL)
{
@@ -806,16 +1071,27 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
/* Update interface information. */
set_ifindex(ifp, ifi->ifi_index, zns);
ifp->flags = ifi->ifi_flags & 0x0000fffff;
- if (vrf_device)
+ if (IS_ZEBRA_IF_VRF(ifp))
SET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
ifp->metric = 0;
ifp->ptm_status = ZEBRA_PTM_STATUS_UNKNOWN;
+ /* Set interface type */
+ zebra_if_set_ziftype (ifp, zif_type, zif_slave_type);
+
+ /* Update link. */
+ zebra_if_update_link (ifp, link_ifindex);
+
netlink_interface_update_hw_addr (tb, ifp);
/* Inform clients, install any configured addresses. */
if_add_update (ifp);
+
+ /* Extract and save L2 interface information, take additional actions. */
+ netlink_interface_update_l2info (ifp, linkinfo[IFLA_INFO_DATA], 1);
+ if (IS_ZEBRA_IF_BRIDGE_SLAVE (ifp))
+ zebra_l2if_update_bridge_slave (ifp, bridge_ifindex);
}
else if (ifp->vrf_id != vrf_id)
{
@@ -830,32 +1106,59 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
}
else
{
- /* Interface status change. */
+ int was_bridge_slave;
+
+ /* Interface update. */
if (IS_ZEBRA_DEBUG_KERNEL)
- zlog_debug ("RTM_NEWLINK status for %s(%u) flags 0x%x",
- name, ifp->ifindex, ifi->ifi_flags);
+ zlog_debug ("RTM_NEWLINK update for %s(%u) "
+ "sl_type %d master %u flags 0x%x",
+ name, ifp->ifindex, zif_slave_type,
+ bridge_ifindex, ifi->ifi_flags);
set_ifindex(ifp, ifi->ifi_index, zns);
ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
ifp->metric = 0;
+ /* Update interface type - NOTE: Only slave_type can change. */
+ was_bridge_slave = IS_ZEBRA_IF_BRIDGE_SLAVE (ifp);
+ zebra_if_set_ziftype (ifp, zif_type, zif_slave_type);
+
netlink_interface_update_hw_addr (tb, ifp);
if (if_is_no_ptm_operative (ifp))
{
ifp->flags = ifi->ifi_flags & 0x0000fffff;
if (!if_is_no_ptm_operative (ifp))
- if_down (ifp);
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("Intf %s(%u) has gone DOWN",
+ name, ifp->ifindex);
+ if_down (ifp);
+ }
else if (if_is_operative (ifp))
- /* Must notify client daemons of new interface status. */
- zebra_interface_up_update (ifp);
+ {
+ /* Must notify client daemons of new interface status. */
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("Intf %s(%u) PTM up, notifying clients",
+ name, ifp->ifindex);
+ zebra_interface_up_update (ifp);
+ }
}
else
{
ifp->flags = ifi->ifi_flags & 0x0000fffff;
if (if_is_operative (ifp))
- if_up (ifp);
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("Intf %s(%u) has come UP", name, ifp->ifindex);
+ if_up (ifp);
+ }
}
+
+ /* Extract and save L2 interface information, take additional actions. */
+ netlink_interface_update_l2info (ifp, linkinfo[IFLA_INFO_DATA], 0);
+ if (IS_ZEBRA_IF_BRIDGE_SLAVE (ifp) || was_bridge_slave)
+ zebra_l2if_update_bridge_slave (ifp, bridge_ifindex);
}
}
else
@@ -873,7 +1176,13 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
UNSET_FLAG(ifp->status, ZEBRA_INTERFACE_VRF_LOOPBACK);
- if (!vrf_device)
+ /* Special handling for bridge or VxLAN interfaces. */
+ if (IS_ZEBRA_IF_BRIDGE (ifp))
+ zebra_l2_bridge_del (ifp);
+ else if (IS_ZEBRA_IF_VXLAN (ifp))
+ zebra_l2_vxlanif_del (ifp);
+
+ if (!IS_ZEBRA_IF_VRF(ifp))
if_delete_update (ifp);
}
diff --git a/zebra/interface.c b/zebra/interface.c
index 39125a392..9618e9bb1 100644
--- a/zebra/interface.c
+++ b/zebra/interface.c
@@ -47,6 +47,7 @@
#include "zebra/zebra_ptm.h"
#include "zebra/rt_netlink.h"
#include "zebra/interface.h"
+#include "zebra/zebra_vxlan.h"
#define ZEBRA_PTM_SUPPORT
@@ -680,6 +681,8 @@ if_delete_connected (struct interface *ifp)
void
if_delete_update (struct interface *ifp)
{
+ struct zebra_if *zif;
+
if (if_is_up(ifp))
{
zlog_err ("interface %s vrf %u index %d is still up while being deleted.",
@@ -713,6 +716,16 @@ if_delete_update (struct interface *ifp)
/* if the ifp is in a vrf, move it to default so vrf can be deleted if desired */
if (ifp->vrf_id)
if_handle_vrf_change (ifp, VRF_DEFAULT);
+
+ /* Reset some zebra interface params to default values. */
+ zif = ifp->info;
+ if (zif)
+ {
+ zif->zif_type = ZEBRA_IF_OTHER;
+ zif->zif_slave_type = ZEBRA_IF_SLAVE_NONE;
+ memset (&zif->l2info, 0, sizeof (union zebra_l2if_info));
+ memset (&zif->brslave_info, 0, sizeof (struct zebra_l2info_brslave));
+ }
}
/* VRF change for an interface */
@@ -834,6 +847,7 @@ void
if_up (struct interface *ifp)
{
struct zebra_if *zif;
+ struct interface *link_if;
zif = ifp->info;
zif->up_count++;
@@ -868,6 +882,24 @@ if_up (struct interface *ifp)
rib_update (ifp->vrf_id, RIB_UPDATE_IF_CHANGE);
zebra_vrf_static_route_interface_fixup (ifp);
+
+ /* Handle interface up for specific types for EVPN. Non-VxLAN interfaces
+ * are checked to see if (remote) neighbor entries need to be installed
+ * on them for ARP suppression.
+ */
+ if (IS_ZEBRA_IF_VXLAN (ifp))
+ zebra_vxlan_if_up (ifp);
+ else if (IS_ZEBRA_IF_BRIDGE (ifp))
+ {
+ link_if = ifp;
+ zebra_vxlan_svi_up (ifp, link_if);
+ }
+ else if (IS_ZEBRA_IF_VLAN (ifp))
+ {
+ link_if = zif->link;
+ if (link_if)
+ zebra_vxlan_svi_up (ifp, link_if);
+ }
}
/* Interface goes down. We have to manage different behavior of based
@@ -876,11 +908,31 @@ void
if_down (struct interface *ifp)
{
struct zebra_if *zif;
+ struct interface *link_if;
zif = ifp->info;
zif->down_count++;
quagga_timestamp (2, zif->down_last, sizeof (zif->down_last));
+ /* Handle interface down for specific types for EVPN. Non-VxLAN interfaces
+ * are checked to see if (remote) neighbor entries need to be purged
+ * for ARP suppression.
+ */
+ if (IS_ZEBRA_IF_VXLAN (ifp))
+ zebra_vxlan_if_down (ifp);
+ else if (IS_ZEBRA_IF_BRIDGE (ifp))
+ {
+ link_if = ifp;
+ zebra_vxlan_svi_down (ifp, link_if);
+ }
+ else if (IS_ZEBRA_IF_VLAN (ifp))
+ {
+ link_if = zif->link;
+ if (link_if)
+ zebra_vxlan_svi_down (ifp, link_if);
+ }
+
+
/* Notify to the protocol daemons. */
zebra_interface_down_update (ifp);
@@ -904,6 +956,17 @@ if_refresh (struct interface *ifp)
if_get_flags (ifp);
}
+void
+zebra_if_update_link (struct interface *ifp, ifindex_t link_ifindex)
+{
+ struct zebra_if *zif;
+
+ zif = (struct zebra_if *)ifp->info;
+ zif->link_ifindex = link_ifindex;
+ zif->link = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT),
+ link_ifindex);
+}
+
/* Output prefix string to vty. */
static int
@@ -1020,6 +1083,37 @@ nd_dump_vty (struct vty *vty, struct interface *ifp)
}
#endif /* HAVE_RTADV */
+static const char *
+zebra_ziftype_2str (zebra_iftype_t zif_type)
+{
+ switch (zif_type)
+ {
+ case ZEBRA_IF_OTHER:
+ return "Other";
+ break;
+
+ case ZEBRA_IF_BRIDGE:
+ return "Bridge";
+ break;
+
+ case ZEBRA_IF_VLAN:
+ return "Vlan";
+ break;
+
+ case ZEBRA_IF_VXLAN:
+ return "Vxlan";
+ break;
+
+ case ZEBRA_IF_VRF:
+ return "VRF";
+ break;
+
+ default:
+ return "Unknown";
+ break;
+ }
+}
+
/* Interface's information print out to vty interface. */
static void
if_dump_vty (struct vty *vty, struct interface *ifp)
@@ -1115,6 +1209,51 @@ if_dump_vty (struct vty *vty, struct interface *ifp)
connected_dump_vty (vty, connected);
}
+ vty_out(vty, " Interface Type %s\n",
+ zebra_ziftype_2str (zebra_if->zif_type));
+ if (IS_ZEBRA_IF_BRIDGE (ifp))
+ {
+ struct zebra_l2info_bridge *bridge_info;
+
+ bridge_info = &zebra_if->l2info.br;
+ vty_out(vty, " Bridge VLAN-aware: %s\n",
+ bridge_info->vlan_aware ? "yes" : "no");
+ }
+ else if (IS_ZEBRA_IF_VLAN(ifp))
+ {
+ struct zebra_l2info_vlan *vlan_info;
+
+ vlan_info = &zebra_if->l2info.vl;
+ vty_out(vty, " VLAN Id %u\n",
+ vlan_info->vid);
+ }
+ else if (IS_ZEBRA_IF_VXLAN (ifp))
+ {
+ struct zebra_l2info_vxlan *vxlan_info;
+
+ vxlan_info = &zebra_if->l2info.vxl;
+ vty_out(vty, " VxLAN Id %u", vxlan_info->vni);
+ if (vxlan_info->vtep_ip.s_addr != INADDR_ANY)
+ vty_out(vty, " VTEP IP: %s", inet_ntoa (vxlan_info->vtep_ip));
+ if (vxlan_info->access_vlan)
+ vty_out(vty, " Access VLAN Id %u", vxlan_info->access_vlan);
+ vty_out(vty, "\n");
+ }
+
+ if (IS_ZEBRA_IF_BRIDGE_SLAVE (ifp))
+ {
+ struct zebra_l2info_brslave *br_slave;
+
+ br_slave = &zebra_if->brslave_info;
+ if (br_slave->bridge_ifindex != IFINDEX_INTERNAL)
+ vty_out(vty, " Master (bridge) ifindex %u\n",
+ br_slave->bridge_ifindex);
+ }
+
+ if (zebra_if->link_ifindex != IFINDEX_INTERNAL)
+ vty_out(vty, " Link ifindex %u\n",
+ zebra_if->link_ifindex);
+
if (HAS_LINK_PARAMS(ifp))
{
int i;
diff --git a/zebra/interface.h b/zebra/interface.h
index b276edc35..f5ca00c4a 100644
--- a/zebra/interface.h
+++ b/zebra/interface.h
@@ -28,6 +28,8 @@
#include "zebra/irdp.h"
#endif
+#include "zebra/zebra_l2.h"
+
/* For interface multicast configuration. */
#define IF_ZEBRA_MULTICAST_UNSPEC 0
#define IF_ZEBRA_MULTICAST_ON 1
@@ -180,6 +182,25 @@ struct rtadvconf
#endif /* HAVE_RTADV */
+/* 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_iftype_t;
+
+/* Zebra "slave" interface type */
+typedef enum
+{
+ ZEBRA_IF_SLAVE_NONE, /* Not a slave */
+ ZEBRA_IF_SLAVE_VRF, /* Member of a VRF */
+ ZEBRA_IF_SLAVE_BRIDGE, /* Member of a bridge */
+ ZEBRA_IF_SLAVE_OTHER, /* Something else - e.g., bond slave */
+} zebra_slave_iftype_t;
+
/* `zebra' daemon local interface structure. */
struct zebra_if
{
@@ -231,8 +252,53 @@ struct zebra_if
/* ptm enable configuration */
u_char ptm_enable;
+
+ /* Zebra interface and "slave" interface type */
+ zebra_iftype_t zif_type;
+ zebra_slave_iftype_t zif_slave_type;
+
+ /* Additional L2 info, depends on zif_type */
+ union zebra_l2if_info l2info;
+
+ /* For members of a bridge, link to bridge. */
+ /* Note: If additional fields become necessary, this can be modified to
+ * be a pointer to a dynamically allocd struct.
+ */
+ struct zebra_l2info_brslave brslave_info;
+
+ /* Link fields - for sub-interfaces. */
+ ifindex_t link_ifindex;
+ struct interface *link;
};
+static inline void
+zebra_if_set_ziftype (struct interface *ifp, zebra_iftype_t zif_type,
+ zebra_slave_iftype_t zif_slave_type)
+{
+ struct zebra_if *zif;
+
+ zif = (struct zebra_if *)ifp->info;
+ zif->zif_type = zif_type;
+ zif->zif_slave_type = zif_slave_type;
+}
+
+#define IS_ZEBRA_IF_VRF(ifp) \
+ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VRF)
+
+#define IS_ZEBRA_IF_BRIDGE(ifp) \
+ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_BRIDGE)
+
+#define IS_ZEBRA_IF_VLAN(ifp) \
+ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VLAN)
+
+#define IS_ZEBRA_IF_VXLAN(ifp) \
+ (((struct zebra_if *)(ifp->info))->zif_type == ZEBRA_IF_VXLAN)
+
+#define IS_ZEBRA_IF_BRIDGE_SLAVE(ifp) \
+ (((struct zebra_if *)(ifp->info))->zif_slave_type == ZEBRA_IF_SLAVE_BRIDGE)
+
+#define IS_ZEBRA_IF_VRF_SLAVE(ifp) \
+ (((struct zebra_if *)(ifp->info))->zif_slave_type == ZEBRA_IF_SLAVE_VRF)
extern struct interface *if_lookup_by_index_per_ns (struct zebra_ns *, u_int32_t);
extern struct interface *if_lookup_by_name_per_ns (struct zebra_ns *, const char *);
@@ -253,6 +319,7 @@ extern int if_subnet_add (struct interface *, struct connected *);
extern int if_subnet_delete (struct interface *, struct connected *);
extern int ipv6_address_configured (struct interface *ifp);
extern void if_handle_vrf_change (struct interface *ifp, vrf_id_t vrf_id);
+extern void zebra_if_update_link (struct interface *ifp, ifindex_t link_ifindex);
extern void vrf_add_update (struct vrf *vrfp);
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index 3570676a4..91d4946b5 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -269,6 +269,12 @@ netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h,
case RTM_DELADDR:
return netlink_interface_addr (snl, h, ns_id, startup);
break;
+ case RTM_NEWNEIGH:
+ return netlink_neigh_change (snl, h, ns_id);
+ break;
+ case RTM_DELNEIGH:
+ return netlink_neigh_change (snl, h, ns_id);
+ break;
default:
zlog_warn ("Unknown netlink nlmsg_type %d vrf %u\n", h->nlmsg_type,
ns_id);
@@ -297,17 +303,21 @@ static void netlink_install_filter (int sock, __u32 pid)
struct sock_filter filter[] = {
/* 0: ldh [4] */
BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)),
- /* 1: jeq 0x18 jt 3 jf 6 */
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 1, 0),
- /* 2: jeq 0x19 jt 3 jf 6 */
- BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 0, 3),
- /* 3: ldw [12] */
+ /* 1: jeq 0x18 jt 5 jf next */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 3, 0),
+ /* 2: jeq 0x19 jt 5 jf next */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 2, 0),
+ /* 3: jeq 0x19 jt 5 jf next */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWNEIGH), 1, 0),
+ /* 4: jeq 0x19 jt 5 jf 8 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELNEIGH), 0, 3),
+ /* 5: ldw [12] */
BPF_STMT(BPF_LD|BPF_ABS|BPF_W, offsetof(struct nlmsghdr, nlmsg_pid)),
- /* 4: jeq XX jt 5 jf 6 */
+ /* 6: jeq XX jt 7 jf 8 */
BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htonl(pid), 0, 1),
- /* 5: ret 0 (skip) */
+ /* 7: ret 0 (skip) */
BPF_STMT(BPF_RET|BPF_K, 0),
- /* 6: ret 0xffff (keep) */
+ /* 8: ret 0xffff (keep) */
BPF_STMT(BPF_RET|BPF_K, 0xffff),
};
@@ -786,7 +796,7 @@ kernel_init (struct zebra_ns *zns)
/* Initialize netlink sockets */
groups = RTMGRP_LINK | RTMGRP_IPV4_ROUTE | RTMGRP_IPV4_IFADDR |
RTMGRP_IPV6_ROUTE | RTMGRP_IPV6_IFADDR |
- RTMGRP_IPV4_MROUTE;
+ RTMGRP_IPV4_MROUTE | RTMGRP_NEIGH;
snprintf (zns->netlink.name, sizeof (zns->netlink.name),
"netlink-listen (NS %u)", zns->ns_id);
diff --git a/zebra/rt.h b/zebra/rt.h
index a39af87b5..90654fb3e 100644
--- a/zebra/rt.h
+++ b/zebra/rt.h
@@ -24,6 +24,8 @@
#include "prefix.h"
#include "if.h"
+#include "vlan.h"
+#include "vxlan.h"
#include "zebra/rib.h"
#include "zebra/zebra_ns.h"
#include "zebra/zebra_mpls.h"
@@ -41,4 +43,18 @@ extern int kernel_del_lsp (zebra_lsp_t *);
extern int mpls_kernel_init (void);
extern int kernel_get_ipmr_sg_stats (void *mroute);
+extern int kernel_add_vtep (vni_t vni, struct interface *ifp,
+ struct in_addr *vtep_ip);
+extern int kernel_del_vtep (vni_t vni, struct interface *ifp,
+ struct in_addr *vtep_ip);
+extern int kernel_add_mac (struct interface *ifp, vlanid_t vid,
+ struct ethaddr *mac, struct in_addr vtep_ip,
+ u_char sticky);
+extern int kernel_del_mac (struct interface *ifp, vlanid_t vid,
+ struct ethaddr *mac, struct in_addr vtep_ip, int local);
+
+extern int kernel_add_neigh (struct interface *ifp, struct ipaddr *ip,
+ struct ethaddr *mac);
+extern int kernel_del_neigh (struct interface *ifp, struct ipaddr *ip);
+
#endif /* _ZEBRA_RT_H */
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index b7e452a0b..e017eb78e 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -41,6 +41,7 @@
#include "vrf.h"
#include "vty.h"
#include "mpls.h"
+#include "vxlan.h"
#include "zebra/zserv.h"
#include "zebra/zebra_ns.h"
@@ -55,6 +56,7 @@
#include "zebra/kernel_netlink.h"
#include "zebra/rt_netlink.h"
#include "zebra/zebra_mroute.h"
+#include "zebra/zebra_vxlan.h"
/* TODO - Temporary definitions, need to refine. */
@@ -93,8 +95,30 @@
#ifndef NDA_MASTER
#define NDA_MASTER 9
#endif
+
+#ifndef NTF_MASTER
+#define NTF_MASTER 0x04
+#endif
+
+#ifndef NTF_SELF
+#define NTF_SELF 0x02
+#endif
+
+#ifndef NTF_EXT_LEARNED
+#define NTF_EXT_LEARNED 0x10
+#endif
+
+#ifndef NDA_IFINDEX
+#define NDA_IFINDEX 8
+#endif
+
+#ifndef NDA_VLAN
+#define NDA_VLAN 5
+#endif
/* End of temporary definitions */
+static vlanid_t filter_vlan = 0;
+
struct gw_family_t
{
u_int16_t filler;
@@ -1555,6 +1579,712 @@ kernel_neigh_update (int add, int ifindex, uint32_t addr, char *lla, int llalen)
}
/*
+ * Add remote VTEP to the flood list for this VxLAN interface (VNI). This
+ * is done by adding an FDB entry with a MAC of 00:00:00:00:00:00.
+ */
+static int
+netlink_vxlan_flood_list_update (struct interface *ifp,
+ struct in_addr *vtep_ip,
+ int cmd)
+{
+ struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT);
+ struct
+ {
+ struct nlmsghdr n;
+ struct ndmsg ndm;
+ char buf[256];
+ } req;
+ u_char dst_mac[6] = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+
+ memset(&req.n, 0, sizeof(req.n));
+ memset(&req.ndm, 0, sizeof(req.ndm));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ if (cmd == RTM_NEWNEIGH)
+ req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_APPEND);
+ req.n.nlmsg_type = cmd;
+ req.ndm.ndm_family = PF_BRIDGE;
+ req.ndm.ndm_state = NUD_NOARP | NUD_PERMANENT;
+ req.ndm.ndm_flags |= NTF_SELF; // Handle by "self", not "master"
+
+
+ addattr_l (&req.n, sizeof (req), NDA_LLADDR, &dst_mac, 6);
+ req.ndm.ndm_ifindex = ifp->ifindex;
+ addattr_l (&req.n, sizeof (req), NDA_DST, &vtep_ip->s_addr, 4);
+
+ return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0);
+}
+
+/*
+ * Add remote VTEP for this VxLAN interface (VNI). In Linux, this involves adding
+ * a "flood" MAC FDB entry.
+ */
+int
+kernel_add_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip)
+{
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("Install %s into flood list for VNI %u intf %s(%u)",
+ inet_ntoa (*vtep_ip), vni, ifp->name, ifp->ifindex);
+
+ return netlink_vxlan_flood_list_update (ifp, vtep_ip, RTM_NEWNEIGH);
+}
+
+/*
+ * Remove remote VTEP for this VxLAN interface (VNI). In Linux, this involves
+ * deleting the "flood" MAC FDB entry.
+ */
+int
+kernel_del_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip)
+{
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("Uninstall %s from flood list for VNI %u intf %s(%u)",
+ inet_ntoa (*vtep_ip), vni, ifp->name, ifp->ifindex);
+
+ return netlink_vxlan_flood_list_update (ifp, vtep_ip, RTM_DELNEIGH);
+}
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+
+static int
+netlink_macfdb_change (struct sockaddr_nl *snl, struct nlmsghdr *h, int len)
+{
+ struct ndmsg *ndm;
+ struct interface *ifp;
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+ struct rtattr *tb[NDA_MAX + 1];
+ struct interface *br_if;
+ struct ethaddr mac;
+ vlanid_t vid = 0;
+ struct prefix vtep_ip;
+ int vid_present = 0, dst_present = 0;
+ char buf[ETHER_ADDR_STRLEN];
+ char vid_buf[20];
+ char dst_buf[30];
+ u_char sticky = 0;
+
+ ndm = NLMSG_DATA (h);
+
+ /* The interface should exist. */
+ ifp = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT), ndm->ndm_ifindex);
+ if (!ifp)
+ return 0;
+
+ /* Locate VRF corresponding to interface. We only process MAC notifications
+ * if EVPN is enabled on this VRF.
+ */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ if (!zvrf || !EVPN_ENABLED(zvrf))
+ return 0;
+ if (!ifp->info)
+ return 0;
+
+ /* The interface should be something we're interested in. */
+ if (!IS_ZEBRA_IF_BRIDGE_SLAVE(ifp))
+ return 0;
+
+ /* Drop "permanent" entries. */
+ if (ndm->ndm_state & NUD_PERMANENT)
+ return 0;
+
+ zif = (struct zebra_if *)ifp->info;
+ if ((br_if = zif->brslave_info.br_if) == NULL)
+ {
+ zlog_warn ("%s family %s IF %s(%u) brIF %u - no bridge master",
+ nl_msg_type_to_str (h->nlmsg_type),
+ nl_family_to_str (ndm->ndm_family),
+ ifp->name, ndm->ndm_ifindex,
+ zif->brslave_info.bridge_ifindex);
+ return 0;
+ }
+
+ /* Parse attributes and extract fields of interest. */
+ memset (tb, 0, sizeof tb);
+ netlink_parse_rtattr (tb, NDA_MAX, NDA_RTA (ndm), len);
+
+ if (!tb[NDA_LLADDR])
+ {
+ zlog_warn ("%s family %s IF %s(%u) brIF %u - no LLADDR",
+ nl_msg_type_to_str (h->nlmsg_type),
+ nl_family_to_str (ndm->ndm_family),
+ ifp->name, ndm->ndm_ifindex,
+ zif->brslave_info.bridge_ifindex);
+ return 0;
+ }
+
+ if (RTA_PAYLOAD (tb[NDA_LLADDR]) != ETHER_ADDR_LEN)
+ {
+ zlog_warn ("%s family %s IF %s(%u) brIF %u - LLADDR is not MAC, len %ld",
+ nl_msg_type_to_str (h->nlmsg_type),
+ nl_family_to_str (ndm->ndm_family),
+ ifp->name, ndm->ndm_ifindex,
+ zif->brslave_info.bridge_ifindex,
+ RTA_PAYLOAD (tb[NDA_LLADDR]));
+ return 0;
+ }
+
+ memcpy (&mac, RTA_DATA (tb[NDA_LLADDR]), ETHER_ADDR_LEN);
+
+ if ((NDA_VLAN <= NDA_MAX) && tb[NDA_VLAN])
+ {
+ vid_present = 1;
+ vid = *(u_int16_t *) RTA_DATA(tb[NDA_VLAN]);
+ sprintf (vid_buf, " VLAN %u", vid);
+ }
+
+ if (tb[NDA_DST])
+ {
+ /* TODO: Only IPv4 supported now. */
+ dst_present = 1;
+ vtep_ip.family = AF_INET;
+ vtep_ip.prefixlen = IPV4_MAX_BITLEN;
+ memcpy (&(vtep_ip.u.prefix4.s_addr), RTA_DATA (tb[NDA_DST]), IPV4_MAX_BYTELEN);
+ sprintf (dst_buf, " dst %s", inet_ntoa (vtep_ip.u.prefix4));
+ }
+
+ sticky = (ndm->ndm_state & NUD_NOARP) ? 1 : 0;
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("Rx %s family %s IF %s(%u)%s %sMAC %s%s",
+ nl_msg_type_to_str (h->nlmsg_type),
+ nl_family_to_str (ndm->ndm_family),
+ ifp->name, ndm->ndm_ifindex,
+ vid_present ? vid_buf : "",
+ sticky ? "sticky " : "",
+ prefix_mac2str (&mac, buf, sizeof (buf)),
+ dst_present ? dst_buf: "");
+
+ if (filter_vlan && vid != filter_vlan)
+ return 0;
+
+ /* If add or update, do accordingly if learnt on a "local" interface; if
+ * the notification is over VxLAN, this has to be related to multi-homing,
+ * so perform an implicit delete of any local entry (if it exists).
+ */
+ if (h->nlmsg_type == RTM_NEWNEIGH)
+ {
+ /* Drop "permanent" entries. */
+ if (ndm->ndm_state & NUD_PERMANENT)
+ return 0;
+
+ if (IS_ZEBRA_IF_VXLAN(ifp))
+ return zebra_vxlan_check_del_local_mac (ifp, br_if, &mac, vid);
+
+ return zebra_vxlan_local_mac_add_update (ifp, br_if, &mac, vid, sticky);
+ }
+
+ /* This is a delete notification.
+ * 1. For a MAC over VxLan, check if it needs to be refreshed(readded)
+ * 2. For a MAC over "local" interface, delete the mac
+ * Note: We will get notifications from both bridge driver and VxLAN driver.
+ * Ignore the notification from VxLan driver as it is also generated
+ * when mac moves from remote to local.
+ */
+ if (dst_present)
+ return 0;
+
+ if (IS_ZEBRA_IF_VXLAN(ifp))
+ return zebra_vxlan_check_readd_remote_mac (ifp, br_if, &mac, vid);
+
+ return zebra_vxlan_local_mac_del (ifp, br_if, &mac, vid);
+}
+
+static int
+netlink_macfdb_table (struct sockaddr_nl *snl, struct nlmsghdr *h,
+ ns_id_t ns_id, int startup)
+{
+ int len;
+ struct ndmsg *ndm;
+
+ if (h->nlmsg_type != RTM_NEWNEIGH)
+ return 0;
+
+ /* Length validity. */
+ len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ndmsg));
+ if (len < 0)
+ return -1;
+
+ /* We are interested only in AF_BRIDGE notifications. */
+ ndm = NLMSG_DATA (h);
+ if (ndm->ndm_family != AF_BRIDGE)
+ return 0;
+
+ return netlink_macfdb_change (snl, h, len);
+}
+
+/* Request for MAC FDB information from the kernel */
+static int
+netlink_request_macs (struct zebra_ns *zns, int family, int type,
+ ifindex_t master_ifindex)
+{
+ struct
+ {
+ struct nlmsghdr n;
+ struct ifinfomsg ifm;
+ char buf[256];
+ } req;
+
+ /* Form the request, specifying filter (rtattr) if needed. */
+ memset (&req, 0, sizeof (req));
+ req.n.nlmsg_type = type;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+ req.ifm.ifi_family = family;
+ if (master_ifindex)
+ addattr32 (&req.n, sizeof(req), IFLA_MASTER, master_ifindex);
+
+ return netlink_request (&zns->netlink_cmd, &req.n);
+}
+
+/*
+ * MAC forwarding database read using netlink interface. This is invoked
+ * at startup.
+ */
+int
+netlink_macfdb_read (struct zebra_ns *zns)
+{
+ int ret;
+
+ /* Get bridge FDB table. */
+ ret = netlink_request_macs (zns, AF_BRIDGE, RTM_GETNEIGH, 0);
+ if (ret < 0)
+ return ret;
+ /* We are reading entire table. */
+ filter_vlan = 0;
+ ret = netlink_parse_info (netlink_macfdb_table, &zns->netlink_cmd, zns, 0, 1);
+
+ return ret;
+}
+
+/*
+ * MAC forwarding database read using netlink interface. This is for a
+ * specific bridge and matching specific access VLAN (if VLAN-aware bridge).
+ */
+int
+netlink_macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp,
+ struct interface *br_if)
+{
+ struct zebra_if *br_zif;
+ struct zebra_if *zif;
+ struct zebra_l2info_vxlan *vxl;
+ int ret = 0;
+
+
+ /* Save VLAN we're filtering on, if needed. */
+ br_zif = (struct zebra_if *) br_if->info;
+ zif = (struct zebra_if *) ifp->info;
+ vxl = &zif->l2info.vxl;
+ if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif))
+ filter_vlan = vxl->access_vlan;
+
+ /* Get bridge FDB table for specific bridge - we do the VLAN filtering. */
+ ret = netlink_request_macs (zns, AF_BRIDGE, RTM_GETNEIGH, br_if->ifindex);
+ if (ret < 0)
+ return ret;
+ ret = netlink_parse_info (netlink_macfdb_table, &zns->netlink_cmd, zns, 0, 0);
+
+ /* Reset VLAN filter. */
+ filter_vlan = 0;
+ return ret;
+}
+
+static int
+netlink_macfdb_update (struct interface *ifp, vlanid_t vid,
+ struct ethaddr *mac,
+ struct in_addr vtep_ip,
+ int local, int cmd,
+ u_char sticky)
+{
+ struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT);
+ struct
+ {
+ struct nlmsghdr n;
+ struct ndmsg ndm;
+ char buf[256];
+ } req;
+ int dst_alen;
+ struct zebra_if *zif;
+ struct interface *br_if;
+ struct zebra_if *br_zif;
+ char buf[ETHER_ADDR_STRLEN];
+ int vid_present = 0, dst_present = 0;
+ char vid_buf[20];
+ char dst_buf[30];
+
+ zif = ifp->info;
+ if ((br_if = zif->brslave_info.br_if) == NULL)
+ {
+ zlog_warn ("MAC %s on IF %s(%u) - no mapping to bridge",
+ (cmd == RTM_NEWNEIGH) ? "add" : "del",
+ ifp->name, ifp->ifindex);
+ return -1;
+ }
+
+ memset(&req.n, 0, sizeof(req.n));
+ memset(&req.ndm, 0, sizeof(req.ndm));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ if (cmd == RTM_NEWNEIGH)
+ req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
+ req.n.nlmsg_type = cmd;
+ req.ndm.ndm_family = AF_BRIDGE;
+ req.ndm.ndm_flags |= NTF_SELF | NTF_MASTER;
+ req.ndm.ndm_state = NUD_REACHABLE;
+
+ if (sticky)
+ req.ndm.ndm_state |= NUD_NOARP;
+ else
+ req.ndm.ndm_flags |= NTF_EXT_LEARNED;
+
+ addattr_l (&req.n, sizeof (req), NDA_LLADDR, mac, 6);
+ req.ndm.ndm_ifindex = ifp->ifindex;
+ if (!local)
+ {
+ dst_alen = 4; // TODO: hardcoded
+ addattr_l (&req.n, sizeof (req), NDA_DST, &vtep_ip, dst_alen);
+ dst_present = 1;
+ sprintf (dst_buf, " dst %s", inet_ntoa (vtep_ip));
+ }
+ br_zif = (struct zebra_if *) br_if->info;
+ if (IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(br_zif) && vid > 0)
+ {
+ addattr16 (&req.n, sizeof (req), NDA_VLAN, vid);
+ vid_present = 1;
+ sprintf (vid_buf, " VLAN %u", vid);
+ }
+ addattr32 (&req.n, sizeof (req), NDA_MASTER, br_if->ifindex);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("Tx %s family %s IF %s(%u)%s %sMAC %s%s",
+ nl_msg_type_to_str (cmd),
+ nl_family_to_str (req.ndm.ndm_family),
+ ifp->name, ifp->ifindex,
+ vid_present ? vid_buf : "",
+ sticky ? "sticky " : "",
+ prefix_mac2str (mac, buf, sizeof (buf)),
+ dst_present ? dst_buf : "");
+
+ return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0);
+}
+
+#define NUD_VALID (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE | \
+ NUD_PROBE | NUD_STALE | NUD_DELAY)
+
+static int
+netlink_ipneigh_change (struct sockaddr_nl *snl, struct nlmsghdr *h, int len)
+{
+ struct ndmsg *ndm;
+ struct interface *ifp;
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+ struct rtattr *tb[NDA_MAX + 1];
+ struct interface *link_if;
+ struct ethaddr mac;
+ struct ipaddr ip;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ int mac_present = 0;
+ u_char ext_learned;
+
+ ndm = NLMSG_DATA (h);
+
+ /* The interface should exist. */
+ ifp = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT), ndm->ndm_ifindex);
+ if (!ifp)
+ return 0;
+
+ /* Locate VRF corresponding to interface. We only process neigh notifications
+ * if EVPN is enabled on this VRF.
+ */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ if (!zvrf || !EVPN_ENABLED(zvrf))
+ return 0;
+ if (!ifp->info)
+ return 0;
+
+ /* Drop "permanent" entries. */
+ if (ndm->ndm_state & NUD_PERMANENT)
+ return 0;
+
+ zif = (struct zebra_if *)ifp->info;
+ /* The neighbor is present on an SVI. From this, we locate the underlying
+ * bridge because we're only interested in neighbors on a VxLAN bridge.
+ * The bridge is located based on the nature of the SVI:
+ * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface
+ * and is linked to the bridge
+ * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge inteface
+ * itself
+ */
+ if (IS_ZEBRA_IF_VLAN(ifp))
+ {
+ link_if = zif->link;
+ if (!link_if)
+ return 0;
+ }
+ else if (IS_ZEBRA_IF_BRIDGE(ifp))
+ link_if = ifp;
+ else
+ return 0;
+
+ /* Parse attributes and extract fields of interest. */
+ memset (tb, 0, sizeof tb);
+ netlink_parse_rtattr (tb, NDA_MAX, NDA_RTA (ndm), len);
+
+ if (!tb[NDA_DST])
+ {
+ zlog_warn ("%s family %s IF %s(%u) - no DST",
+ nl_msg_type_to_str (h->nlmsg_type),
+ nl_family_to_str (ndm->ndm_family),
+ ifp->name, ndm->ndm_ifindex);
+ return 0;
+ }
+ memset (&mac, 0, sizeof (struct ethaddr));
+ memset (&ip, 0, sizeof (struct ipaddr));
+ ip.ipa_type = (ndm->ndm_family == AF_INET) ? IPADDR_V4 : IPADDR_V6;
+ memcpy (&ip.ip.addr, RTA_DATA(tb[NDA_DST]), RTA_PAYLOAD(tb[NDA_DST]));
+
+ if (h->nlmsg_type == RTM_NEWNEIGH)
+ {
+ if (tb[NDA_LLADDR])
+ {
+ if (RTA_PAYLOAD (tb[NDA_LLADDR]) != ETHER_ADDR_LEN)
+ {
+ zlog_warn ("%s family %s IF %s(%u) - LLADDR is not MAC, len %ld",
+ nl_msg_type_to_str (h->nlmsg_type),
+ nl_family_to_str (ndm->ndm_family),
+ ifp->name, ndm->ndm_ifindex,
+ RTA_PAYLOAD (tb[NDA_LLADDR]));
+ return 0;
+ }
+
+ mac_present = 1;
+ memcpy (&mac, RTA_DATA (tb[NDA_LLADDR]), ETHER_ADDR_LEN);
+ }
+
+ ext_learned = (ndm->ndm_flags & NTF_EXT_LEARNED) ? 1 : 0;
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("Rx %s family %s IF %s(%u) IP %s MAC %s state 0x%x flags 0x%x",
+ nl_msg_type_to_str (h->nlmsg_type),
+ nl_family_to_str (ndm->ndm_family),
+ ifp->name, ndm->ndm_ifindex,
+ ipaddr2str (&ip, buf2, sizeof(buf2)),
+ mac_present ? prefix_mac2str (&mac, buf, sizeof (buf)) : "",
+ ndm->ndm_state, ndm->ndm_flags);
+
+ /* If the neighbor state is valid for use, process as an add or update
+ * else process as a delete. Note that the delete handling may result
+ * in re-adding the neighbor if it is a valid "remote" neighbor.
+ */
+ if (ndm->ndm_state & NUD_VALID)
+ return zebra_vxlan_local_neigh_add_update (ifp, link_if,
+ &ip, &mac,
+ ndm->ndm_state, ext_learned);
+
+ return zebra_vxlan_local_neigh_del (ifp, link_if, &ip);
+ }
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("Rx %s family %s IF %s(%u) IP %s",
+ nl_msg_type_to_str (h->nlmsg_type),
+ nl_family_to_str (ndm->ndm_family),
+ ifp->name, ndm->ndm_ifindex,
+ ipaddr2str (&ip, buf2, sizeof(buf2)));
+
+ /* Process the delete - it may result in re-adding the neighbor if it is
+ * a valid "remote" neighbor.
+ */
+ return zebra_vxlan_local_neigh_del (ifp, link_if, &ip);
+}
+
+static int
+netlink_neigh_table (struct sockaddr_nl *snl, struct nlmsghdr *h,
+ ns_id_t ns_id, int startup)
+{
+ int len;
+ struct ndmsg *ndm;
+
+ if (h->nlmsg_type != RTM_NEWNEIGH)
+ return 0;
+
+ /* Length validity. */
+ len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ndmsg));
+ if (len < 0)
+ return -1;
+
+ /* We are interested only in AF_INET or AF_INET6 notifications. */
+ ndm = NLMSG_DATA (h);
+ if (ndm->ndm_family != AF_INET && ndm->ndm_family != AF_INET6)
+ return 0;
+
+ return netlink_neigh_change (snl, h, len);
+}
+
+/* Request for IP neighbor information from the kernel */
+static int
+netlink_request_neigh (struct zebra_ns *zns, int family, int type,
+ ifindex_t ifindex)
+{
+ struct
+ {
+ struct nlmsghdr n;
+ struct ndmsg ndm;
+ char buf[256];
+ } req;
+
+ /* Form the request, specifying filter (rtattr) if needed. */
+ memset (&req, 0, sizeof (req));
+ req.n.nlmsg_type = type;
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+ req.ndm.ndm_family = family;
+ if (ifindex)
+ addattr32 (&req.n, sizeof(req), NDA_IFINDEX, ifindex);
+
+ return netlink_request (&zns->netlink_cmd, &req.n);
+}
+
+/*
+ * IP Neighbor table read using netlink interface. This is invoked
+ * at startup.
+ */
+int
+netlink_neigh_read (struct zebra_ns *zns)
+{
+ int ret;
+
+ /* Get IP neighbor table. */
+ ret = netlink_request_neigh (zns, AF_UNSPEC, RTM_GETNEIGH, 0);
+ if (ret < 0)
+ return ret;
+ ret = netlink_parse_info (netlink_neigh_table, &zns->netlink_cmd, zns, 0, 1);
+
+ return ret;
+}
+
+/*
+ * IP Neighbor table read using netlink interface. This is for a specific
+ * VLAN device.
+ */
+int
+netlink_neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if)
+{
+ int ret = 0;
+
+ ret = netlink_request_neigh (zns, AF_UNSPEC, RTM_GETNEIGH, vlan_if->ifindex);
+ if (ret < 0)
+ return ret;
+ ret = netlink_parse_info (netlink_neigh_table, &zns->netlink_cmd, zns, 0, 0);
+
+ return ret;
+}
+
+int
+netlink_neigh_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
+ ns_id_t ns_id)
+{
+ int len;
+ struct ndmsg *ndm;
+
+ if (!(h->nlmsg_type == RTM_NEWNEIGH || h->nlmsg_type == RTM_DELNEIGH))
+ return 0;
+
+ /* Length validity. */
+ len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ndmsg));
+ if (len < 0)
+ return -1;
+
+ /* Is this a notification for the MAC FDB or IP neighbor table? */
+ ndm = NLMSG_DATA (h);
+ if (ndm->ndm_family == AF_BRIDGE)
+ return netlink_macfdb_change (snl, h, len);
+
+ if (ndm->ndm_type != RTN_UNICAST)
+ return 0;
+
+ if (ndm->ndm_family == AF_INET || ndm->ndm_family == AF_INET6)
+ return netlink_ipneigh_change (snl, h, len);
+
+ return 0;
+}
+
+static int
+netlink_neigh_update2 (struct interface *ifp, struct ipaddr *ip,
+ struct ethaddr *mac, u_int32_t flags, int cmd)
+{
+ struct {
+ struct nlmsghdr n;
+ struct ndmsg ndm;
+ char buf[256];
+ } req;
+ int ipa_len;
+
+ struct zebra_ns *zns = zebra_ns_lookup (NS_DEFAULT);
+ char buf[INET6_ADDRSTRLEN];
+ char buf2[ETHER_ADDR_STRLEN];
+
+ memset(&req.n, 0, sizeof(req.n));
+ memset(&req.ndm, 0, sizeof(req.ndm));
+
+ req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ndmsg));
+ req.n.nlmsg_flags = NLM_F_REQUEST;
+ if (cmd == RTM_NEWNEIGH)
+ req.n.nlmsg_flags |= (NLM_F_CREATE | NLM_F_REPLACE);
+ req.n.nlmsg_type = cmd; //RTM_NEWNEIGH or RTM_DELNEIGH
+ req.ndm.ndm_family = IS_IPADDR_V4(ip) ? AF_INET : AF_INET6;
+ req.ndm.ndm_state = flags;
+ req.ndm.ndm_ifindex = ifp->ifindex;
+ req.ndm.ndm_type = RTN_UNICAST;
+ req.ndm.ndm_flags = NTF_EXT_LEARNED;
+
+
+ ipa_len = IS_IPADDR_V4(ip) ? IPV4_MAX_BYTELEN : IPV6_MAX_BYTELEN;
+ addattr_l(&req.n, sizeof(req), NDA_DST, &ip->ip.addr, ipa_len);
+ if (mac)
+ addattr_l (&req.n, sizeof (req), NDA_LLADDR, mac, 6);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("Tx %s family %s IF %s(%u) Neigh %s MAC %s",
+ nl_msg_type_to_str (cmd),
+ nl_family_to_str (req.ndm.ndm_family),
+ ifp->name, ifp->ifindex,
+ ipaddr2str (ip, buf, sizeof(buf)),
+ mac ? prefix_mac2str (mac, buf2, sizeof (buf2)) : "null");
+
+ return netlink_talk (netlink_talk_filter, &req.n, &zns->netlink_cmd, zns, 0);
+}
+
+int
+kernel_add_mac (struct interface *ifp, vlanid_t vid,
+ struct ethaddr *mac, struct in_addr vtep_ip,
+ u_char sticky)
+{
+ return netlink_macfdb_update (ifp, vid, mac, vtep_ip, 0, RTM_NEWNEIGH, sticky);
+}
+
+int
+kernel_del_mac (struct interface *ifp, vlanid_t vid,
+ struct ethaddr *mac, struct in_addr vtep_ip, int local)
+{
+ return netlink_macfdb_update (ifp, vid, mac, vtep_ip, local, RTM_DELNEIGH, 0);
+}
+
+int kernel_add_neigh (struct interface *ifp, struct ipaddr *ip,
+ struct ethaddr *mac)
+{
+ return netlink_neigh_update2 (ifp, ip, mac, NUD_REACHABLE,
+ RTM_NEWNEIGH);
+}
+
+int kernel_del_neigh (struct interface *ifp, struct ipaddr *ip)
+{
+ return netlink_neigh_update2 (ifp, ip, NULL, 0, RTM_DELNEIGH);
+}
+
+/*
* MPLS label forwarding table change via netlink interface.
*/
int
diff --git a/zebra/rt_netlink.h b/zebra/rt_netlink.h
index 9ba86003b..8b061fc2e 100644
--- a/zebra/rt_netlink.h
+++ b/zebra/rt_netlink.h
@@ -51,6 +51,15 @@ extern int netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
ns_id_t ns_id, int startup);
extern int netlink_route_read (struct zebra_ns *zns);
+extern int netlink_neigh_change (struct sockaddr_nl *snl, struct nlmsghdr *h,
+ ns_id_t ns_id);
+extern int netlink_macfdb_read (struct zebra_ns *zns);
+extern int netlink_macfdb_read_for_bridge (struct zebra_ns *zns,
+ struct interface *ifp, struct interface *br_if);
+extern int netlink_neigh_read (struct zebra_ns *zns);
+extern int netlink_neigh_read_for_vlan (struct zebra_ns *zns,
+ struct interface *vlan_if);
+
#endif /* HAVE_NETLINK */
#endif /* _ZEBRA_RT_NETLINK_H */
diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c
index 3ae09945b..fd8010ca0 100644
--- a/zebra/rt_socket.c
+++ b/zebra/rt_socket.c
@@ -29,6 +29,7 @@
#include "sockunion.h"
#include "log.h"
#include "privs.h"
+#include "vxlan.h"
#include "zebra/debug.h"
#include "zebra/rib.h"
@@ -426,3 +427,41 @@ kernel_get_ipmr_sg_stats (void *mroute)
{
return 0;
}
+
+int
+kernel_add_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip)
+{
+ return 0;
+}
+
+int
+kernel_del_vtep (vni_t vni, struct interface *ifp, struct in_addr *vtep_ip)
+{
+ return 0;
+}
+
+int
+kernel_add_mac (struct interface *ifp, vlanid_t vid,
+ struct ethaddr *mac, struct in_addr vtep_ip,
+ u_char sticky)
+{
+ return 0;
+}
+
+int
+kernel_del_mac (struct interface *ifp, vlanid_t vid,
+ struct ethaddr *mac, struct in_addr vtep_ip, int local)
+{
+ return 0;
+}
+
+int kernel_add_neigh (struct interface *ifp, struct ipaddr *ip,
+ struct ethaddr *mac)
+{
+ return 0;
+}
+
+int kernel_del_neigh (struct interface *ifp, struct ipaddr *ip)
+{
+ return 0;
+}
diff --git a/zebra/rtread_getmsg.c b/zebra/rtread_getmsg.c
index 1fb2984dd..b1bef571b 100644
--- a/zebra/rtread_getmsg.c
+++ b/zebra/rtread_getmsg.c
@@ -237,3 +237,23 @@ route_read (struct zebra_ns *zns)
exit:
close (dev);
}
+
+/* Only implemented for netlink method */
+void
+macfdb_read (struct zebra_ns *zns)
+{
+}
+
+void macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp,
+ struct interface *br_if)
+{
+}
+
+void
+neigh_read (struct zebra_ns *zns)
+{
+}
+
+void neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if)
+{
+}
diff --git a/zebra/rtread_netlink.c b/zebra/rtread_netlink.c
index d59883445..d26aa59f8 100644
--- a/zebra/rtread_netlink.c
+++ b/zebra/rtread_netlink.c
@@ -29,3 +29,24 @@ void route_read (struct zebra_ns *zns)
{
netlink_route_read (zns);
}
+
+void macfdb_read (struct zebra_ns *zns)
+{
+ netlink_macfdb_read (zns);
+}
+
+void macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp,
+ struct interface *br_if)
+{
+ netlink_macfdb_read_for_bridge (zns, ifp, br_if);
+}
+
+void neigh_read (struct zebra_ns *zns)
+{
+ netlink_neigh_read (zns);
+}
+
+void neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if)
+{
+ netlink_neigh_read_for_vlan (zns, vlan_if);
+}
diff --git a/zebra/rtread_sysctl.c b/zebra/rtread_sysctl.c
index cabb1f771..30e593d87 100644
--- a/zebra/rtread_sysctl.c
+++ b/zebra/rtread_sysctl.c
@@ -83,3 +83,23 @@ route_read (struct zebra_ns *zns)
return;
}
+
+/* Only implemented for the netlink method. */
+void
+macfdb_read (struct zebra_ns *zns)
+{
+}
+
+void macfdb_read_for_bridge (struct zebra_ns *zns, struct interface *ifp,
+ struct interface *br_if)
+{
+}
+
+void
+neigh_read (struct zebra_ns *zns)
+{
+}
+
+void neigh_read_for_vlan (struct zebra_ns *zns, struct interface *vlan_if)
+{
+}
diff --git a/zebra/zebra_l2.c b/zebra/zebra_l2.c
new file mode 100644
index 000000000..b71b96a18
--- /dev/null
+++ b/zebra/zebra_l2.c
@@ -0,0 +1,251 @@
+/*
+ * Zebra Layer-2 interface handling code
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "log.h"
+#include "linklist.h"
+#include "stream.h"
+#include "hash.h"
+#include "jhash.h"
+
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/zebra_ns.h"
+#include "zebra/zserv.h"
+#include "zebra/debug.h"
+#include "zebra/interface.h"
+#include "zebra/zebra_memory.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/rt_netlink.h"
+#include "zebra/zebra_l2.h"
+#include "zebra/zebra_vxlan.h"
+
+/* definitions */
+
+/* static function declarations */
+
+/* Private functions */
+static void
+map_slaves_to_bridge (struct interface *br_if, int link)
+{
+ struct vrf *vrf;
+ struct listnode *node;
+ struct interface *ifp;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ {
+ for (ALL_LIST_ELEMENTS_RO (vrf->iflist, node, ifp))
+ {
+ struct zebra_if *zif;
+ struct zebra_l2info_brslave *br_slave;
+
+ if (ifp->ifindex == IFINDEX_INTERNAL ||
+ !ifp->info)
+ continue;
+ if (!IS_ZEBRA_IF_BRIDGE_SLAVE (ifp))
+ continue;
+
+ /* NOTE: This assumes 'zebra_l2info_brslave' is the first field
+ * for any L2 interface.
+ */
+ zif = (struct zebra_if *) ifp->info;
+ br_slave = &zif->brslave_info;
+
+ if (link)
+ {
+ if (br_slave->bridge_ifindex == br_if->ifindex)
+ br_slave->br_if = br_if;
+ }
+ else
+ {
+ if (br_slave->br_if == br_if)
+ br_slave->br_if = NULL;
+ }
+ }
+ }
+}
+
+/* Public functions */
+void
+zebra_l2_map_slave_to_bridge (struct zebra_l2info_brslave *br_slave)
+{
+ struct interface *br_if;
+
+ /* TODO: Handle change of master */
+ br_if = if_lookup_by_index_per_ns (zebra_ns_lookup (NS_DEFAULT),
+ br_slave->bridge_ifindex);
+ if (br_if)
+ br_slave->br_if = br_if;
+}
+
+void
+zebra_l2_unmap_slave_from_bridge (struct zebra_l2info_brslave *br_slave)
+{
+ br_slave->br_if = NULL;
+}
+
+/*
+ * Handle Bridge interface add or update. Update relevant info,
+ * map slaves (if any) to the bridge.
+ */
+void
+zebra_l2_bridge_add_update (struct interface *ifp,
+ struct zebra_l2info_bridge *bridge_info,
+ int add)
+{
+ struct zebra_if *zif;
+
+ zif = ifp->info;
+ assert(zif);
+
+ /* Copy over the L2 information. */
+ memcpy (&zif->l2info.br, bridge_info, sizeof (*bridge_info));
+
+ /* Link all slaves to this bridge */
+ map_slaves_to_bridge (ifp, 1);
+}
+
+/*
+ * Handle Bridge interface delete.
+ */
+void
+zebra_l2_bridge_del (struct interface *ifp)
+{
+ /* Unlink all slaves to this bridge */
+ map_slaves_to_bridge (ifp, 0);
+}
+
+/*
+ * Update L2 info for a VLAN interface. Only relevant parameter is the
+ * VLAN Id and this cannot change.
+ */
+void
+zebra_l2_vlanif_update (struct interface *ifp,
+ struct zebra_l2info_vlan *vlan_info)
+{
+ struct zebra_if *zif;
+
+ zif = ifp->info;
+ assert(zif);
+
+ /* Copy over the L2 information. */
+ memcpy (&zif->l2info.vl, vlan_info, sizeof (*vlan_info));
+}
+
+/*
+ * Update L2 info for a VxLAN interface. This is called upon interface
+ * addition as well as update. Upon add, need to invoke the VNI create
+ * function. Upon update, the params of interest are the local tunnel
+ * IP and VLAN mapping, but the latter is handled separately.
+ */
+void
+zebra_l2_vxlanif_add_update (struct interface *ifp,
+ struct zebra_l2info_vxlan *vxlan_info,
+ int add)
+{
+ struct zebra_if *zif;
+ struct in_addr old_vtep_ip;
+
+ zif = ifp->info;
+ assert(zif);
+
+ if (add)
+ {
+ memcpy (&zif->l2info.vxl, vxlan_info, sizeof (*vxlan_info));
+ zebra_vxlan_if_add (ifp);
+ return;
+ }
+
+ old_vtep_ip = zif->l2info.vxl.vtep_ip;
+ if (IPV4_ADDR_SAME(&old_vtep_ip, &vxlan_info->vtep_ip))
+ return;
+
+ zif->l2info.vxl.vtep_ip = vxlan_info->vtep_ip;
+ zebra_vxlan_if_update (ifp, ZEBRA_VXLIF_LOCAL_IP_CHANGE);
+}
+
+/*
+ * Handle change to VLAN to VNI mapping.
+ */
+void
+zebra_l2_vxlanif_update_access_vlan (struct interface *ifp,
+ vlanid_t access_vlan)
+{
+ struct zebra_if *zif;
+ vlanid_t old_access_vlan;
+
+ zif = ifp->info;
+ assert(zif);
+
+ old_access_vlan = zif->l2info.vxl.access_vlan;
+ if (old_access_vlan == access_vlan)
+ return;
+
+ zif->l2info.vxl.access_vlan = access_vlan;
+ zebra_vxlan_if_update (ifp, ZEBRA_VXLIF_VLAN_CHANGE);
+}
+
+/*
+ * Handle VxLAN interface delete.
+ */
+void
+zebra_l2_vxlanif_del (struct interface *ifp)
+{
+ zebra_vxlan_if_del (ifp);
+}
+
+/*
+ * Map or unmap interface from bridge.
+ * NOTE: It is currently assumped that an interface has to be unmapped
+ * from a bridge before it can be mapped to another bridge.
+ */
+void
+zebra_l2if_update_bridge_slave (struct interface *ifp,
+ ifindex_t bridge_ifindex)
+{
+ struct zebra_if *zif;
+ ifindex_t old_bridge_ifindex;
+
+ zif = ifp->info;
+ assert(zif);
+
+ old_bridge_ifindex = zif->brslave_info.bridge_ifindex;
+ if (old_bridge_ifindex == bridge_ifindex)
+ return;
+
+ zif->brslave_info.bridge_ifindex = bridge_ifindex;
+
+ /* Set up or remove link with master */
+ if (bridge_ifindex != IFINDEX_INTERNAL)
+ zebra_l2_map_slave_to_bridge (&zif->brslave_info);
+ else if (old_bridge_ifindex != IFINDEX_INTERNAL)
+ zebra_l2_unmap_slave_from_bridge (&zif->brslave_info);
+
+ /* In the case of VxLAN, invoke the handler for EVPN. */
+ if (zif->zif_type == ZEBRA_IF_VXLAN)
+ zebra_vxlan_if_update (ifp, ZEBRA_VXLIF_MASTER_CHANGE);
+}
diff --git a/zebra/zebra_l2.h b/zebra/zebra_l2.h
new file mode 100644
index 000000000..5cfc6dee4
--- /dev/null
+++ b/zebra/zebra_l2.h
@@ -0,0 +1,94 @@
+/*
+ * Zebra Layer-2 interface Data structures and definitions
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_L2_H
+#define _ZEBRA_L2_H
+
+#include <zebra.h>
+
+#include "if.h"
+#include "vlan.h"
+#include "vxlan.h"
+
+/* zebra L2 interface information - bridge slave (linkage to bridge) */
+struct zebra_l2info_brslave
+{
+ ifindex_t bridge_ifindex; /* Bridge Master */
+ struct interface *br_if; /* Pointer to master */
+};
+
+/* zebra L2 interface information - bridge interface */
+struct zebra_l2info_bridge
+{
+ u_char vlan_aware; /* VLAN-aware bridge? */
+};
+
+/* zebra L2 interface information - VLAN interface */
+struct zebra_l2info_vlan
+{
+ vlanid_t vid; /* VLAN id */
+};
+
+/* zebra L2 interface information - VXLAN interface */
+struct zebra_l2info_vxlan
+{
+ vni_t vni; /* VNI */
+ struct in_addr vtep_ip; /* Local tunnel IP */
+ vlanid_t access_vlan; /* Access VLAN - for VLAN-aware bridge. */
+};
+
+union zebra_l2if_info
+{
+ struct zebra_l2info_bridge br;
+ struct zebra_l2info_vlan vl;
+ struct zebra_l2info_vxlan vxl;
+};
+
+/* NOTE: These macros are to be invoked only in the "correct" context.
+ * IOW, the macro VNI_FROM_ZEBRA_IF() will assume the interface is
+ * of type ZEBRA_IF_VXLAN.
+ */
+#define VNI_FROM_ZEBRA_IF(zif) (zif)->l2info.vxl.vni
+#define VLAN_ID_FROM_ZEBRA_IF(zif) (zif)->l2info.vl.vid
+
+#define IS_ZEBRA_IF_BRIDGE_VLAN_AWARE(zif) \
+ ((zif)->l2info.br.vlan_aware == 1)
+
+
+extern void zebra_l2_map_slave_to_bridge (struct zebra_l2info_brslave *br_slave);
+extern void zebra_l2_unmap_slave_from_bridge (struct zebra_l2info_brslave *br_slave);
+extern void zebra_l2_bridge_add_update (struct interface *ifp,
+ struct zebra_l2info_bridge *bridge_info,
+ int add);
+extern void zebra_l2_bridge_del (struct interface *ifp);
+extern void zebra_l2_vlanif_update (struct interface *ifp,
+ struct zebra_l2info_vlan *vlan_info);
+extern void zebra_l2_vxlanif_add_update (struct interface *ifp,
+ struct zebra_l2info_vxlan *vxlan_info,
+ int add);
+extern void zebra_l2_vxlanif_update_access_vlan (struct interface *ifp,
+ vlanid_t access_vlan);
+extern void zebra_l2_vxlanif_del (struct interface *ifp);
+extern void zebra_l2if_update_bridge_slave (struct interface *ifp,
+ ifindex_t bridge_ifindex);
+
+#endif /* _ZEBRA_L2_H */
diff --git a/zebra/zebra_l2_null.c b/zebra/zebra_l2_null.c
new file mode 100644
index 000000000..d50107f65
--- /dev/null
+++ b/zebra/zebra_l2_null.c
@@ -0,0 +1,76 @@
+/*
+ * Zebra Layer-2 interface Data structures and definitions
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "zebra/debug.h"
+#include "zebra/zserv.h"
+#include "zebra/rib.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_l2.h"
+
+void zebra_l2_map_slave_to_bridge (struct zebra_l2info_brslave *br_slave)
+{
+}
+
+void
+zebra_l2_unmap_slave_from_bridge (struct zebra_l2info_brslave *br_slave)
+{
+}
+
+void zebra_l2_bridge_add_update (struct interface *ifp,
+ struct zebra_l2info_bridge *bridge_info,
+ int add)
+{
+}
+
+void zebra_l2_bridge_del (struct interface *ifp)
+{
+}
+
+void zebra_l2_vlanif_update (struct interface *ifp,
+ struct zebra_l2info_vlan *vlan_info)
+{
+}
+
+void zebra_l2_vxlanif_add_update (struct interface *ifp,
+ struct zebra_l2info_vxlan *vxlan_info,
+ int add)
+{
+}
+
+void
+zebra_l2_vxlanif_update_access_vlan (struct interface *ifp,
+ vlanid_t access_vlan)
+{
+}
+
+void zebra_l2_vxlanif_del (struct interface *ifp)
+{
+}
+
+void
+zebra_l2if_update_bridge_slave (struct interface *ifp,
+ ifindex_t bridge_ifindex)
+{
+}
diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c
index bedecf009..b5d6f8c06 100644
--- a/zebra/zebra_vrf.c
+++ b/zebra/zebra_vrf.c
@@ -37,6 +37,7 @@
#include "zebra/zebra_static.h"
#include "zebra/interface.h"
#include "zebra/zebra_mpls.h"
+#include "zebra/zebra_vxlan.h"
extern struct zebra_t zebrad;
@@ -244,6 +245,9 @@ zebra_vrf_delete (struct vrf *vrf)
rib_close_table (zvrf->other_table[afi][table_id]);
}
+ /* Cleanup Vxlan table and update kernel */
+ zebra_vxlan_close_tables (zvrf);
+
zebra_mpls_close_tables (zvrf);
for (ALL_LIST_ELEMENTS_RO (vrf->iflist, node, ifp))
@@ -421,6 +425,7 @@ zebra_vrf_alloc (void)
zvrf->import_check_table[afi] = table;
}
+ zebra_vxlan_init_tables (zvrf);
zebra_mpls_init_tables (zvrf);
return zvrf;
diff --git a/zebra/zebra_vrf.h b/zebra/zebra_vrf.h
index 790e2e53d..29f7df00f 100644
--- a/zebra/zebra_vrf.h
+++ b/zebra/zebra_vrf.h
@@ -95,6 +95,15 @@ struct zebra_vrf
u_int16_t mpls_flags;
#define MPLS_FLAG_SCHEDULE_LSPS (1 << 0)
+ /*
+ * VNI hash table (for EVPN). Only in default instance.
+ */
+ struct hash *vni_table;
+ /*
+ * Whether EVPN is enabled or not.
+ */
+ int advertise_all_vni;
+
/* Route Installs */
uint64_t installs;
uint64_t removals;
diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c
index 90bb21c66..b1da38a7b 100644
--- a/zebra/zebra_vty.c
+++ b/zebra/zebra_vty.c
@@ -32,6 +32,7 @@
#include "mpls.h"
#include "routemap.h"
#include "srcdest_table.h"
+#include "vxlan.h"
#include "zebra/zserv.h"
#include "zebra/zebra_vrf.h"
@@ -41,6 +42,7 @@
#include "zebra/zebra_routemap.h"
#include "zebra/zebra_static.h"
#include "lib/json.h"
+#include "zebra/zebra_vxlan.h"
extern int allow_delete;
@@ -54,6 +56,9 @@ static void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn,
#define ONE_DAY_SECOND 60*60*24
#define ONE_WEEK_SECOND 60*60*24*7
+/* VNI range as per RFC 7432 */
+#define CMD_VNI_RANGE "(1-16777215)"
+
/* General function for static route. */
int
zebra_static_ipv4 (struct vty *vty, safi_t safi, int add_cmd,
@@ -3044,6 +3049,237 @@ DEFUN (show_vrf,
return CMD_SUCCESS;
}
+DEFUN (show_evpn_vni,
+ show_evpn_vni_cmd,
+ "show evpn vni",
+ SHOW_STR
+ "EVPN\n"
+ "VxLAN information\n")
+{
+ struct zebra_vrf *zvrf;
+
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ zebra_vxlan_print_vnis(vty, zvrf);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_vni_vni,
+ show_evpn_vni_vni_cmd,
+ "show evpn vni " CMD_VNI_RANGE,
+ SHOW_STR
+ "EVPN\n"
+ "VxLAN Network Identifier\n"
+ "VNI number\n")
+{
+ struct zebra_vrf *zvrf;
+ vni_t vni;
+
+ vni = strtoul(argv[3]->arg, NULL, 10);
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ zebra_vxlan_print_vni(vty, zvrf, vni);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_mac_vni,
+ show_evpn_mac_vni_cmd,
+ "show evpn mac vni " CMD_VNI_RANGE,
+ SHOW_STR
+ "EVPN\n"
+ "MAC addresses\n"
+ "VxLAN Network Identifier\n"
+ "VNI number\n")
+{
+ struct zebra_vrf *zvrf;
+ vni_t vni;
+
+ vni = strtoul(argv[4]->arg, NULL, 10);
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ zebra_vxlan_print_macs_vni(vty, zvrf, vni);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_mac_vni_all,
+ show_evpn_mac_vni_all_cmd,
+ "show evpn mac vni all",
+ SHOW_STR
+ "EVPN\n"
+ "MAC addresses\n"
+ "VxLAN Network Identifier\n"
+ "All VNIs\n")
+{
+ struct zebra_vrf *zvrf;
+
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ zebra_vxlan_print_macs_all_vni(vty, zvrf);
+ 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_STR
+ "EVPN\n"
+ "MAC addresses\n"
+ "VxLAN Network Identifier\n"
+ "All VNIs\n"
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n")
+{
+ struct zebra_vrf *zvrf;
+ struct in_addr vtep_ip;
+
+ if (!inet_aton (argv[6]->arg, &vtep_ip))
+ {
+ 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);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (show_evpn_mac_vni_mac,
+ show_evpn_mac_vni_mac_cmd,
+ "show evpn mac vni " CMD_VNI_RANGE " mac WORD",
+ SHOW_STR
+ "EVPN\n"
+ "MAC addresses\n"
+ "VxLAN Network Identifier\n"
+ "VNI number\n"
+ "MAC\n"
+ "MAC address (e.g., 00:e0:ec:20:12:62)\n")
+{
+ struct zebra_vrf *zvrf;
+ vni_t vni;
+ struct ethaddr mac;
+
+ vni = strtoul(argv[4]->arg, NULL, 10);
+ if (!prefix_str2mac (argv[6]->arg, &mac))
+ {
+ vty_out (vty, "%% Malformed MAC address");
+ return CMD_WARNING;
+ }
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ zebra_vxlan_print_specific_mac_vni(vty, zvrf, vni, &mac);
+ return CMD_SUCCESS;
+}
+
+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_STR
+ "EVPN\n"
+ "MAC addresses\n"
+ "VxLAN Network Identifier\n"
+ "VNI number\n"
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n")
+{
+ struct zebra_vrf *zvrf;
+ vni_t vni;
+ struct in_addr vtep_ip;
+
+ vni = strtoul(argv[4]->arg, NULL, 10);
+ if (!inet_aton (argv[6]->arg, &vtep_ip))
+ {
+ 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);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_neigh_vni,
+ show_evpn_neigh_vni_cmd,
+ "show evpn arp-cache vni " CMD_VNI_RANGE,
+ SHOW_STR
+ "EVPN\n"
+ "ARP and ND cache\n"
+ "VxLAN Network Identifier\n"
+ "VNI number\n")
+{
+ struct zebra_vrf *zvrf;
+ vni_t vni;
+
+ vni = strtoul(argv[4]->arg, NULL, 10);
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ zebra_vxlan_print_neigh_vni(vty, zvrf, vni);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_evpn_neigh_vni_all,
+ show_evpn_neigh_vni_all_cmd,
+ "show evpn arp-cache vni all",
+ SHOW_STR
+ "EVPN\n"
+ "ARP and ND cache\n"
+ "VxLAN Network Identifier\n"
+ "All VNIs\n")
+{
+ struct zebra_vrf *zvrf;
+
+ zvrf = vrf_info_lookup(VRF_DEFAULT);
+ zebra_vxlan_print_neigh_all_vni(vty, zvrf);
+ 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_STR
+ "EVPN\n"
+ "ARP and ND cache\n"
+ "VxLAN Network Identifier\n"
+ "VNI number\n"
+ "Neighbor\n"
+ "Neighbor address (IPv4 or IPv6 address)\n")
+{
+ struct zebra_vrf *zvrf;
+ vni_t vni;
+ struct ipaddr ip;
+
+ vni = strtoul(argv[4]->arg, NULL, 10);
+ if (str2ipaddr (argv[6]->arg, &ip) != 0)
+ {
+ 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);
+ 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_STR
+ "EVPN\n"
+ "ARP and ND cache\n"
+ "VxLAN Network Identifier\n"
+ "VNI number\n"
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n")
+{
+ struct zebra_vrf *zvrf;
+ vni_t vni;
+ struct in_addr vtep_ip;
+
+ vni = strtoul(argv[4]->arg, NULL, 10);
+ if (!inet_aton (argv[6]->arg, &vtep_ip))
+ {
+ 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);
+ return CMD_SUCCESS;
+}
+
/* Static ip route configuration write function. */
static int
zebra_ip_config (struct vty *vty)
@@ -3234,4 +3470,16 @@ zebra_vty_init (void)
install_element (VIEW_NODE, &show_ipv6_route_vrf_all_prefix_cmd);
install_element (VIEW_NODE, &show_ipv6_mroute_vrf_all_cmd);
+
+ install_element (VIEW_NODE, &show_evpn_vni_cmd);
+ install_element (VIEW_NODE, &show_evpn_vni_vni_cmd);
+ install_element (VIEW_NODE, &show_evpn_mac_vni_cmd);
+ install_element (VIEW_NODE, &show_evpn_mac_vni_all_cmd);
+ install_element (VIEW_NODE, &show_evpn_mac_vni_all_vtep_cmd);
+ install_element (VIEW_NODE, &show_evpn_mac_vni_mac_cmd);
+ install_element (VIEW_NODE, &show_evpn_mac_vni_vtep_cmd);
+ install_element (VIEW_NODE, &show_evpn_neigh_vni_cmd);
+ install_element (VIEW_NODE, &show_evpn_neigh_vni_all_cmd);
+ install_element (VIEW_NODE, &show_evpn_neigh_vni_neigh_cmd);
+ install_element (VIEW_NODE, &show_evpn_neigh_vni_vtep_cmd);
}
diff --git a/zebra/zebra_vxlan.c b/zebra/zebra_vxlan.c
new file mode 100644
index 000000000..7df31cb93
--- /dev/null
+++ b/zebra/zebra_vxlan.c
@@ -0,0 +1,3380 @@
+/*
+ * Zebra EVPN for VxLAN code
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "prefix.h"
+#include "table.h"
+#include "memory.h"
+#include "log.h"
+#include "linklist.h"
+#include "stream.h"
+#include "hash.h"
+#include "jhash.h"
+#include "vlan.h"
+#include "vxlan.h"
+
+#include "zebra/rib.h"
+#include "zebra/rt.h"
+#include "zebra/zebra_ns.h"
+#include "zebra/zserv.h"
+#include "zebra/debug.h"
+#include "zebra/interface.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/rt_netlink.h"
+#include "zebra/zebra_vxlan_private.h"
+#include "zebra/zebra_vxlan.h"
+#include "zebra/zebra_memory.h"
+#include "zebra/zebra_l2.h"
+
+DEFINE_MTYPE_STATIC(ZEBRA, ZVNI, "VNI hash");
+DEFINE_MTYPE_STATIC(ZEBRA, ZVNI_VTEP, "VNI remote VTEP");
+DEFINE_MTYPE_STATIC(ZEBRA, MAC, "VNI MAC");
+DEFINE_MTYPE_STATIC(ZEBRA, NEIGH, "VNI Neighbor");
+
+/* definitions */
+
+
+/* static function declarations */
+static void
+zvni_print_neigh (zebra_neigh_t *n, void *ctxt);
+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);
+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 int
+zvni_macip_send_msg_to_client (struct zebra_vrf *zvrf, vni_t vni,
+ struct ethaddr *macaddr,
+ struct ipaddr *ip, u_char sticky,
+ 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 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,
+ struct in_addr *r_vtep_ip);
+static void
+zvni_neigh_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
+ int uninstall, int upd_client, u_int32_t flags);
+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);
+static int
+zvni_neigh_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
+ struct ipaddr *ip, struct ethaddr *macaddr);
+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, struct interface *br_if);
+static struct interface *
+zvni_map_to_svi (struct zebra_vrf *zvrf, vlanid_t vid,
+ struct interface *br_if);
+
+static unsigned int
+mac_hash_keymake (void *p);
+static int
+mac_cmp (const void *p1, const void *p2);
+static void *
+zvni_mac_alloc (void *p);
+static zebra_mac_t *
+zvni_mac_add (zebra_vni_t *zvni, struct ethaddr *macaddr);
+static int
+zvni_mac_del (zebra_vni_t *zvni, zebra_mac_t *mac);
+static int
+zvni_mac_del_hash_entry (struct hash_backet *backet, void *arg);
+static void
+zvni_mac_del_from_vtep (zebra_vni_t *zvni, int uninstall,
+ struct in_addr *r_vtep_ip);
+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);
+static int
+zvni_mac_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
+ struct ethaddr *macaddr, u_char sticky);
+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);
+static int
+zvni_mac_uninstall (zebra_vni_t *zvni, zebra_mac_t *mac, int local);
+static void
+zvni_install_mac_hash (struct hash_backet *backet, void *ctxt);
+
+static unsigned int
+vni_hash_keymake (void *p);
+static int
+vni_hash_cmp (const void *p1, const void *p2);
+static void *
+zvni_alloc (void *p);
+static zebra_vni_t *
+zvni_lookup (struct zebra_vrf *zvrf, vni_t vni);
+static zebra_vni_t *
+zvni_add (struct zebra_vrf *zvrf, vni_t vni);
+static int
+zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni);
+static int
+zvni_send_add_to_client (struct zebra_vrf *zvrf, zebra_vni_t *zvni);
+static int
+zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni);
+static void
+zvni_build_hash_table (struct zebra_vrf *zvrf);
+static int
+zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep);
+static zebra_vtep_t *
+zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip);
+static zebra_vtep_t *
+zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip);
+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);
+
+
+/* Private functions */
+
+/*
+ * Helper function to determine maximum width of neighbor IP address for
+ * display - just because we're dealing with IPv6 addresses that can
+ * widely vary.
+ */
+static void
+zvni_find_neigh_addr_width (struct hash_backet *backet, void *ctxt)
+{
+ zebra_neigh_t *n;
+ char buf[INET6_ADDRSTRLEN];
+ struct neigh_walk_ctx *wctx = ctxt;
+ int width;
+
+ n = (zebra_neigh_t *) backet->data;
+ if (!n)
+ return;
+
+ ipaddr2str (&n->ip, buf, sizeof(buf)),
+ width = strlen (buf);
+ if (width > wctx->addr_width)
+ wctx->addr_width = width;
+}
+
+/*
+ * Print a specific neighbor entry.
+ */
+static void
+zvni_print_neigh (zebra_neigh_t *n, void *ctxt)
+{
+ 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");
+}
+
+/*
+ * Print neighbor hash entry - called for display of all neighbors.
+ */
+static void
+zvni_print_neigh_hash (struct hash_backet *backet, void *ctxt)
+{
+ struct vty *vty;
+ zebra_neigh_t *n;
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ struct neigh_walk_ctx *wctx = ctxt;
+
+ vty = wctx->vty;
+ n = (zebra_neigh_t *) backet->data;
+ if (!n)
+ return;
+
+ 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);
+ 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)
+ 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, buf2, "remote", buf1,
+ inet_ntoa (n->r_vtep_ip));
+ wctx->count++;
+ }
+ }
+ else
+ {
+ vty_out(vty, "%*s %-6s %-17s %-21s\n",
+ -wctx->addr_width, buf2, "remote", buf1,
+ inet_ntoa (n->r_vtep_ip));
+ wctx->count++;
+ }
+ }
+}
+
+/*
+ * Print neighbors for all VNI.
+ */
+static void
+zvni_print_neigh_hash_all_vni (struct hash_backet *backet, void *ctxt)
+{
+ struct vty *vty;
+ zebra_vni_t *zvni;
+ u_int32_t num_neigh;
+ struct neigh_walk_ctx wctx;
+
+ vty = (struct vty *) ctxt;
+ zvni = (zebra_vni_t *) backet->data;
+ if (!zvni)
+ 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)
+ 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
+ * the maximum width.
+ */
+ memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.vty = vty;
+ wctx.addr_width = 15;
+ 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");
+ hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+}
+
+/*
+ * Print a specific MAC entry.
+ */
+static void
+zvni_print_mac (zebra_mac_t *mac, void *ctxt)
+{
+ struct vty *vty;
+ char buf1[20];
+
+ vty = (struct vty *) ctxt;
+ vty_out(vty, "MAC: %s",
+ prefix_mac2str (&mac->macaddr, buf1, sizeof (buf1)));
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL))
+ {
+ struct zebra_ns *zns;
+ struct interface *ifp;
+ ifindex_t ifindex;
+
+ ifindex = mac->fwd_info.local.ifindex;
+ zns = zebra_ns_lookup (NS_DEFAULT);
+ ifp = if_lookup_by_index_per_ns (zns, ifindex);
+ if (!ifp) // unexpected
+ return;
+ 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
+ {
+ vty_out(vty, " Remote VTEP: %s",
+ inet_ntoa (mac->fwd_info.r_vtep_ip));
+ }
+ vty_out(vty, " ARP ref: %u", mac->neigh_refcnt);
+ vty_out(vty, "\n");
+}
+
+/*
+ * Print MAC hash entry - called for display of all MACs.
+ */
+static void
+zvni_print_mac_hash (struct hash_backet *backet, void *ctxt)
+{
+ struct vty *vty;
+ zebra_mac_t *mac;
+ char buf1[20];
+ struct mac_walk_ctx *wctx = ctxt;
+
+ vty = wctx->vty;
+ mac = (zebra_mac_t *) backet->data;
+ if (!mac)
+ return;
+
+ prefix_mac2str (&mac->macaddr, buf1, sizeof (buf1));
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_LOCAL) &&
+ !(wctx->flags & SHOW_REMOTE_MAC_FROM_VTEP))
+ {
+ struct zebra_ns *zns;
+ ifindex_t ifindex;
+ struct interface *ifp;
+ vlanid_t vid;
+
+ zns = zebra_ns_lookup (NS_DEFAULT);
+ ifindex = mac->fwd_info.local.ifindex;
+ ifp = if_lookup_by_index_per_ns (zns, ifindex);
+ 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");
+ wctx->count++;
+ }
+ else
+ {
+ 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");
+ }
+ 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));
+ wctx->count++;
+ }
+ }
+
+}
+
+/*
+ * Print MACs for all VNI.
+ */
+static void
+zvni_print_mac_hash_all_vni (struct hash_backet *backet, void *ctxt)
+{
+ struct vty *vty;
+ zebra_vni_t *zvni;
+ u_int32_t num_macs;
+ struct mac_walk_ctx *wctx = ctxt;
+
+ vty = (struct vty *) wctx->vty;
+
+ zvni = (zebra_vni_t *) backet->data;
+ if (!zvni)
+ return;
+ wctx->zvni = zvni;
+
+ /*We are iterating over a new VNI, set the count to 0*/
+ wctx->count = 0;
+
+ 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");
+ }
+
+ hash_iterate(zvni->mac_table, zvni_print_mac_hash, wctx);
+}
+
+/*
+ * Print a specific VNI entry.
+ */
+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;
+
+ vty = (struct vty *) ctxt;
+
+ vty_out(vty, "VNI: %u\n", zvni->vni);
+ if (!zvni->vxlan_if)
+ { // unexpected
+ 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));
+
+ if (!zvni->vteps)
+ {
+ 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));
+ }
+ 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)
+{
+ struct vty *vty;
+ zebra_vni_t *zvni;
+ zebra_vtep_t *zvtep;
+ u_int32_t num_vteps = 0;
+ u_int32_t num_macs = 0;
+ u_int32_t num_neigh = 0;
+
+ vty = (struct vty *) ctxt;
+ zvni = (zebra_vni_t *) backet->data;
+ if (!zvni)
+ return;
+
+ zvtep = zvni->vteps;
+ while (zvtep)
+ {
+ num_vteps++;
+ zvtep = zvtep->next;
+ }
+
+ 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);
+}
+
+/*
+ * Inform BGP about local MACIP.
+ */
+static int
+zvni_macip_send_msg_to_client (struct zebra_vrf *zvrf, vni_t vni,
+ struct ethaddr *macaddr,
+ struct ipaddr *ip, u_char sticky,
+ u_int16_t cmd)
+{
+ struct zserv *client;
+ struct stream *s;
+ int ipa_len;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ client = zebra_find_client (ZEBRA_ROUTE_BGP);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = client->obuf;
+ stream_reset (s);
+
+ zserv_create_header (s, cmd, zvrf_id (zvrf));
+ stream_putl (s, vni);
+ stream_put (s, macaddr->octet, ETHER_ADDR_LEN);
+ if (ip)
+ {
+ ipa_len = 0;
+ if (IS_IPADDR_V4(ip))
+ ipa_len = IPV4_MAX_BYTELEN;
+ else if (IS_IPADDR_V6(ip))
+ ipa_len = IPV6_MAX_BYTELEN;
+
+ stream_putl (s, ipa_len); /* IP address length */
+ if (ipa_len)
+ stream_put (s, &ip->ip.addr, ipa_len); /* IP address */
+ }
+ else
+ stream_putl (s, 0); /* Just MAC. */
+
+ stream_putc (s, sticky); /* Sticky 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));
+
+ if (cmd == ZEBRA_MACIP_ADD)
+ client->macipadd_cnt++;
+ else
+ client->macipdel_cnt++;
+
+ return zebra_server_send_message(client);
+}
+
+/*
+ * Make hash key for neighbors.
+ */
+static unsigned int
+neigh_hash_keymake (void *p)
+{
+ zebra_neigh_t *n = p;
+ struct ipaddr *ip = &n->ip;
+
+ if (IS_IPADDR_V4(ip))
+ return jhash_1word (ip->ipaddr_v4.s_addr, 0);
+
+ return jhash2 (ip->ipaddr_v6.s6_addr32,
+ ZEBRA_NUM_OF(ip->ipaddr_v6.s6_addr32), 0);
+}
+
+/*
+ * Compare two neighbor hash structures.
+ */
+static int
+neigh_cmp (const void *p1, const void *p2)
+{
+ const zebra_neigh_t *n1 = p1;
+ const zebra_neigh_t *n2 = p2;
+
+ if (n1 == NULL && n2 == NULL)
+ return 1;
+
+ if (n1 == NULL || n2 == NULL)
+ return 0;
+
+ return (memcmp(&n1->ip, &n2->ip, sizeof (struct ipaddr)) == 0);
+}
+
+/*
+ * Callback to allocate neighbor hash entry.
+ */
+static void *
+zvni_neigh_alloc (void *p)
+{
+ const zebra_neigh_t *tmp_n = p;
+ zebra_neigh_t *n;
+
+ n = XCALLOC (MTYPE_NEIGH, sizeof(zebra_neigh_t));
+ *n = *tmp_n;
+
+ return ((void *)n);
+}
+
+/*
+ * Add neighbor entry.
+ */
+static zebra_neigh_t *
+zvni_neigh_add (zebra_vni_t *zvni, struct ipaddr *ip)
+{
+ zebra_neigh_t tmp_n;
+ zebra_neigh_t *n = 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);
+
+ return n;
+}
+
+/*
+ * Delete neighbor entry.
+ */
+static int
+zvni_neigh_del (zebra_vni_t *zvni, zebra_neigh_t *n)
+{
+ zebra_neigh_t *tmp_n;
+
+ /* Free the VNI hash entry and allocated memory. */
+ tmp_n = hash_release (zvni->neigh_table, n);
+ if (tmp_n)
+ XFREE(MTYPE_NEIGH, tmp_n);
+
+ return 0;
+}
+
+/*
+ * Free neighbor hash entry (callback)
+ */
+static int
+zvni_neigh_del_hash_entry (struct hash_backet *backet, void *arg)
+{
+ struct neigh_walk_ctx *wctx = arg;
+ zebra_neigh_t *n = backet->data;
+
+ if (((wctx->flags & DEL_LOCAL_NEIGH) && (n->flags & ZEBRA_NEIGH_LOCAL)) ||
+ ((wctx->flags & DEL_REMOTE_NEIGH) && (n->flags & ZEBRA_NEIGH_REMOTE)) ||
+ ((wctx->flags & DEL_REMOTE_NEIGH_FROM_VTEP) &&
+ (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);
+
+ if (wctx->uninstall)
+ zvni_neigh_uninstall (wctx->zvni, n);
+
+ return zvni_neigh_del (wctx->zvni, n);
+ }
+
+ return 0;
+}
+
+/*
+ * Delete all neighbor entries from specific VTEP for a particular VNI.
+ */
+static void
+zvni_neigh_del_from_vtep (zebra_vni_t *zvni, int uninstall,
+ struct in_addr *r_vtep_ip)
+{
+ struct neigh_walk_ctx wctx;
+
+ if (!zvni->neigh_table)
+ return;
+
+ memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.uninstall = uninstall;
+ wctx.flags = DEL_REMOTE_NEIGH_FROM_VTEP;
+ wctx.r_vtep_ip = *r_vtep_ip;
+
+ hash_iterate (zvni->neigh_table,
+ (void (*) (struct hash_backet *, void *))
+ zvni_neigh_del_hash_entry, &wctx);
+}
+
+/*
+ * Delete all neighbor entries for this VNI.
+ */
+static void
+zvni_neigh_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
+ int uninstall, int upd_client, u_int32_t flags)
+{
+ struct neigh_walk_ctx wctx;
+
+ if (!zvni->neigh_table)
+ return;
+
+ memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.zvrf = zvrf;
+ wctx.uninstall = uninstall;
+ wctx.upd_client = upd_client;
+ wctx.flags = flags;
+
+ hash_iterate (zvni->neigh_table,
+ (void (*) (struct hash_backet *, void *))
+ zvni_neigh_del_hash_entry, &wctx);
+}
+
+/*
+ * Look up neighbor hash entry.
+ */
+static zebra_neigh_t *
+zvni_neigh_lookup (zebra_vni_t *zvni, struct ipaddr *ip)
+{
+ zebra_neigh_t tmp;
+ zebra_neigh_t *n;
+
+ memset (&tmp, 0, sizeof(tmp));
+ memcpy (&tmp.ip, ip, sizeof (struct ipaddr));
+ n = hash_lookup (zvni->neigh_table, &tmp);
+
+ return n;
+}
+
+/*
+ * 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)
+{
+ return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, ip, 0,
+ ZEBRA_MACIP_ADD);
+}
+
+/*
+ * Inform BGP about local neighbor deletion.
+ */
+static int
+zvni_neigh_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni,
+ struct ipaddr *ip, struct ethaddr *macaddr)
+{
+ return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, ip, 0,
+ ZEBRA_MACIP_DEL);
+}
+
+/*
+ * Install remote neighbor into the kernel.
+ */
+static int
+zvni_neigh_install (zebra_vni_t *zvni, zebra_neigh_t *n)
+{
+ struct zebra_vrf *zvrf;
+ struct zebra_if *zif;
+ struct zebra_l2info_vxlan *vxl;
+ struct interface *vlan_if;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE))
+ return 0;
+
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ assert(zvrf);
+ zif = zvni->vxlan_if->info;
+ if (!zif)
+ return -1;
+ vxl = &zif->l2info.vxl;
+
+ vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (!vlan_if)
+ return -1;
+
+ return kernel_add_neigh (vlan_if, &n->ip, &n->emac);
+}
+
+/*
+ * Uninstall remote neighbor from the kernel.
+ */
+static int
+zvni_neigh_uninstall (zebra_vni_t *zvni, zebra_neigh_t *n)
+{
+ struct zebra_vrf *zvrf;
+ struct zebra_if *zif;
+ struct zebra_l2info_vxlan *vxl;
+ struct interface *vlan_if;
+
+ if (!(n->flags & ZEBRA_NEIGH_REMOTE))
+ return 0;
+
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ assert(zvrf);
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ zif = zvni->vxlan_if->info;
+ if (!zif)
+ return -1;
+ vxl = &zif->l2info.vxl;
+ vlan_if = zvni_map_to_svi (zvrf, vxl->access_vlan,
+ zif->brslave_info.br_if);
+ if (!vlan_if)
+ return -1;
+
+ return kernel_del_neigh (vlan_if, &n->ip);
+}
+
+/*
+ * Install neighbor hash entry - called upon access VLAN change.
+ */
+static void
+zvni_install_neigh_hash (struct hash_backet *backet, void *ctxt)
+{
+ zebra_neigh_t *n;
+ struct neigh_walk_ctx *wctx = ctxt;
+
+ n = (zebra_neigh_t *) backet->data;
+ if (!n)
+ return;
+
+ if (CHECK_FLAG(n->flags, ZEBRA_NEIGH_REMOTE))
+ zvni_neigh_install (wctx->zvni, n);
+}
+
+/*
+ * Make hash key for MAC.
+ */
+static unsigned int
+mac_hash_keymake (void *p)
+{
+ zebra_mac_t *pmac = p;
+ char *pnt = (char *) pmac->macaddr.octet;
+ unsigned int key = 0;
+ int c = 0;
+
+ key += pnt[c];
+ key += pnt[c + 1];
+ key += pnt[c + 2];
+ key += pnt[c + 3];
+ key += pnt[c + 4];
+ key += pnt[c + 5];
+
+ return (key);
+}
+
+/*
+ * Compare two MAC addresses.
+ */
+static int
+mac_cmp (const void *p1, const void *p2)
+{
+ const zebra_mac_t *pmac1 = p1;
+ const zebra_mac_t *pmac2 = p2;
+
+ if (pmac1 == NULL && pmac2 == NULL)
+ return 1;
+
+ if (pmac1 == NULL || pmac2 == NULL)
+ return 0;
+
+ return(memcmp(pmac1->macaddr.octet, pmac2->macaddr.octet, ETHER_ADDR_LEN) == 0);
+}
+
+/*
+ * Callback to allocate MAC hash entry.
+ */
+static void *
+zvni_mac_alloc (void *p)
+{
+ const zebra_mac_t *tmp_mac = p;
+ zebra_mac_t *mac;
+
+ mac = XCALLOC (MTYPE_MAC, sizeof(zebra_mac_t));
+ *mac = *tmp_mac;
+
+ return ((void *)mac);
+}
+
+/*
+ * Add MAC entry.
+ */
+static zebra_mac_t *
+zvni_mac_add (zebra_vni_t *zvni, struct ethaddr *macaddr)
+{
+ zebra_mac_t tmp_mac;
+ zebra_mac_t *mac = NULL;
+
+ memset (&tmp_mac, 0, sizeof (zebra_mac_t));
+ memcpy(&tmp_mac.macaddr, macaddr, ETHER_ADDR_LEN);
+ mac = hash_get (zvni->mac_table, &tmp_mac, zvni_mac_alloc);
+ assert (mac);
+
+ return mac;
+}
+
+/*
+ * Delete MAC entry.
+ */
+static int
+zvni_mac_del (zebra_vni_t *zvni, zebra_mac_t *mac)
+{
+ zebra_mac_t *tmp_mac;
+
+ /* Free the VNI hash entry and allocated memory. */
+ tmp_mac = hash_release (zvni->mac_table, mac);
+ if (tmp_mac)
+ XFREE(MTYPE_MAC, tmp_mac);
+
+ return 0;
+}
+
+/*
+ * Free MAC hash entry (callback)
+ */
+static int
+zvni_mac_del_hash_entry (struct hash_backet *backet, void *arg)
+{
+ struct mac_walk_ctx *wctx = arg;
+ zebra_mac_t *mac = backet->data;
+ u_char sticky = 0;
+
+ if (((wctx->flags & DEL_LOCAL_MAC) && (mac->flags & ZEBRA_MAC_LOCAL)) ||
+ ((wctx->flags & DEL_REMOTE_MAC) && (mac->flags & ZEBRA_MAC_REMOTE)) ||
+ ((wctx->flags & DEL_REMOTE_MAC_FROM_VTEP) &&
+ (mac->flags & ZEBRA_MAC_REMOTE) &&
+ IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &wctx->r_vtep_ip)
+ ))
+ {
+ 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);
+ }
+
+ if (wctx->uninstall)
+ zvni_mac_uninstall (wctx->zvni, mac, 0);
+
+ return zvni_mac_del (wctx->zvni, mac);
+ }
+
+ return 0;
+}
+
+/*
+ * Delete all MAC entries from specific VTEP for a particular VNI.
+ */
+static void
+zvni_mac_del_from_vtep (zebra_vni_t *zvni, int uninstall,
+ struct in_addr *r_vtep_ip)
+{
+ struct mac_walk_ctx wctx;
+
+ if (!zvni->mac_table)
+ return;
+
+ memset (&wctx, 0, sizeof (struct mac_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.uninstall = uninstall;
+ wctx.flags = DEL_REMOTE_MAC_FROM_VTEP;
+ wctx.r_vtep_ip = *r_vtep_ip;
+
+ hash_iterate (zvni->mac_table,
+ (void (*) (struct hash_backet *, void *))
+ zvni_mac_del_hash_entry, &wctx);
+}
+
+/*
+ * Delete all MAC entries for this VNI.
+ */
+static void
+zvni_mac_del_all (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
+ int uninstall, int upd_client, u_int32_t flags)
+{
+ struct mac_walk_ctx wctx;
+
+ if (!zvni->mac_table)
+ return;
+
+ memset (&wctx, 0, sizeof (struct mac_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.zvrf = zvrf;
+ wctx.uninstall = uninstall;
+ wctx.upd_client = upd_client;
+ wctx.flags = flags;
+
+ hash_iterate (zvni->mac_table,
+ (void (*) (struct hash_backet *, void *))
+ zvni_mac_del_hash_entry, &wctx);
+}
+
+/*
+ * Look up MAC hash entry.
+ */
+static zebra_mac_t *
+zvni_mac_lookup (zebra_vni_t *zvni, struct ethaddr *mac)
+{
+ zebra_mac_t tmp;
+ zebra_mac_t *pmac;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.macaddr, mac, ETHER_ADDR_LEN);
+ pmac = hash_lookup (zvni->mac_table, &tmp);
+
+ return pmac;
+}
+
+/*
+ * 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)
+{
+ return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, NULL,
+ sticky, ZEBRA_MACIP_ADD);
+}
+
+/*
+ * 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)
+{
+ return zvni_macip_send_msg_to_client (zvrf, vni, macaddr, NULL,
+ sticky, ZEBRA_MACIP_DEL);
+}
+
+/*
+ * Map port or (port, VLAN) to a VNI. This is invoked upon getting MAC
+ * notifications, to see if there are of interest.
+ * TODO: Need to make this as a hash table.
+ */
+static zebra_vni_t *
+zvni_map_vlan (struct interface *ifp, struct interface *br_if, vlanid_t vid)
+{
+ struct zebra_vrf *zvrf;
+ struct listnode *node;
+ struct interface *tmp_if;
+ struct zebra_if *zif;
+ struct zebra_l2info_bridge *br;
+ struct zebra_l2info_vxlan *vxl;
+ u_char bridge_vlan_aware;
+ zebra_vni_t *zvni;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ /* Determine if bridge is VLAN-aware or not */
+ zif = br_if->info;
+ assert (zif);
+ br = &zif->l2info.br;
+ bridge_vlan_aware = br->vlan_aware;
+
+ /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
+ /* TODO: Optimize with a hash. */
+ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if))
+ {
+ zif = tmp_if->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+ if (!if_is_operative (tmp_if))
+ continue;
+ vxl = &zif->l2info.vxl;
+
+ if (zif->brslave_info.br_if != br_if)
+ continue;
+
+ if (!bridge_vlan_aware)
+ break;
+
+ if (vxl->access_vlan == vid)
+ break;
+ }
+
+ if (!tmp_if)
+ return NULL;
+
+ zvni = zvni_lookup (zvrf, vxl->vni);
+ return zvni;
+}
+
+/*
+ * Map SVI and associated bridge to a VNI. This is invoked upon getting
+ * neighbor notifications, to see if they are of interest.
+ * TODO: Need to make this as a hash table.
+ */
+static zebra_vni_t *
+zvni_map_svi (struct interface *ifp, struct interface *br_if)
+{
+ struct zebra_vrf *zvrf;
+ struct listnode *node;
+ struct interface *tmp_if;
+ struct zebra_if *zif;
+ struct zebra_l2info_bridge *br;
+ struct zebra_l2info_vxlan *vxl;
+ u_char bridge_vlan_aware;
+ vlanid_t vid = 0;
+ zebra_vni_t *zvni;
+
+ /* Make sure the linked interface is a bridge. */
+ if (!IS_ZEBRA_IF_BRIDGE (br_if))
+ return NULL;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ /* Determine if bridge is VLAN-aware or not */
+ zif = br_if->info;
+ assert (zif);
+ br = &zif->l2info.br;
+ bridge_vlan_aware = br->vlan_aware;
+ if (bridge_vlan_aware)
+ {
+ struct zebra_l2info_vlan *vl;
+
+ if (!IS_ZEBRA_IF_VLAN(ifp))
+ return NULL;
+
+ zif = ifp->info;
+ assert (zif);
+ vl = &zif->l2info.vl;
+ vid = vl->vid;
+ }
+
+ /* See if this interface (or interface plus VLAN Id) maps to a VxLAN */
+ /* TODO: Optimize with a hash. */
+ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if))
+ {
+ zif = tmp_if->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+ if (!if_is_operative (tmp_if))
+ continue;
+ vxl = &zif->l2info.vxl;
+
+ if (zif->brslave_info.br_if != br_if)
+ continue;
+
+ if (!bridge_vlan_aware)
+ break;
+
+ if (vxl->access_vlan == vid)
+ break;
+ }
+
+ if (!tmp_if)
+ return NULL;
+
+ zvni = zvni_lookup (zvrf, vxl->vni);
+ return zvni;
+}
+
+/* Map to SVI on bridge corresponding to specified VLAN. This can be one
+ * of two cases:
+ * (a) In the case of a VLAN-aware bridge, the SVI is a L3 VLAN interface
+ * linked to the bridge
+ * (b) In the case of a VLAN-unaware bridge, the SVI is the bridge inteface
+ * itself
+ */
+static struct interface *
+zvni_map_to_svi (struct zebra_vrf *zvrf, vlanid_t vid,
+ struct interface *br_if)
+{
+ struct listnode *node;
+ struct interface *tmp_if;
+ struct zebra_if *zif;
+ struct zebra_l2info_bridge *br;
+ struct zebra_l2info_vlan *vl;
+ u_char bridge_vlan_aware;
+
+ /* Determine if bridge is VLAN-aware or not */
+ zif = br_if->info;
+ assert (zif);
+ br = &zif->l2info.br;
+ bridge_vlan_aware = br->vlan_aware;
+
+ /* Check oper status of the SVI. */
+ if (!bridge_vlan_aware)
+ return if_is_operative (br_if) ? br_if : NULL;
+
+ /* Identify corresponding VLAN interface. */
+ /* TODO: Optimize with a hash. */
+ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, tmp_if))
+ {
+ /* Check oper status of the SVI. */
+ if (!if_is_operative (tmp_if))
+ continue;
+ zif = tmp_if->info;
+ if (!zif ||
+ zif->zif_type != ZEBRA_IF_VLAN ||
+ zif->link != br_if)
+ continue;
+ vl = (struct zebra_l2info_vlan *)&zif->l2info.vl;
+
+ if (vl->vid == vid)
+ break;
+ }
+
+ return tmp_if;
+}
+
+/*
+ * Install remote MAC into the kernel.
+ */
+static int
+zvni_mac_install (zebra_vni_t *zvni, zebra_mac_t *mac)
+{
+ struct zebra_if *zif;
+ struct zebra_l2info_vxlan *vxl;
+ u_char sticky;
+
+ if (!(mac->flags & ZEBRA_MAC_REMOTE))
+ return 0;
+
+ zif = zvni->vxlan_if->info;
+ if (!zif)
+ return -1;
+ vxl = &zif->l2info.vxl;
+
+ sticky = CHECK_FLAG (mac->flags, ZEBRA_MAC_STICKY) ? 1: 0;
+
+ return kernel_add_mac (zvni->vxlan_if, vxl->access_vlan,
+ &mac->macaddr, mac->fwd_info.r_vtep_ip, sticky);
+}
+
+/*
+ * Uninstall remote MAC from the kernel. In the scenario where the MAC
+ * moves to remote, we have to uninstall any existing local entry first.
+ */
+static int
+zvni_mac_uninstall (zebra_vni_t *zvni, zebra_mac_t *mac, int local)
+{
+ struct zebra_if *zif;
+ struct zebra_l2info_vxlan *vxl;
+ struct in_addr vtep_ip = { .s_addr = 0 };
+ struct zebra_ns *zns;
+ struct interface *ifp;
+
+ if (!local && !(mac->flags & ZEBRA_MAC_REMOTE))
+ return 0;
+
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ zif = zvni->vxlan_if->info;
+ if (!zif)
+ return -1;
+ vxl = &zif->l2info.vxl;
+
+ if (local)
+ {
+ zns = zebra_ns_lookup (NS_DEFAULT);
+ ifp = if_lookup_by_index_per_ns (zns, mac->fwd_info.local.ifindex);
+ if (!ifp) // unexpected
+ return -1;
+ }
+ else
+ {
+ ifp = zvni->vxlan_if;
+ vtep_ip = mac->fwd_info.r_vtep_ip;
+ }
+
+ return kernel_del_mac (ifp, vxl->access_vlan,
+ &mac->macaddr, vtep_ip, local);
+}
+
+/*
+ * Install MAC hash entry - called upon access VLAN change.
+ */
+static void
+zvni_install_mac_hash (struct hash_backet *backet, void *ctxt)
+{
+ zebra_mac_t *mac;
+ struct mac_walk_ctx *wctx = ctxt;
+
+ mac = (zebra_mac_t *) backet->data;
+ if (!mac)
+ return;
+
+ if (CHECK_FLAG(mac->flags, ZEBRA_MAC_REMOTE))
+ zvni_mac_install (wctx->zvni, mac);
+}
+
+/*
+ * Decrement neighbor refcount of MAC; uninstall and free it if
+ * appropriate.
+ */
+static void
+zvni_deref_ip2mac (zebra_vni_t *zvni, zebra_mac_t *mac, int uninstall)
+{
+ if (mac->neigh_refcnt)
+ mac->neigh_refcnt--;
+
+ if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_AUTO) ||
+ mac->neigh_refcnt > 0)
+ return;
+
+ if (uninstall)
+ zvni_mac_uninstall (zvni, mac, 0);
+
+ zvni_mac_del (zvni, mac);
+}
+
+/*
+ * Read and populate local MACs and neighbors corresponding to this VNI.
+ */
+static void
+zvni_read_mac_neigh (struct zebra_vrf *zvrf, zebra_vni_t *zvni,
+ struct interface *ifp)
+{
+ struct zebra_if *zif;
+ struct interface *vlan_if;
+ struct zebra_l2info_vxlan *vxl;
+
+ zif = ifp->info;
+ vxl = &zif->l2info.vxl;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Reading MAC FDB and Neighbors for intf %s(%u) VNI %u master %u",
+ ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni,
+ zif->brslave_info.bridge_ifindex);
+
+ 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)
+ neigh_read_for_vlan (zvrf->zns, vlan_if);
+}
+
+/*
+ * Hash function for VNI.
+ */
+static unsigned int
+vni_hash_keymake (void *p)
+{
+ const zebra_vni_t *zvni = p;
+
+ return (jhash_1word(zvni->vni, 0));
+}
+
+/*
+ * Compare 2 VNI hash entries.
+ */
+static int
+vni_hash_cmp (const void *p1, const void *p2)
+{
+ const zebra_vni_t *zvni1 = p1;
+ const zebra_vni_t *zvni2 = p2;
+
+ return (zvni1->vni == zvni2->vni);
+}
+
+/*
+ * Callback to allocate VNI hash entry.
+ */
+static void *
+zvni_alloc (void *p)
+{
+ const zebra_vni_t *tmp_vni = p;
+ zebra_vni_t *zvni;
+
+ zvni = XCALLOC (MTYPE_ZVNI, sizeof(zebra_vni_t));
+ zvni->vni = tmp_vni->vni;
+ return ((void *)zvni);
+}
+
+/*
+ * Look up VNI hash entry.
+ */
+static zebra_vni_t *
+zvni_lookup (struct zebra_vrf *zvrf, vni_t vni)
+{
+ zebra_vni_t tmp_vni;
+ zebra_vni_t *zvni = NULL;
+
+ memset (&tmp_vni, 0, sizeof (zebra_vni_t));
+ tmp_vni.vni = vni;
+ zvni = hash_lookup (zvrf->vni_table, &tmp_vni);
+
+ return zvni;
+}
+
+/*
+ * Add VNI hash entry.
+ */
+static zebra_vni_t *
+zvni_add (struct zebra_vrf *zvrf, vni_t vni)
+{
+ zebra_vni_t tmp_zvni;
+ zebra_vni_t *zvni = NULL;
+
+ memset (&tmp_zvni, 0, sizeof (zebra_vni_t));
+ tmp_zvni.vni = vni;
+ zvni = hash_get (zvrf->vni_table, &tmp_zvni, zvni_alloc);
+ assert (zvni);
+
+ /* Create hash table for MAC */
+ zvni->mac_table = hash_create(mac_hash_keymake,
+ mac_cmp,
+ "Zebra VNI MAC Table");
+
+ /* Create hash table for neighbors */
+ zvni->neigh_table = hash_create(neigh_hash_keymake,
+ neigh_cmp,
+ "Zebra VNI Neighbor Table");
+
+ return zvni;
+}
+
+/*
+ * Delete VNI hash entry.
+ */
+static int
+zvni_del (struct zebra_vrf *zvrf, zebra_vni_t *zvni)
+{
+ zebra_vni_t *tmp_zvni;
+
+ zvni->vxlan_if = NULL;
+
+ /* Free the neighbor hash table. */
+ hash_free(zvni->neigh_table);
+ zvni->neigh_table = NULL;
+
+ /* Free the MAC hash table. */
+ hash_free(zvni->mac_table);
+ zvni->mac_table = NULL;
+
+ /* Free the VNI hash entry and allocated memory. */
+ tmp_zvni = hash_release (zvrf->vni_table, zvni);
+ if (tmp_zvni)
+ XFREE(MTYPE_ZVNI, tmp_zvni);
+
+ return 0;
+}
+
+/*
+ * Inform BGP about local VNI addition.
+ */
+static int
+zvni_send_add_to_client (struct zebra_vrf *zvrf,
+ zebra_vni_t *zvni)
+{
+ struct zserv *client;
+ struct stream *s;
+
+ client = zebra_find_client (ZEBRA_ROUTE_BGP);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = client->obuf;
+ stream_reset (s);
+
+ zserv_create_header (s, ZEBRA_VNI_ADD, zvrf_id (zvrf));
+ stream_putl (s, zvni->vni);
+ stream_put_in_addr (s, &zvni->local_vtep_ip);
+
+ /* Write packet size. */
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Send VNI_ADD %u %s to %s",
+ zvrf_id (zvrf), zvni->vni,
+ inet_ntoa(zvni->local_vtep_ip),
+ zebra_route_string (client->proto));
+
+ client->vniadd_cnt++;
+ return zebra_server_send_message(client);
+}
+
+/*
+ * Inform BGP about local VNI deletion.
+ */
+static int
+zvni_send_del_to_client (struct zebra_vrf *zvrf, vni_t vni)
+{
+ struct zserv *client;
+ struct stream *s;
+
+ client = zebra_find_client (ZEBRA_ROUTE_BGP);
+ /* BGP may not be running. */
+ if (!client)
+ return 0;
+
+ s = client->obuf;
+ stream_reset (s);
+
+ zserv_create_header (s, ZEBRA_VNI_DEL, zvrf_id (zvrf));
+ stream_putl (s, vni);
+
+ /* Write packet size. */
+ stream_putw_at (s, 0, stream_get_endp (s));
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Send VNI_DEL %u to %s", zvrf_id (zvrf), vni,
+ zebra_route_string (client->proto));
+
+ client->vnidel_cnt++;
+ return zebra_server_send_message(client);
+}
+
+/*
+ * Build the VNI hash table by going over the VxLAN interfaces. This
+ * is called when EVPN (advertise-all-vni) is enabled.
+ */
+static void
+zvni_build_hash_table (struct zebra_vrf *zvrf)
+{
+ struct listnode *node;
+ struct interface *ifp;
+
+ /* Walk VxLAN interfaces and create VNI hash. */
+ for (ALL_LIST_ELEMENTS_RO (vrf_iflist (zvrf_id (zvrf)), node, ifp))
+ {
+ struct zebra_if *zif;
+ struct zebra_l2info_vxlan *vxl;
+ zebra_vni_t *zvni;
+ vni_t vni;
+
+ zif = ifp->info;
+ if (!zif || zif->zif_type != ZEBRA_IF_VXLAN)
+ continue;
+ vxl = &zif->l2info.vxl;
+
+ vni = vxl->vni;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Create VNI hash for intf %s(%u) VNI %u local IP %s",
+ zvrf_id (zvrf), ifp->name, ifp->ifindex, vni,
+ inet_ntoa (vxl->vtep_ip));
+
+ /* VNI hash entry is not expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (zvni)
+ {
+ zlog_err ("VNI hash already present for VRF %d IF %s(%u) VNI %u",
+ zvrf_id (zvrf), ifp->name, ifp->ifindex, vni);
+ continue;
+ }
+
+ zvni = zvni_add (zvrf, vni);
+ if (!zvni)
+ {
+ zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u",
+ zvrf_id (zvrf), ifp->name, ifp->ifindex, vni);
+ return;
+ }
+
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+
+ /* Inform BGP if interface is up and mapped to bridge. */
+ if (if_is_operative (ifp) &&
+ zif->brslave_info.br_if)
+ zvni_send_add_to_client (zvrf, zvni);
+ }
+}
+
+/*
+ * See if remote VTEP matches with prefix.
+ */
+static int
+zvni_vtep_match (struct in_addr *vtep_ip, zebra_vtep_t *zvtep)
+{
+ return (IPV4_ADDR_SAME (vtep_ip, &zvtep->vtep_ip));
+}
+
+/*
+ * Locate remote VTEP in VNI hash table.
+ */
+static zebra_vtep_t *
+zvni_vtep_find (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+{
+ zebra_vtep_t *zvtep;
+
+ if (!zvni)
+ return NULL;
+
+ for (zvtep = zvni->vteps; zvtep; zvtep = zvtep->next)
+ {
+ if (zvni_vtep_match (vtep_ip, zvtep))
+ break;
+ }
+
+ return zvtep;
+}
+
+/*
+ * Add remote VTEP to VNI hash table.
+ */
+static zebra_vtep_t *
+zvni_vtep_add (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+{
+ zebra_vtep_t *zvtep;
+
+ zvtep = XCALLOC (MTYPE_ZVNI_VTEP, sizeof(zebra_vtep_t));
+ if (!zvtep)
+ {
+ zlog_err ("Failed to alloc VTEP entry, VNI %u", zvni->vni);
+ return NULL;
+ }
+
+ zvtep->vtep_ip = *vtep_ip;
+
+ if (zvni->vteps)
+ zvni->vteps->prev = zvtep;
+ zvtep->next = zvni->vteps;
+ zvni->vteps = zvtep;
+
+ return zvtep;
+}
+
+/*
+ * Remove remote VTEP from VNI hash table.
+ */
+static int
+zvni_vtep_del (zebra_vni_t *zvni, zebra_vtep_t *zvtep)
+{
+ if (zvtep->next)
+ zvtep->next->prev = zvtep->prev;
+ if (zvtep->prev)
+ zvtep->prev->next = zvtep->next;
+ else
+ zvni->vteps = zvtep->next;
+
+ zvtep->prev = zvtep->next = NULL;
+ XFREE (MTYPE_ZVNI_VTEP, zvtep);
+
+ return 0;
+}
+
+/*
+ * Delete all remote VTEPs for this VNI (upon VNI delete). Also
+ * uninstall from kernel if asked to.
+ */
+static int
+zvni_vtep_del_all (zebra_vni_t *zvni, int uninstall)
+{
+ zebra_vtep_t *zvtep, *zvtep_next;
+
+ if (!zvni)
+ return -1;
+
+ for (zvtep = zvni->vteps; zvtep; zvtep = zvtep_next)
+ {
+ zvtep_next = zvtep->next;
+ if (uninstall)
+ zvni_vtep_uninstall (zvni, &zvtep->vtep_ip);
+ zvni_vtep_del (zvni, zvtep);
+ }
+
+ return 0;
+}
+
+/*
+ * Install remote VTEP into the kernel.
+ */
+static int
+zvni_vtep_install (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+{
+ return kernel_add_vtep (zvni->vni, zvni->vxlan_if, vtep_ip);
+}
+
+/*
+ * Uninstall remote VTEP from the kernel.
+ */
+static int
+zvni_vtep_uninstall (zebra_vni_t *zvni, struct in_addr *vtep_ip)
+{
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p couldn't be uninstalled - no intf",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ return kernel_del_vtep (zvni->vni, zvni->vxlan_if, vtep_ip);
+}
+
+/*
+ * Cleanup VNI/VTEP and update kernel
+ */
+static void
+zvni_cleanup_all (struct hash_backet *backet, void *zvrf)
+{
+ zebra_vni_t *zvni;
+
+ zvni = (zebra_vni_t *) backet->data;
+ if (!zvni)
+ return;
+
+ /* Free up all neighbors and MACs, if any. */
+ zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all (zvni, 1);
+
+ /* Delete the hash entry. */
+ zvni_del (zvrf, zvni);
+}
+
+
+/* Public functions */
+
+/*
+ * Display Neighbors for a VNI (VTY command handler).
+ */
+void
+zebra_vxlan_print_neigh_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni)
+{
+ zebra_vni_t *zvni;
+ u_int32_t num_neigh;
+ struct neigh_walk_ctx wctx;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ vty_out (vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+ num_neigh = hashcount(zvni->neigh_table);
+ if (!num_neigh)
+ 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
+ * the maximum width.
+ */
+ memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.vty = vty;
+ wctx.addr_width = 15;
+ 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");
+
+ hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+}
+
+/*
+ * Display neighbors across all VNIs (VTY command handler).
+ */
+void
+zebra_vxlan_print_neigh_all_vni (struct vty *vty, struct zebra_vrf *zvrf)
+{
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ hash_iterate(zvrf->vni_table, zvni_print_neigh_hash_all_vni, vty);
+}
+
+/*
+ * Display specific neighbor for a VNI, if present (VTY command handler).
+ */
+void
+zebra_vxlan_print_specific_neigh_vni (struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni, struct ipaddr *ip)
+{
+ zebra_vni_t *zvni;
+ zebra_neigh_t *n;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ vty_out (vty, "%% VNI %u does not exist", vni);
+ return;
+ }
+ n = zvni_neigh_lookup (zvni, ip);
+ if (!n)
+ {
+ vty_out (vty, "%% Requested neighbor does not exist in VNI %u\n",
+ vni);
+ return;
+ }
+
+ zvni_print_neigh (n, vty);
+}
+
+/*
+ * Display neighbors for a VNI from specific VTEP (VTY command handler).
+ * 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)
+{
+ zebra_vni_t *zvni;
+ u_int32_t num_neigh;
+ struct neigh_walk_ctx wctx;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ vty_out (vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+ num_neigh = hashcount(zvni->neigh_table);
+ if (!num_neigh)
+ return;
+
+ memset (&wctx, 0, sizeof (struct neigh_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.vty = vty;
+ wctx.flags = SHOW_REMOTE_NEIGH_FROM_VTEP;
+ wctx.r_vtep_ip = vtep_ip;
+
+ hash_iterate(zvni->neigh_table, zvni_print_neigh_hash, &wctx);
+}
+
+/*
+ * Display MACs for a VNI (VTY command handler).
+ */
+void
+zebra_vxlan_print_macs_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni)
+{
+ zebra_vni_t *zvni;
+ u_int32_t num_macs;
+ struct mac_walk_ctx wctx;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ vty_out (vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+ num_macs = hashcount(zvni->mac_table);
+ if (!num_macs)
+ return;
+
+ memset (&wctx, 0, sizeof (struct mac_walk_ctx));
+ wctx.zvni = zvni;
+ wctx.vty = vty;
+
+ 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");
+
+ hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
+}
+
+/*
+ * Display MACs for all VNIs (VTY command handler).
+ */
+void
+zebra_vxlan_print_macs_all_vni (struct vty *vty, struct zebra_vrf *zvrf)
+{
+ struct mac_walk_ctx wctx;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ memset (&wctx, 0, sizeof (struct mac_walk_ctx));
+ wctx.vty = vty;
+ hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
+}
+
+/*
+ * Display MACs for all VNIs (VTY command handler).
+ */
+void
+zebra_vxlan_print_macs_all_vni_vtep (struct vty *vty, struct zebra_vrf *zvrf,
+ struct in_addr vtep_ip)
+{
+ struct mac_walk_ctx wctx;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ memset (&wctx, 0, sizeof (struct mac_walk_ctx));
+ wctx.vty = vty;
+ wctx.flags = SHOW_REMOTE_MAC_FROM_VTEP;
+ wctx.r_vtep_ip = vtep_ip;
+ hash_iterate(zvrf->vni_table, zvni_print_mac_hash_all_vni, &wctx);
+}
+
+/*
+ * Display specific MAC for a VNI, if present (VTY command handler).
+ */
+void
+zebra_vxlan_print_specific_mac_vni (struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni, struct ethaddr *macaddr)
+{
+ zebra_vni_t *zvni;
+ zebra_mac_t *mac;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ vty_out (vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+ mac = zvni_mac_lookup (zvni, macaddr);
+ if (!mac)
+ {
+ vty_out (vty, "%% Requested MAC does not exist in VNI %u\n",
+ vni);
+ return;
+ }
+
+ zvni_print_mac (mac, vty);
+}
+
+/*
+ * 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)
+{
+ zebra_vni_t *zvni;
+ u_int32_t num_macs;
+ struct mac_walk_ctx wctx;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ vty_out (vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+ num_macs = hashcount(zvni->mac_table);
+ if (!num_macs)
+ return;
+ 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;
+ hash_iterate(zvni->mac_table, zvni_print_mac_hash, &wctx);
+}
+
+
+/*
+ * Display VNI information (VTY command handler).
+ */
+void
+zebra_vxlan_print_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni)
+{
+ zebra_vni_t *zvni;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ vty_out (vty, "%% VNI %u does not exist\n", vni);
+ return;
+ }
+ zvni_print (zvni, (void *)vty);
+}
+
+/*
+ * Display VNI hash table (VTY command handler).
+ */
+void
+zebra_vxlan_print_vnis (struct vty *vty, struct zebra_vrf *zvrf)
+{
+ u_int32_t num_vnis;
+
+ if (!EVPN_ENABLED(zvrf))
+ return;
+ num_vnis = hashcount(zvrf->vni_table);
+ if (!num_vnis)
+ 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);
+}
+
+/*
+ * Handle neighbor delete (on a VLAN device / L3 interface) from the
+ * kernel. This may result in either the neighbor getting deleted from
+ * our database or being re-added to the kernel (if it is a valid
+ * remote neighbor).
+ */
+int
+zebra_vxlan_local_neigh_del (struct interface *ifp,
+ struct interface *link_if,
+ struct ipaddr *ip)
+{
+ zebra_vni_t *zvni;
+ zebra_neigh_t *n;
+ struct zebra_vrf *zvrf;
+ char buf[INET6_ADDRSTRLEN];
+
+ /* We are only interested in neighbors on an SVI that resides on top
+ * of a VxLAN bridge.
+ */
+ zvni = zvni_map_svi (ifp, link_if);
+ if (!zvni)
+ return 0;
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p doesn't have intf upon local neighbor DEL",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Del neighbor %s intf %s(%u) -> VNI %u",
+ ifp->vrf_id, ipaddr2str (ip, buf, sizeof(buf)),
+ ifp->name, ifp->ifindex, zvni->vni);
+
+ /* If entry doesn't exist, nothing to do. */
+ n = zvni_neigh_lookup (zvni, ip);
+ if (!n)
+ 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.
+ */
+ if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE))
+ {
+ zvni_neigh_install (zvni, n);
+ return 0;
+ }
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ assert(zvrf);
+
+ /* Remove neighbor from BGP. */
+ zvni_neigh_send_del_to_client (zvrf, zvni->vni, &n->ip, &n->emac);
+
+ /* Delete this neighbor entry. */
+ zvni_neigh_del (zvni, n);
+
+ return 0;
+}
+
+/*
+ * Handle neighbor add or update (on a VLAN device / L3 interface)
+ * from the kernel.
+ */
+int
+zebra_vxlan_local_neigh_add_update (struct interface *ifp,
+ struct interface *link_if,
+ struct ipaddr *ip,
+ struct ethaddr *macaddr,
+ u_int16_t state,
+ u_char ext_learned)
+{
+ zebra_vni_t *zvni;
+ zebra_neigh_t *n;
+ struct zebra_vrf *zvrf;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+ int send_upd = 1, send_del = 0;
+
+ /* We are only interested in neighbors on an SVI that resides on top
+ * of a VxLAN bridge.
+ */
+ zvni = zvni_map_svi (ifp, link_if);
+ if (!zvni)
+ return 0;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ assert(zvrf);
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Add/Update neighbor %s MAC %s intf %s(%u) state 0x%x "
+ "%s-> VNI %u",
+ ifp->vrf_id, ipaddr2str (ip, buf2, sizeof(buf2)),
+ prefix_mac2str (macaddr, buf, sizeof (buf)),
+ ifp->name, ifp->ifindex, state,
+ ext_learned ? "ext-learned " : "", zvni->vni);
+
+ /* If same entry already exists, it might be a change or it might be a
+ * move from remote to local.
+ */
+ n = zvni_neigh_lookup (zvni, ip);
+ if (n)
+ {
+ if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_LOCAL))
+ {
+ if (memcmp (n->emac.octet, macaddr->octet, ETHER_ADDR_LEN) == 0)
+ {
+ if (n->ifindex == ifp->ifindex)
+ /* we're not interested in whatever has changed. */
+ return 0;
+ /* client doesn't care about a purely local change. */
+ send_upd = 0;
+ }
+ else
+ /* If the MAC has changed, issue a delete first as this means a
+ * different MACIP route.
+ */
+ send_del = 1;
+ }
+ else if (ext_learned)
+ /* The neighbor is remote and that is the notification we got. */
+ {
+ /* TODO: Evaluate if we need to do anything here. */
+ return 0;
+ }
+ else
+ /* Neighbor has moved from remote to local. */
+ {
+ UNSET_FLAG (n->flags, ZEBRA_NEIGH_REMOTE);
+ n->r_vtep_ip.s_addr = 0;
+ }
+ }
+ else
+ {
+ n = zvni_neigh_add (zvni, ip);
+ 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, buf, sizeof (buf)),
+ ifp->name, ifp->ifindex, zvni->vni);
+ return -1;
+ }
+ }
+
+ /* Issue delete for older info, if needed. */
+ if (send_del)
+ zvni_neigh_send_del_to_client (zvrf, zvni->vni, &n->ip, &n->emac);
+
+ /* Set "local" forwarding info. */
+ SET_FLAG (n->flags, ZEBRA_NEIGH_LOCAL);
+ memcpy (&n->emac, macaddr, ETHER_ADDR_LEN);
+ n->ifindex = ifp->ifindex;
+
+ /* Inform BGP if required. */
+ if (send_upd)
+ return zvni_neigh_send_add_to_client (zvrf, zvni->vni, ip, macaddr);
+
+ return 0;
+}
+
+/*
+ * Handle message from client to delete a remote MACIP for a VNI.
+ */
+int zebra_vxlan_remote_macip_del (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ struct stream *s;
+ vni_t vni;
+ struct ethaddr macaddr;
+ struct ipaddr ip;
+ struct in_addr vtep_ip;
+ zebra_vni_t *zvni;
+ zebra_mac_t *mac;
+ zebra_neigh_t *n;
+ u_short l = 0, ipa_len;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+
+ s = client->ibuf;
+
+ while (l < length)
+ {
+ /* Obtain each remote MACIP and process. */
+ /* Message contains VNI, followed by MAC followed by IP (if any)
+ * followed by remote VTEP IP.
+ */
+ mac = NULL;
+ n = NULL;
+ memset (&ip, 0, sizeof (ip));
+ vni = (vni_t) stream_getl (s);
+ stream_get (&macaddr.octet, s, ETHER_ADDR_LEN);
+ ipa_len = stream_getl (s);
+ if (ipa_len)
+ {
+ ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4: IPADDR_V6;
+ stream_get (&ip.ip.addr, s, ipa_len);
+ }
+ l += 4 + ETHER_ADDR_LEN + 4 + ipa_len;
+ vtep_ip.s_addr = stream_get_ipv4(s);
+ l += IPV4_MAX_BYTELEN;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Recv MACIP Del MAC %s IP %s VNI %u Remote VTEP %s from %s",
+ zvrf_id (zvrf),
+ prefix_mac2str (&macaddr, buf, sizeof (buf)),
+ ipaddr2str (&ip, buf1, sizeof (buf1)),
+ vni, inet_ntoa (vtep_ip),
+ zebra_route_string (client->proto));
+
+ /* Locate VNI hash entry - expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("Failed to locate VNI hash upon remote MACIP DEL, "
+ "VRF %d VNI %u", zvrf_id (zvrf), vni);
+ continue;
+ }
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p doesn't have intf upon remote MACIP DEL",
+ vni, zvni);
+ continue;
+ }
+
+ /* The remote VTEP specified is normally expected to exist, but it is
+ * possible that the peer may delete the VTEP before deleting any MACs
+ * referring to the VTEP, in which case the handler (see remote_vtep_del)
+ * would have already deleted the MACs.
+ */
+ if (!zvni_vtep_find (zvni, &vtep_ip))
+ continue;
+
+ /* If the local VxLAN interface is not up (should be a transient
+ * event), there's nothing more to do.
+ */
+ if (!if_is_operative (zvni->vxlan_if))
+ continue;
+
+ mac = zvni_mac_lookup (zvni, &macaddr);
+ if (ipa_len)
+ n = zvni_neigh_lookup (zvni, &ip);
+
+ if (n && !mac)
+ {
+ zlog_err ("failed to locate MAC %s for neigh %s in VRF %u VNI %u",
+ prefix_mac2str (&macaddr, buf, sizeof (buf)),
+ ipaddr2str (&ip, buf1, sizeof (buf1)),
+ zvrf_id (zvrf), vni);
+ continue;
+ }
+
+ /* If the remote mac or neighbor doesn't exist there is nothing more
+ * to do. Otherwise, uninstall the entry and then remove it.
+ */
+ if (!mac && !n)
+ continue;
+
+ /* Uninstall remote neighbor or MAC. */
+ if (n)
+ {
+ /* When the MAC changes for an IP, it is possible the client may
+ * update the new MAC before trying to delete the "old" neighbor
+ * (as these are two different MACIP routes). Do the delete only
+ * if the MAC matches.
+ */
+ if (CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE) &&
+ (memcmp (n->emac.octet, macaddr.octet, ETHER_ADDR_LEN) == 0))
+ {
+ zvni_neigh_uninstall (zvni, n);
+ zvni_neigh_del (zvni, n);
+ zvni_deref_ip2mac (zvni, mac, 1);
+ }
+ }
+ else
+ {
+ if (CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE))
+ {
+ if (!mac->neigh_refcnt)
+ {
+ zvni_mac_uninstall (zvni, mac, 0);
+ zvni_mac_del (zvni, mac);
+ }
+ else
+ SET_FLAG (mac->flags, ZEBRA_MAC_AUTO);
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Handle message from client to add a remote MACIP for a VNI. This
+ * could be just the add of a MAC address or the add of a neighbor
+ * (IP+MAC).
+ */
+int
+zebra_vxlan_remote_macip_add (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ struct stream *s;
+ vni_t vni;
+ struct ethaddr macaddr;
+ struct ipaddr ip;
+ struct in_addr vtep_ip;
+ zebra_vni_t *zvni;
+ zebra_vtep_t *zvtep;
+ zebra_mac_t *mac, *old_mac;
+ zebra_neigh_t *n;
+ u_short l = 0, ipa_len;
+ int update_mac = 0, update_neigh = 0;
+ char buf[ETHER_ADDR_STRLEN];
+ char buf1[INET6_ADDRSTRLEN];
+ u_char sticky;
+
+ assert (EVPN_ENABLED (zvrf));
+
+ s = client->ibuf;
+
+ while (l < length)
+ {
+ /* Obtain each remote MACIP and process. */
+ /* Message contains VNI, followed by MAC followed by IP (if any)
+ * followed by remote VTEP IP.
+ */
+ update_mac = update_neigh = 0;
+ mac = NULL;
+ n = NULL;
+ memset (&ip, 0, sizeof (ip));
+ vni = (vni_t) stream_getl (s);
+ stream_get (&macaddr.octet, s, ETHER_ADDR_LEN);
+ ipa_len = stream_getl (s);
+ if (ipa_len)
+ {
+ ip.ipa_type = (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4: IPADDR_V6;
+ stream_get (&ip.ip.addr, s, ipa_len);
+ }
+ l += 4 + ETHER_ADDR_LEN + 4 + ipa_len;
+ vtep_ip.s_addr = stream_get_ipv4 (s);
+ l += IPV4_MAX_BYTELEN;
+
+ /* Get 'sticky' flag. */
+ sticky = stream_getc(s);
+ l++;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Recv MACIP Add %sMAC %s IP %s VNI %u Remote VTEP %s from %s",
+ zvrf_id (zvrf),
+ sticky ? "sticky " : "",
+ prefix_mac2str (&macaddr, buf, sizeof (buf)),
+ ipaddr2str (&ip, buf1, sizeof (buf1)),
+ vni, inet_ntoa (vtep_ip),
+ zebra_route_string (client->proto));
+
+ /* Locate VNI hash entry - expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ zlog_err ("Failed to locate VNI hash upon remote MACIP ADD, VRF %d VNI %u",
+ zvrf_id (zvrf), vni);
+ continue;
+ }
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p doesn't have intf upon remote MACIP add",
+ vni, zvni);
+ continue;
+ }
+ /* If the local VxLAN interface is not up (should be a transient
+ * event), there's nothing more to do.
+ */
+ if (!if_is_operative (zvni->vxlan_if))
+ continue;
+
+ /* The remote VTEP specified should normally exist, but it is possible
+ * that when peering comes up, peer may advertise MACIP routes before
+ * advertising type-3 routes.
+ */
+ zvtep = zvni_vtep_find (zvni, &vtep_ip);
+ if (!zvtep)
+ {
+ if (zvni_vtep_add (zvni, &vtep_ip) == NULL)
+ {
+ zlog_err ("Failed to add remote VTEP, VRF %d VNI %u zvni %p",
+ zvrf_id (zvrf), vni, zvni);
+ continue;
+ }
+
+ zvni_vtep_install (zvni, &vtep_ip);
+ }
+
+ /* First, check if the remote MAC is unknown or has a change. If so,
+ * that needs to be updated first. Note that client could install
+ * MAC and MACIP separately or just install the latter.
+ */
+ mac = zvni_mac_lookup (zvni, &macaddr);
+ if (!mac || !CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE) ||
+ (CHECK_FLAG (mac->flags, ZEBRA_MAC_STICKY) ? 1: 0) != sticky ||
+ !IPV4_ADDR_SAME(&mac->fwd_info.r_vtep_ip, &vtep_ip))
+ update_mac = 1;
+
+ if (update_mac)
+ {
+ if (!mac)
+ {
+ mac = zvni_mac_add (zvni, &macaddr);
+ if (!mac)
+ {
+ zlog_warn ("%u:Failed to add MAC %s VNI %u Remote VTEP %s",
+ zvrf_id (zvrf),
+ prefix_mac2str (&macaddr, buf, sizeof (buf)),
+ vni, inet_ntoa (vtep_ip));
+ return -1;
+ }
+
+ /* Is this MAC created for a MACIP? */
+ if (ipa_len)
+ SET_FLAG (mac->flags, ZEBRA_MAC_AUTO);
+ }
+ else if (CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
+ {
+ /* Moving from local to remote, issue delete. */
+ zvni_mac_uninstall (zvni, mac, 1);
+ }
+
+ /* Set "auto" and "remote" forwarding info. */
+ UNSET_FLAG (mac->flags, ZEBRA_MAC_LOCAL);
+ memset (&mac->fwd_info, 0, sizeof (mac->fwd_info));
+ SET_FLAG (mac->flags, ZEBRA_MAC_REMOTE);
+ mac->fwd_info.r_vtep_ip = vtep_ip;
+
+ if (sticky)
+ SET_FLAG (mac->flags, ZEBRA_MAC_STICKY);
+ else
+ UNSET_FLAG (mac->flags, ZEBRA_MAC_STICKY);
+
+ /* Install the entry. */
+ zvni_mac_install (zvni, mac);
+ }
+
+ /* If there is no IP, continue - after clearing AUTO flag of MAC. */
+ if (!ipa_len)
+ {
+ UNSET_FLAG (mac->flags, ZEBRA_MAC_AUTO);
+ continue;
+ }
+
+ /* Check if the remote neighbor itself is unknown or has a change.
+ * If so, create or update and then install the entry.
+ */
+ n = zvni_neigh_lookup (zvni, &ip);
+ if (!n || !CHECK_FLAG (n->flags, ZEBRA_NEIGH_REMOTE) ||
+ (memcmp(&n->emac, &macaddr, sizeof (macaddr)) != 0) ||
+ !IPV4_ADDR_SAME(&n->r_vtep_ip, &vtep_ip))
+ update_neigh = 1;
+
+ if (update_neigh)
+ {
+ if (!n)
+ {
+ n = zvni_neigh_add (zvni, &ip);
+ if (!n)
+ {
+ zlog_warn ("%u:Failed to add Neigh %s MAC %s VNI %u Remote VTEP %s",
+ zvrf_id (zvrf), ipaddr2str (&ip, buf1, sizeof (buf1)),
+ prefix_mac2str (&macaddr, buf, sizeof (buf)),
+ vni, inet_ntoa (vtep_ip));
+ return -1;
+ }
+
+ /* New neighbor referring to this MAC. */
+ mac->neigh_refcnt++;
+ }
+ else if (memcmp(&n->emac, &macaddr, sizeof (macaddr)) != 0)
+ {
+ /* MAC change, update ref counts for old and new MAC. */
+ old_mac = zvni_mac_lookup (zvni, &n->emac);
+ if (old_mac)
+ zvni_deref_ip2mac (zvni, old_mac, 1);
+ mac->neigh_refcnt++;
+ }
+
+ /* Set "remote" forwarding info. */
+ UNSET_FLAG (n->flags, ZEBRA_NEIGH_LOCAL);
+ /* TODO: Handle MAC change. */
+ memcpy (&n->emac, &macaddr, ETHER_ADDR_LEN);
+ n->r_vtep_ip = vtep_ip;
+ SET_FLAG (n->flags, ZEBRA_NEIGH_REMOTE);
+
+ /* Install the entry. */
+ zvni_neigh_install (zvni, n);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Handle notification of MAC add/update over VxLAN. If the kernel is notifying
+ * us, this must involve a multihoming scenario. Treat this as implicit delete
+ * of any prior local MAC.
+ */
+int
+zebra_vxlan_check_del_local_mac (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *macaddr,
+ vlanid_t vid)
+{
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+ struct zebra_l2info_vxlan *vxl;
+ vni_t vni;
+ zebra_vni_t *zvni;
+ zebra_mac_t *mac;
+ char buf[ETHER_ADDR_STRLEN];
+ u_char sticky;
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ /* If EVPN is not enabled, nothing to do. */
+ if (!EVPN_ENABLED(zvrf))
+ return 0;
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ return 0;
+
+ /* If entry doesn't exist, nothing to do. */
+ mac = zvni_mac_lookup (zvni, macaddr);
+ if (!mac)
+ return 0;
+
+ /* Is it a local entry? */
+ if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Add/update remote MAC %s intf %s(%u) VNI %u - del local",
+ ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
+ ifp->name, ifp->ifindex, vni);
+
+ /* 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);
+
+ /* Delete this MAC entry. */
+ zvni_mac_del (zvni, mac);
+
+ return 0;
+}
+
+/*
+ * Handle remote MAC delete by kernel; readd the remote MAC if we have it.
+ * This can happen because the remote MAC entries are also added as "dynamic",
+ * so the kernel can ageout the entry.
+ */
+int
+zebra_vxlan_check_readd_remote_mac (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *macaddr,
+ vlanid_t vid)
+{
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+ struct zebra_l2info_vxlan *vxl;
+ vni_t vni;
+ zebra_vni_t *zvni;
+ zebra_mac_t *mac;
+ char buf[ETHER_ADDR_STRLEN];
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ /* If EVPN is not enabled, nothing to do. */
+ if (!EVPN_ENABLED(zvrf))
+ return 0;
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ return 0;
+
+ /* If entry doesn't exist, nothing to do. */
+ mac = zvni_mac_lookup (zvni, macaddr);
+ if (!mac)
+ return 0;
+
+ /* Is it a remote entry? */
+ if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_REMOTE))
+ return 0;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Del remote MAC %s intf %s(%u) VNI %u - readd",
+ ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
+ ifp->name, ifp->ifindex, vni);
+
+ zvni_mac_install (zvni, mac);
+ return 0;
+}
+
+/*
+ * Handle local MAC delete (on a port or VLAN corresponding to this VNI).
+ */
+int
+zebra_vxlan_local_mac_del (struct interface *ifp, struct interface *br_if,
+ struct ethaddr *macaddr, vlanid_t vid)
+{
+ zebra_vni_t *zvni;
+ zebra_mac_t *mac;
+ struct zebra_vrf *zvrf;
+ char buf[ETHER_ADDR_STRLEN];
+ u_char sticky;
+
+ /* We are interested in MACs only on ports or (port, VLAN) that
+ * map to a VNI.
+ */
+ zvni = zvni_map_vlan (ifp, br_if, vid);
+ if (!zvni)
+ return 0;
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p doesn't have intf upon local MAC DEL",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Del MAC %s intf %s(%u) VID %u -> VNI %u",
+ ifp->vrf_id, prefix_mac2str (macaddr, buf, sizeof (buf)),
+ ifp->name, ifp->ifindex, vid, zvni->vni);
+
+ /* If entry doesn't exist, nothing to do. */
+ mac = zvni_mac_lookup (zvni, macaddr);
+ if (!mac)
+ return 0;
+
+ /* Is it a local entry? */
+ if (!CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
+ return 0;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ assert(zvrf);
+
+ /* 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);
+
+ /* Delete this MAC entry. */
+ zvni_mac_del (zvni, mac);
+
+ return 0;
+}
+
+/*
+ * Handle local MAC add (on a port or VLAN corresponding to this VNI).
+ */
+int
+zebra_vxlan_local_mac_add_update (struct interface *ifp, struct interface *br_if,
+ struct ethaddr *macaddr, vlanid_t vid,
+ u_char sticky)
+{
+ zebra_vni_t *zvni;
+ zebra_mac_t *mac;
+ struct zebra_vrf *zvrf;
+ char buf[ETHER_ADDR_STRLEN];
+ int add = 1;
+ u_char mac_sticky;
+
+ /* We are interested in MACs only on ports or (port, VLAN) that
+ * map to a VNI.
+ */
+ zvni = zvni_map_vlan (ifp, br_if, vid);
+ if (!zvni)
+ {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Add/Update %sMAC %s intf %s(%u) VID %u, could not find VNI",
+ ifp->vrf_id,
+ sticky ? "sticky " : "",
+ prefix_mac2str (macaddr, buf, sizeof (buf)),
+ ifp->name, ifp->ifindex, vid);
+ return 0;
+ }
+
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p doesn't have intf upon local MAC ADD",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u",
+ ifp->vrf_id,
+ sticky ? "sticky " : "",
+ prefix_mac2str (macaddr, buf, sizeof (buf)),
+ ifp->name, ifp->ifindex, vid, zvni->vni);
+
+ /* If same entry already exists, nothing to do. */
+ mac = zvni_mac_lookup (zvni, macaddr);
+ if (mac)
+ {
+ if (CHECK_FLAG (mac->flags, ZEBRA_MAC_LOCAL))
+ {
+ mac_sticky = CHECK_FLAG (mac->flags, ZEBRA_MAC_STICKY) ? 1: 0;
+
+ if (mac_sticky == sticky &&
+ mac->fwd_info.local.ifindex == ifp->ifindex &&
+ mac->fwd_info.local.vid == vid)
+ {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Add/Update %sMAC %s intf %s(%u) VID %u -> VNI %u, "
+ "entry exists and has not changed ",
+ ifp->vrf_id,
+ sticky ? "sticky " : "",
+ prefix_mac2str (macaddr, buf, sizeof (buf)),
+ ifp->name, ifp->ifindex, vid, zvni->vni);
+ return 0;
+ }
+
+ add = 0; /* This is an update of local interface. */
+ }
+ }
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(zvni->vxlan_if->vrf_id);
+ assert(zvrf);
+
+ 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, vid);
+ return -1;
+ }
+ }
+
+ /* Set "local" forwarding info. */
+ UNSET_FLAG (mac->flags, ZEBRA_MAC_REMOTE);
+ memset (&mac->fwd_info, 0, sizeof (mac->fwd_info));
+ SET_FLAG (mac->flags, ZEBRA_MAC_LOCAL);
+ mac->fwd_info.local.ifindex = ifp->ifindex;
+ mac->fwd_info.local.vid = vid;
+
+ if (sticky)
+ SET_FLAG (mac->flags, ZEBRA_MAC_STICKY);
+ else
+ UNSET_FLAG (mac->flags, ZEBRA_MAC_STICKY);
+
+ /* Inform BGP if required. */
+ if (add)
+ return zvni_mac_send_add_to_client (zvrf, zvni->vni, macaddr, sticky);
+
+ return 0;
+}
+
+/*
+ * Handle message from client to delete a remote VTEP for a VNI.
+ */
+int zebra_vxlan_remote_vtep_del (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ struct stream *s;
+ u_short l = 0;
+ vni_t vni;
+ struct in_addr vtep_ip;
+ zebra_vni_t *zvni;
+ zebra_vtep_t *zvtep;
+
+ s = client->ibuf;
+
+ while (l < length)
+ {
+ /* Obtain each remote VTEP and process. */
+ vni = (vni_t) stream_getl (s);
+ l += 4;
+ vtep_ip.s_addr = stream_get_ipv4 (s);
+ l += IPV4_MAX_BYTELEN;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Recv VTEP_DEL %s VNI %u from %s",
+ zvrf_id (zvrf), inet_ntoa (vtep_ip),
+ vni, zebra_route_string (client->proto));
+
+ /* Locate VNI hash entry - expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("Failed to locate VNI hash upon remote VTEP DEL, "
+ "VRF %d VNI %u", zvrf_id (zvrf), vni);
+ continue;
+ }
+
+ /* If the remote VTEP does not exist, there's nothing more to do.
+ * Otherwise, uninstall any remote MACs pointing to this VTEP and
+ * then, the VTEP entry itself and remove it.
+ */
+ zvtep = zvni_vtep_find (zvni, &vtep_ip);
+ if (!zvtep)
+ continue;
+
+ zvni_neigh_del_from_vtep (zvni, 1, &vtep_ip);
+ zvni_mac_del_from_vtep (zvni, 1, &vtep_ip);
+ zvni_vtep_uninstall (zvni, &vtep_ip);
+ zvni_vtep_del (zvni, zvtep);
+ }
+
+ return 0;
+}
+
+/*
+ * Handle message from client to add a remote VTEP for a VNI.
+ */
+int zebra_vxlan_remote_vtep_add (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ struct stream *s;
+ u_short l = 0;
+ vni_t vni;
+ struct in_addr vtep_ip;
+ zebra_vni_t *zvni;
+
+ assert (EVPN_ENABLED (zvrf));
+
+ s = client->ibuf;
+
+ while (l < length)
+ {
+ /* Obtain each remote VTEP and process. */
+ vni = (vni_t) stream_getl (s);
+ l += 4;
+ vtep_ip.s_addr = stream_get_ipv4 (s);
+ l += IPV4_MAX_BYTELEN;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Recv VTEP_ADD %s VNI %u from %s",
+ zvrf_id (zvrf), inet_ntoa (vtep_ip),
+ vni, zebra_route_string (client->proto));
+
+ /* Locate VNI hash entry - expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ zlog_err ("Failed to locate VNI hash upon remote VTEP ADD, VRF %d VNI %u",
+ zvrf_id (zvrf), vni);
+ continue;
+ }
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p doesn't have intf upon remote VTEP ADD",
+ zvni->vni, zvni);
+ continue;
+ }
+
+
+ /* If the remote VTEP already exists, or the local VxLAN interface is
+ * not up (should be a transient event), there's nothing more to do.
+ * Otherwise, add and install the entry.
+ */
+ if (zvni_vtep_find (zvni, &vtep_ip))
+ continue;
+
+ if (!if_is_operative (zvni->vxlan_if))
+ continue;
+
+ if (zvni_vtep_add (zvni, &vtep_ip) == NULL)
+ {
+ zlog_err ("Failed to add remote VTEP, VRF %d VNI %u zvni %p",
+ zvrf_id (zvrf), vni, zvni);
+ continue;
+ }
+
+ zvni_vtep_install (zvni, &vtep_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).
+ */
+int
+zebra_vxlan_svi_down (struct interface *ifp, struct interface *link_if)
+{
+ return 0;
+}
+
+/*
+ * Handle SVI interface coming up. This may or may not be of interest,
+ * but if this is a SVI on a VxLAN bridge, we need to install any remote
+ * neighbor entries (which will be used for EVPN ARP suppression).
+ */
+int
+zebra_vxlan_svi_up (struct interface *ifp, struct interface *link_if)
+{
+ zebra_vni_t *zvni;
+ struct neigh_walk_ctx n_wctx;
+
+ zvni = zvni_map_svi (ifp, link_if);
+ if (!zvni)
+ return 0;
+
+ if (!zvni->vxlan_if)
+ {
+ zlog_err ("VNI %u hash %p doesn't have intf upon SVI up",
+ zvni->vni, zvni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:SVI %s(%u) VNI %u is UP, installing neighbors",
+ ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni);
+
+ /* Install any remote neighbors for this VNI. */
+ memset (&n_wctx, 0, sizeof (struct neigh_walk_ctx));
+ n_wctx.zvni = zvni;
+ hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx);
+
+ return 0;
+}
+
+/*
+ * Handle VxLAN interface down - update BGP if required, and do
+ * internal cleanup.
+ */
+int
+zebra_vxlan_if_down (struct interface *ifp)
+{
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+ zebra_vni_t *zvni;
+ struct zebra_l2info_vxlan *vxl;
+ vni_t vni;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ /* If EVPN is not enabled, nothing further to be done. */
+ if (!EVPN_ENABLED(zvrf))
+ return 0;
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Intf %s(%u) VNI %u is DOWN",
+ ifp->vrf_id, ifp->name, ifp->ifindex, vni);
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ zlog_err ("Failed to locate VNI hash at DOWN, VRF %d IF %s(%u) VNI %u",
+ ifp->vrf_id, ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ assert (zvni->vxlan_if == ifp);
+
+ /* Delete this VNI from BGP. */
+ zvni_send_del_to_client (zvrf, zvni->vni);
+
+ /* Free up all neighbors and MACs, if any. */
+ zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all (zvni, 1);
+
+ return 0;
+}
+
+/*
+ * Handle VxLAN interface up - update BGP if required.
+ */
+int
+zebra_vxlan_if_up (struct interface *ifp)
+{
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+ zebra_vni_t *zvni;
+ struct zebra_l2info_vxlan *vxl;
+ vni_t vni;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ /* If EVPN is not enabled, nothing further to be done. */
+ if (!EVPN_ENABLED(zvrf))
+ return 0;
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Intf %s(%u) VNI %u is UP",
+ ifp->vrf_id, ifp->name, ifp->ifindex, vni);
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ zlog_err ("Failed to locate VNI hash at UP, VRF %d IF %s(%u) VNI %u",
+ ifp->vrf_id, ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ assert (zvni->vxlan_if == ifp);
+
+ /* If part of a bridge, inform BGP about this VNI. */
+ /* Also, read and populate local MACs and neighbors. */
+ if (zif->brslave_info.br_if)
+ {
+ zvni_send_add_to_client (zvrf, zvni);
+ zvni_read_mac_neigh (zvrf, zvni, ifp);
+ }
+
+ return 0;
+}
+
+/*
+ * Handle VxLAN interface delete. Locate and remove entry in hash table
+ * and update BGP, if required.
+ */
+int
+zebra_vxlan_if_del (struct interface *ifp)
+{
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+ zebra_vni_t *zvni;
+ struct zebra_l2info_vxlan *vxl;
+ vni_t vni;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ /* If EVPN is not enabled, nothing further to be done. */
+ if (!EVPN_ENABLED(zvrf))
+ return 0;
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Del VNI %u intf %s(%u)",
+ ifp->vrf_id, vni, ifp->name, ifp->ifindex);
+
+ /* Locate hash entry; it is expected to exist. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ zlog_err ("Failed to locate VNI hash at del, VRF %d IF %s(%u) VNI %u",
+ ifp->vrf_id, ifp->name, ifp->ifindex, vni);
+ return 0;
+ }
+
+ /* Delete VNI from BGP. */
+ zvni_send_del_to_client (zvrf, zvni->vni);
+
+ /* Free up all neighbors and MAC, if any. */
+ zvni_neigh_del_all (zvrf, zvni, 0, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all (zvrf, zvni, 0, 0, DEL_ALL_MAC);
+
+ /* Free up all remote VTEPs, if any. */
+ zvni_vtep_del_all (zvni, 0);
+
+ /* Delete the hash entry. */
+ if (zvni_del (zvrf, zvni))
+ {
+ zlog_err ("Failed to del VNI hash %p, VRF %d IF %s(%u) VNI %u",
+ zvni, ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Handle VxLAN interface update - change to tunnel IP, master or VLAN.
+ */
+int
+zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags)
+{
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+ zebra_vni_t *zvni;
+ struct zebra_l2info_vxlan *vxl;
+ vni_t vni;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ /* If EVPN is not enabled, nothing further to be done. */
+ if (!EVPN_ENABLED(zvrf))
+ return 0;
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ /* Update VNI hash. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ zlog_err ("Failed to find VNI hash on update, VRF %d IF %s(%u) VNI %u",
+ ifp->vrf_id, ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Update VNI %u intf %s(%u) VLAN %u local IP %s "
+ "master %u chg 0x%x",
+ ifp->vrf_id, vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa (vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex, chgflags);
+
+ /* Removed from bridge? */
+ if ((chgflags & ZEBRA_VXLIF_MASTER_CHANGE) &&
+ (zif->brslave_info.bridge_ifindex == IFINDEX_INTERNAL))
+ {
+ /* Delete from client, remove all remote VTEPs */
+ /* Also, free up all MACs and neighbors. */
+ zvni_send_del_to_client (zvrf, zvni->vni);
+ zvni_neigh_del_all (zvrf, zvni, 1, 0, DEL_ALL_NEIGH);
+ zvni_mac_del_all (zvrf, zvni, 1, 0, DEL_ALL_MAC);
+ zvni_vtep_del_all (zvni, 1);
+ }
+ else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE)
+ {
+ /* Remove all existing local neighbors and MACs for this VNI
+ * (including from BGP)
+ */
+ zvni_neigh_del_all (zvrf, zvni, 0, 1, DEL_LOCAL_MAC);
+ zvni_mac_del_all (zvrf, zvni, 0, 1, DEL_LOCAL_MAC);
+ }
+
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+
+ /* Take further actions needed. Note that if we are here, there is a
+ * change of interest.
+ */
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative (ifp) || !zif->brslave_info.br_if)
+ return 0;
+
+ /* Inform BGP, if there is a change of interest. */
+ if (chgflags & (ZEBRA_VXLIF_MASTER_CHANGE | ZEBRA_VXLIF_LOCAL_IP_CHANGE))
+ zvni_send_add_to_client (zvrf, zvni);
+
+ /* If there is a valid new master or a VLAN mapping change, read and
+ * populate local MACs and neighbors. Also, reinstall any remote MACs
+ * and neighbors for this VNI (based on new VLAN).
+ */
+ if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE)
+ zvni_read_mac_neigh (zvrf, zvni, ifp);
+ else if (chgflags & ZEBRA_VXLIF_VLAN_CHANGE)
+ {
+ struct mac_walk_ctx m_wctx;
+ struct neigh_walk_ctx n_wctx;
+
+ zvni_read_mac_neigh (zvrf, zvni, ifp);
+
+ memset (&m_wctx, 0, sizeof (struct mac_walk_ctx));
+ m_wctx.zvni = zvni;
+ hash_iterate(zvni->mac_table, zvni_install_mac_hash, &m_wctx);
+
+ memset (&n_wctx, 0, sizeof (struct neigh_walk_ctx));
+ n_wctx.zvni = zvni;
+ hash_iterate(zvni->neigh_table, zvni_install_neigh_hash, &n_wctx);
+ }
+
+ return 0;
+}
+
+/*
+ * Handle VxLAN interface add.
+ */
+int
+zebra_vxlan_if_add (struct interface *ifp)
+{
+ struct zebra_if *zif;
+ struct zebra_vrf *zvrf;
+ zebra_vni_t *zvni;
+ struct zebra_l2info_vxlan *vxl;
+ vni_t vni;
+
+ /* Locate VRF corresponding to interface. */
+ zvrf = vrf_info_lookup(ifp->vrf_id);
+ assert(zvrf);
+
+ /* If EVPN is not enabled, nothing further to be done. */
+ if (!EVPN_ENABLED(zvrf))
+ return 0;
+
+ zif = ifp->info;
+ assert(zif);
+ vxl = &zif->l2info.vxl;
+ vni = vxl->vni;
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:Add VNI %u intf %s(%u) VLAN %u local IP %s master %u",
+ ifp->vrf_id, vni, ifp->name, ifp->ifindex,
+ vxl->access_vlan, inet_ntoa (vxl->vtep_ip),
+ zif->brslave_info.bridge_ifindex);
+
+ /* Create or update VNI hash. */
+ zvni = zvni_lookup (zvrf, vni);
+ if (!zvni)
+ {
+ zvni = zvni_add (zvrf, vni);
+ if (!zvni)
+ {
+ zlog_err ("Failed to add VNI hash, VRF %d IF %s(%u) VNI %u",
+ ifp->vrf_id, ifp->name, ifp->ifindex, vni);
+ return -1;
+ }
+ }
+
+ zvni->local_vtep_ip = vxl->vtep_ip;
+ zvni->vxlan_if = ifp;
+
+ /* If down or not mapped to a bridge, we're done. */
+ if (!if_is_operative (ifp) || !zif->brslave_info.br_if)
+ return 0;
+
+ /* Inform BGP */
+ zvni_send_add_to_client (zvrf, zvni);
+
+ /* Read and populate local MACs and neighbors */
+ zvni_read_mac_neigh (zvrf, zvni, ifp);
+
+ 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
+ * uninstalled from the kernel.
+ */
+int zebra_vxlan_advertise_all_vni (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ struct stream *s;
+ int advertise;
+
+ s = client->ibuf;
+ advertise = stream_getc (s);
+
+ if (IS_ZEBRA_DEBUG_VXLAN)
+ zlog_debug ("%u:EVPN VNI Adv %s, currently %s",
+ zvrf_id (zvrf), advertise ? "enabled" : "disabled",
+ EVPN_ENABLED(zvrf) ? "enabled" : "disabled");
+
+ if (zvrf->advertise_all_vni == advertise)
+ return 0;
+
+ zvrf->advertise_all_vni = advertise;
+ if (EVPN_ENABLED(zvrf))
+ {
+ /* Build VNI hash table and inform BGP. */
+ zvni_build_hash_table (zvrf);
+
+ /* Read the MAC FDB */
+ macfdb_read (zvrf->zns);
+
+ /* Read neighbors */
+ neigh_read (zvrf->zns);
+ }
+ else
+ {
+ /* Cleanup VTEPs for all VNIs - uninstall from
+ * kernel and free entries.
+ */
+ hash_iterate (zvrf->vni_table, zvni_cleanup_all, zvrf);
+ }
+
+ return 0;
+}
+
+/*
+ * Allocate VNI hash table for this VRF and do other initialization.
+ * NOTE: Currently supported only for default VRF.
+ */
+void
+zebra_vxlan_init_tables (struct zebra_vrf *zvrf)
+{
+ if (!zvrf)
+ return;
+ zvrf->vni_table = hash_create(vni_hash_keymake,
+ vni_hash_cmp,
+ "Zebra VRF VNI Table");
+}
+
+/* Close all VNI handling */
+void
+zebra_vxlan_close_tables (struct zebra_vrf *zvrf)
+{
+ hash_iterate (zvrf->vni_table, zvni_cleanup_all, zvrf);
+}
diff --git a/zebra/zebra_vxlan.h b/zebra/zebra_vxlan.h
new file mode 100644
index 000000000..e5f0bd2f6
--- /dev/null
+++ b/zebra/zebra_vxlan.h
@@ -0,0 +1,108 @@
+/*
+ * Zebra VxLAN (EVPN) Data structures and definitions
+ * These are public definitions referenced by other files.
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VXLAN_H
+#define _ZEBRA_VXLAN_H
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "if.h"
+#include "vlan.h"
+#include "vxlan.h"
+
+#include "zebra/zebra_vrf.h"
+
+/* Is EVPN enabled? */
+#define EVPN_ENABLED(zvrf) (zvrf)->advertise_all_vni
+
+/* VxLAN interface change flags of interest. */
+#define ZEBRA_VXLIF_LOCAL_IP_CHANGE 0x1
+#define ZEBRA_VXLIF_MASTER_CHANGE 0x2
+#define ZEBRA_VXLIF_VLAN_CHANGE 0x4
+
+extern void zebra_vxlan_print_macs_vni (struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni);
+extern void zebra_vxlan_print_macs_all_vni (struct vty *vty,
+ struct zebra_vrf *zvrf);
+extern void zebra_vxlan_print_macs_all_vni_vtep (struct vty *vty,
+ struct zebra_vrf *zvrf,
+ struct in_addr vtep_ip);
+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);
+extern void zebra_vxlan_print_neigh_vni (struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni);
+extern void zebra_vxlan_print_neigh_all_vni (struct vty *vty,
+ struct zebra_vrf *zvrf);
+extern void zebra_vxlan_print_specific_neigh_vni (struct vty *vty,
+ struct zebra_vrf *zvrf, vni_t vni, struct ipaddr *ip);
+extern void zebra_vxlan_print_neigh_vni_vtep (struct vty *vty,
+ struct zebra_vrf *zvrf, vni_t vni, struct in_addr vtep_ip);
+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);
+
+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);
+extern int zebra_vxlan_local_neigh_add_update (struct interface *ifp,
+ struct interface *link_if,
+ struct ipaddr *ip,
+ struct ethaddr *macaddr,
+ u_int16_t state,
+ u_char ext_learned);
+extern int zebra_vxlan_local_neigh_del (struct interface *ifp,
+ struct interface *link_if,
+ struct ipaddr *ip);
+extern int zebra_vxlan_remote_macip_add (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf);
+extern int zebra_vxlan_remote_macip_del (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf);
+extern int zebra_vxlan_local_mac_add_update (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *mac, vlanid_t vid,
+ u_char sticky);
+extern int zebra_vxlan_local_mac_del (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *mac, vlanid_t vid);
+extern int zebra_vxlan_check_readd_remote_mac (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *mac, vlanid_t vid);
+extern int zebra_vxlan_check_del_local_mac (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *mac, vlanid_t vid);
+extern int zebra_vxlan_if_up (struct interface *ifp);
+extern int zebra_vxlan_if_down (struct interface *ifp);
+extern int zebra_vxlan_if_add (struct interface *ifp);
+extern int zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags);
+extern int zebra_vxlan_if_del (struct interface *ifp);
+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_all_vni (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf);
+extern void zebra_vxlan_init_tables (struct zebra_vrf *zvrf);
+extern void zebra_vxlan_close_tables (struct zebra_vrf *);
+
+#endif /* _ZEBRA_VXLAN_H */
diff --git a/zebra/zebra_vxlan_null.c b/zebra/zebra_vxlan_null.c
new file mode 100644
index 000000000..b0f09930b
--- /dev/null
+++ b/zebra/zebra_vxlan_null.c
@@ -0,0 +1,197 @@
+/*
+ * Zebra VxLAN (EVPN)
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+
+#include "if.h"
+#include "zebra/debug.h"
+#include "zebra/zserv.h"
+#include "zebra/rib.h"
+#include "zebra/zebra_vrf.h"
+#include "zebra/zebra_l2.h"
+#include "zebra/zebra_vxlan.h"
+
+void
+zebra_vxlan_print_macs_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni)
+{
+}
+
+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)
+{
+}
+
+void
+zebra_vxlan_print_specific_mac_vni (struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni, struct ethaddr *mac)
+{
+}
+
+void
+zebra_vxlan_print_macs_vni_vtep (struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni, struct in_addr vtep_ip)
+{
+}
+
+void
+zebra_vxlan_print_neigh_vni (struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni)
+{
+}
+
+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)
+{
+}
+
+void zebra_vxlan_print_neigh_vni_vtep (struct vty *vty, struct zebra_vrf *zvrf,
+ vni_t vni, struct in_addr vtep_ip)
+{
+}
+
+void
+zebra_vxlan_print_vni (struct vty *vty, struct zebra_vrf *zvrf, vni_t vni)
+{
+}
+
+void
+zebra_vxlan_print_vnis (struct vty *vty, struct zebra_vrf *zvrf)
+{
+}
+
+int zebra_vxlan_svi_up (struct interface *ifp, struct interface *link_if)
+{
+ return 0;
+}
+
+int zebra_vxlan_svi_down (struct interface *ifp, struct interface *link_if)
+{
+ return 0;
+}
+
+int zebra_vxlan_remote_macip_add (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ return 0;
+}
+
+int zebra_vxlan_remote_macip_del (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ return 0;
+}
+
+int zebra_vxlan_local_mac_add_update (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *mac, vlanid_t vid,
+ u_char sticky)
+{
+ return 0;
+}
+
+int zebra_vxlan_local_mac_del (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *mac, vlanid_t vid)
+{
+ return 0;
+}
+
+int zebra_vxlan_check_readd_remote_mac (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *mac, vlanid_t vid)
+{
+ return 0;
+}
+
+int zebra_vxlan_check_del_local_mac (struct interface *ifp,
+ struct interface *br_if,
+ struct ethaddr *mac, vlanid_t vid)
+{
+ return 0;
+}
+
+int
+zebra_vxlan_if_up (struct interface *ifp)
+{
+ return 0;
+}
+
+int
+zebra_vxlan_if_down (struct interface *ifp)
+{
+ return 0;
+}
+
+int
+zebra_vxlan_if_add (struct interface *ifp)
+{
+ return 0;
+}
+
+int
+zebra_vxlan_if_update (struct interface *ifp, u_int16_t chgflags)
+{
+ return 0;
+}
+
+int
+zebra_vxlan_if_del (struct interface *ifp)
+{
+ return 0;
+}
+
+int zebra_vxlan_remote_vtep_add (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ return 0;
+}
+
+int zebra_vxlan_remote_vtep_del (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ return 0;
+}
+
+int zebra_vxlan_advertise_all_vni (struct zserv *client, int sock,
+ u_short length, struct zebra_vrf *zvrf)
+{
+ return 0;
+}
+
+void
+zebra_vxlan_init_tables (struct zebra_vrf *zvrf)
+{
+}
+
+void
+zebra_vxlan_close_tables (struct zebra_vrf *zvrf)
+{
+}
diff --git a/zebra/zebra_vxlan_private.h b/zebra/zebra_vxlan_private.h
new file mode 100644
index 000000000..0de86c3bc
--- /dev/null
+++ b/zebra/zebra_vxlan_private.h
@@ -0,0 +1,199 @@
+/*
+ * Zebra VxLAN (EVPN) Data structures and definitions
+ * These are "internal" to this function.
+ * Copyright (C) 2016, 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _ZEBRA_VXLAN_PRIVATE_H
+#define _ZEBRA_VXLAN_PRIVATE_H
+
+#include <zebra.h>
+
+#include <zebra.h>
+
+#include "if.h"
+#include "linklist.h"
+
+/* definitions */
+typedef struct zebra_vni_t_ zebra_vni_t;
+typedef struct zebra_vtep_t_ zebra_vtep_t;
+typedef struct zebra_mac_t_ zebra_mac_t;
+typedef struct zebra_neigh_t_ zebra_neigh_t;
+
+/*
+ * VTEP info
+ *
+ * Right now, this just has each remote VTEP's IP address.
+ */
+struct zebra_vtep_t_
+{
+ /* Remote IP. */
+ /* NOTE: Can only be IPv4 right now. */
+ struct in_addr vtep_ip;
+
+ /* Links. */
+ struct zebra_vtep_t_ *next;
+ struct zebra_vtep_t_ *prev;
+};
+
+
+/*
+ * VNI hash table
+ *
+ * Contains information pertaining to a VNI:
+ * - the list of remote VTEPs (with this VNI)
+ */
+struct zebra_vni_t_
+{
+ /* VNI - key */
+ vni_t vni;
+
+ /* Corresponding VxLAN interface. */
+ struct interface *vxlan_if;
+
+ /* List of remote VTEPs */
+ zebra_vtep_t *vteps;
+
+ /* Local IP */
+ struct in_addr local_vtep_ip;
+
+ /* List of local or remote MAC */
+ struct hash *mac_table;
+
+ /* List of local or remote neighbors (MAC+IP) */
+ struct hash *neigh_table;
+};
+
+/*
+ * MAC hash table.
+ *
+ * This table contains the MAC addresses pertaining to this VNI.
+ * This includes local MACs learnt on an attached VLAN that maps
+ * to this VNI as well as remote MACs learnt and installed by BGP.
+ * Local MACs will be known either on a VLAN sub-interface or
+ * on (port, VLAN); however, it is sufficient for zebra to maintain
+ * against the VNI i.e., it does not need to retain the local "port"
+ * information. The correct VNI will be obtained as zebra maintains
+ * the mapping (of VLAN to VNI).
+ */
+struct zebra_mac_t_
+{
+ /* MAC address. */
+ struct ethaddr macaddr;
+
+ u_int32_t flags;
+#define ZEBRA_MAC_LOCAL 0x01
+#define ZEBRA_MAC_REMOTE 0x02
+#define ZEBRA_MAC_AUTO 0x04 /* Auto created for neighbor. */
+#define ZEBRA_MAC_STICKY 0x08 /* Static MAC */
+
+ /* Local or remote info. */
+ union
+ {
+ struct
+ {
+ ifindex_t ifindex;
+ vlanid_t vid;
+ } local;
+
+ struct in_addr r_vtep_ip;
+ } fwd_info;
+
+ u_int32_t neigh_refcnt;
+};
+
+/*
+ * Context for MAC hash walk - used by callbacks.
+ */
+struct mac_walk_ctx
+{
+ zebra_vni_t *zvni; /* VNI hash */
+ struct zebra_vrf *zvrf; /* VRF - for client notification. */
+ int uninstall; /* uninstall from kernel? */
+ int upd_client; /* uninstall from client? */
+
+ u_int32_t flags;
+#define DEL_LOCAL_MAC 0x1
+#define DEL_REMOTE_MAC 0x2
+#define DEL_ALL_MAC (DEL_LOCAL_MAC | DEL_REMOTE_MAC)
+#define DEL_REMOTE_MAC_FROM_VTEP 0x4
+#define SHOW_REMOTE_MAC_FROM_VTEP 0x8
+
+ 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 */
+};
+
+/*
+ * Neighbor hash table.
+ *
+ * This table contains the neighbors (IP to MAC bindings) pertaining to
+ * this VNI. This includes local neighbors learnt on the attached VLAN
+ * device that maps to this VNI as well as remote neighbors learnt and
+ * installed by BGP.
+ * Local neighbors will be known against the VLAN device (SVI); however,
+ * it is sufficient for zebra to maintain against the VNI. The correct
+ * VNI will be obtained as zebra maintains the mapping (of VLAN to VNI).
+ */
+struct zebra_neigh_t_
+{
+ /* IP address. */
+ struct ipaddr ip;
+
+ /* MAC address. */
+ struct ethaddr emac;
+
+ /* Underlying interface. */
+ ifindex_t ifindex;
+
+ u_int32_t flags;
+#define ZEBRA_NEIGH_LOCAL 0x01
+#define ZEBRA_NEIGH_REMOTE 0x02
+
+ /* Remote VTEP IP - applicable only for remote neighbors. */
+ struct in_addr r_vtep_ip;
+};
+
+/*
+ * Context for neighbor hash walk - used by callbacks.
+ */
+struct neigh_walk_ctx
+{
+ zebra_vni_t *zvni; /* VNI hash */
+ struct zebra_vrf *zvrf; /* VRF - for client notification. */
+ int uninstall; /* uninstall from kernel? */
+ int upd_client; /* uninstall from client? */
+
+ u_int32_t flags;
+#define DEL_LOCAL_NEIGH 0x1
+#define DEL_REMOTE_NEIGH 0x2
+#define DEL_ALL_NEIGH (DEL_LOCAL_NEIGH | DEL_REMOTE_NEIGH)
+#define DEL_REMOTE_NEIGH_FROM_VTEP 0x4
+#define SHOW_REMOTE_NEIGH_FROM_VTEP 0x8
+
+ 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 */
+};
+
+#endif /* _ZEBRA_VXLAN_PRIVATE_H */
diff --git a/zebra/zserv.c b/zebra/zserv.c
index e3c8ea0dd..15feb78c0 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -54,6 +54,7 @@
#include "zebra/zebra_mpls.h"
#include "zebra/zebra_mroute.h"
#include "zebra/label_manager.h"
+#include "zebra/zebra_vxlan.h"
/* Event list of zebra. */
enum event { ZEBRA_SERV, ZEBRA_READ, ZEBRA_WRITE };
@@ -2434,6 +2435,21 @@ zebra_client_read (struct thread *thread)
case ZEBRA_FEC_UNREGISTER:
zserv_fec_unregister (client, sock, length);
break;
+ case ZEBRA_ADVERTISE_ALL_VNI:
+ zebra_vxlan_advertise_all_vni (client, sock, length, zvrf);
+ break;
+ case ZEBRA_REMOTE_VTEP_ADD:
+ zebra_vxlan_remote_vtep_add (client, sock, length, zvrf);
+ break;
+ case ZEBRA_REMOTE_VTEP_DEL:
+ zebra_vxlan_remote_vtep_del (client, sock, length, zvrf);
+ break;
+ case ZEBRA_REMOTE_MACIP_ADD:
+ zebra_vxlan_remote_macip_add (client, sock, length, zvrf);
+ break;
+ case ZEBRA_REMOTE_MACIP_DEL:
+ zebra_vxlan_remote_macip_del (client, sock, length, zvrf);
+ break;
default:
zlog_info ("Zebra received unknown command %d", command);
break;
@@ -2725,6 +2741,10 @@ zebra_show_client_detail (struct vty *vty, struct zserv *client)
client->bfd_peer_upd8_cnt, client->bfd_peer_del_cnt);
vty_out (vty, "Interface Up Notifications: %d\n",client->ifup_cnt);
vty_out (vty, "Interface Down Notifications: %d\n",client->ifdown_cnt);
+ vty_out (vty, "VNI add notifications: %d\n", client->vniadd_cnt);
+ vty_out (vty, "VNI delete notifications: %d\n", client->vnidel_cnt);
+ vty_out (vty, "MAC-IP add notifications: %d\n", client->macipadd_cnt);
+ vty_out (vty, "MAC-IP delete notifications: %d\n", client->macipdel_cnt);
vty_out (vty, "\n");
return;
diff --git a/zebra/zserv.h b/zebra/zserv.h
index dcc98d83f..2d6f6fae7 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -105,6 +105,10 @@ struct zserv
u_int32_t vrfdel_cnt;
u_int32_t if_vrfchg_cnt;
u_int32_t bfd_client_reg_cnt;
+ u_int32_t vniadd_cnt;
+ u_int32_t vnidel_cnt;
+ u_int32_t macipadd_cnt;
+ u_int32_t macipdel_cnt;
time_t connect_time;
time_t last_read_time;
@@ -145,6 +149,11 @@ extern void hostinfo_get (void);
extern void rib_init (void);
extern void interface_list (struct zebra_ns *);
extern void route_read (struct zebra_ns *);
+extern void macfdb_read (struct zebra_ns *);
+extern void macfdb_read_for_bridge (struct zebra_ns *, struct interface *,
+ struct interface *);
+extern void neigh_read (struct zebra_ns *);
+extern void neigh_read_for_vlan (struct zebra_ns *, struct interface *);
extern void kernel_init (struct zebra_ns *);
extern void kernel_terminate (struct zebra_ns *);
extern void zebra_route_map_init (void);