diff options
author | Russ White <russ@riw.us> | 2017-07-14 15:36:31 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-14 15:36:31 +0200 |
commit | 805d1ca6b06158ce9f31f77867ef9d293744a95f (patch) | |
tree | 855a3dca8ef223561892a62f8ef3adef89ff84eb | |
parent | Merge pull request #813 from opensourcerouting/newline-redux (diff) | |
parent | bgpd, zebra: Cleanup warnings from new code (diff) | |
download | frr-805d1ca6b06158ce9f31f77867ef9d293744a95f.tar.xz frr-805d1ca6b06158ce9f31f77867ef9d293744a95f.zip |
Merge pull request #809 from donaldsharp/evpn_plus_struct_attr
Evpn plus struct attr
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 (ð_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, <, 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 @@ -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 @@ -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); |