]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
EVPN: VLAN support - temporary commit
authorOndrej Zajicek <santiago@crfreenet.org>
Fri, 14 Jun 2024 16:50:36 +0000 (18:50 +0200)
committerOndrej Zajicek <santiago@crfreenet.org>
Sun, 15 Jun 2025 00:56:35 +0000 (02:56 +0200)
lib/evpn.h
proto/evpn/config.Y
proto/evpn/evpn.c
proto/evpn/evpn.h

index c340c38566fe3ff7b2b8b4910274e869afad105c..e0643e79afc8e7dea890dccacefb569c3ddae717 100644 (file)
@@ -24,6 +24,10 @@ enum evpn_esi_type {
   EVPN_ESI_MAX
 };
 
+#define EVPN_TAG_MAX   0xffffffff
+#define EVPN_VNI_MAX   0x00ffffff
+#define EVPN_VID_MAX   0x00000fff
+
 typedef struct evpn_esi {
   u8 type;
   u8 value[9];
index 06cb5333bc32e2e66eb8fca3041efbb70ac7ea60..443a55e8d26214064bcaa6b36b0b6bb6ef77b1b7 100644 (file)
@@ -14,12 +14,14 @@ CF_HDR
 
 CF_DEFINES
 
+static struct evpn_vlan_config *this_evpn_vlan;
+
 #define EVPN_CFG ((struct evpn_config *) this_proto)
 
 
 CF_DECLS
 
-CF_KEYWORDS(EVPN, ROUTE, IMPORT, EXPORT, TARGET, RD, DISTINGUISHER, TUNNEL, DEVICE, VNI, VID)
+CF_KEYWORDS(EVPN, ROUTE, IMPORT, EXPORT, TARGET, RD, DISTINGUISHER, TUNNEL, DEVICE, VNI, VID, VLAN, RANGE)
 
 %type <e> evpn_targets
 %type <cc> evpn_channel_start evpn_channel
@@ -48,6 +50,7 @@ evpn_channel: evpn_channel_start channel_opt_list channel_end;
 evpn_proto_start: proto_start EVPN
 {
   this_proto = proto_config_new(&proto_evpn, $1);
+  init_list(&EVPN_CFG->vlans);
 };
 
 
@@ -64,6 +67,8 @@ evpn_proto_item:
  | ROUTER ADDRESS ipa { EVPN_CFG->router_addr = $3; }
  | VNI expr { EVPN_CFG->vni = $2; }
  | VID expr { EVPN_CFG->vid = $2; if ($2 > 4095) cf_error("VID must be in range 0-4095"); }
+ | TAG expr { EVPN_CFG->tagX = $2; }
+ | evpn_vlan
  ;
 
 evpn_proto_opts:
@@ -81,6 +86,57 @@ evpn_targets:
  ;
 
 
+evpn_vlan: evpn_vlan_start evpn_vlan_opt_list evpn_vlan_end;
+
+evpn_vlan_start: VLAN expr
+{
+  this_evpn_vlan = cfg_allocz(sizeof(struct evpn_vlan_config));
+  add_tail(&EVPN_CFG->vlans, &this_evpn_vlan->n);
+  this_evpn_vlan->id = $2;
+  this_evpn_vlan->range = 1;
+  this_evpn_vlan->vni = (u32) -1; /* undefined */
+  this_evpn_vlan->vid = (u32) -1; /* undefined */
+}
+
+evpn_vlan_opt:
+   RANGE expr { this_evpn_vlan->range = $2; }
+ | VNI expr { this_evpn_vlan->vni = $2; if ($2 > EVPN_VNI_MAX) cf_error("VNI must be less than 2^24"); }
+ | VID expr { this_evpn_vlan->vid = $2; if ($2 > EVPN_VID_MAX) cf_error("VID must be less than 2^12"); }
+ ;
+
+evpn_vlan_opts:
+   /* empty */
+ | evpn_vlan_opts evpn_vlan_opt ';'
+ ;
+
+evpn_vlan_opt_list:
+   /* empty */
+ | '{' evpn_vlan_opts '}'
+ ;
+
+evpn_vlan_end:
+{
+  struct evpn_vlan_config *vc = this_evpn_vlan;
+
+  if (vc->vni == (u32) -1)
+    vc->vni = vc->id;
+
+  if (vc->vid == (u32) -1)
+    vc->vid = vc->id;
+
+  if (((u64) vc->id + vc->range - 1) > EVPN_TAG_MAX)
+    cf_error("Last tag in range must be less than 2^32");
+
+  if (((u64) vc->vni + vc->range - 1) > EVPN_VNI_MAX)
+    cf_error("Last VNI in range must be less than 2^24");
+
+  if (((u64) vc->vid + vc->range - 1) > EVPN_VID_MAX)
+    cf_error("Last VID in range must be less than 2^12");
+
+  this_evpn_vlan = NULL;
+}
+
+
 CF_CODE
 
 CF_END
index 4f204447be0436bd55d336ff6622a0a7dbc217a7..32fa12c8b7afaf3adff2c6480228aee05d782980 100644 (file)
 
 #undef LOCAL_DEBUG
 
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdlib.h>
+
 #include "nest/bird.h"
 #include "nest/iface.h"
 #include "nest/protocol.h"
 static inline const struct adata * ea_get_adata(ea_list *e, uint id)
 { eattr *a = ea_find(e, id); return a ? a->u.ptr : &null_adata; }
 
-static inline int
-mpls_valid_nexthop(const rta *a)
-{
-  /* MPLS does not support special blackhole targets */
-  if (a->dest != RTD_UNICAST)
-    return 0;
 
-  /* MPLS does not support ARP / neighbor discovery */
-  for (const struct nexthop *nh = &a->nh; nh ; nh = nh->next)
-    if (ipa_zero(nh->gw) && (nh->iface->flags & IF_MULTIACCESS))
-      return 0;
+static struct evpn_vlan *evpn_find_vlan_by_tag(struct evpn_proto *p, u32 tag);
+static struct evpn_vlan *evpn_find_vlan_by_vid(struct evpn_proto *p, u32 vid);
+
+#define EVPN_ROOT_VLAN(P) \
+  ( &(struct evpn_vlan){ .tag = (P)->tagX, .vni = (P)->vni, .vid = (P)->vid } )
+
+#define evpn_get_vlan_by_tag(P,TAG) \
+  ( evpn_find_vlan_by_tag((P), (TAG)) ?: EVPN_ROOT_VLAN(P) )
+
+#define evpn_get_vlan_by_vid(P,VID) \
+  ( evpn_find_vlan_by_vid((P), (VID)) ?: EVPN_ROOT_VLAN(P) )
 
-  return 1;
-}
 
 static int
 evpn_import_targets(struct evpn_proto *p, const struct adata *list)
@@ -148,9 +153,10 @@ static void
 evpn_announce_mac(struct evpn_proto *p, const net_addr_eth *n0, rte *new)
 {
   struct channel *c = p->evpn_channel;
+  struct evpn_vlan *v = evpn_get_vlan_by_vid(p, n0->vid);
 
   net_addr *n = alloca(sizeof(net_addr_evpn_mac));
-  net_fill_evpn_mac(n, p->rd, 0, n0->mac);
+  net_fill_evpn_mac(n, p->rd, v->tag, n0->mac);
 
   if (new)
   {
@@ -164,7 +170,7 @@ evpn_announce_mac(struct evpn_proto *p, const net_addr_eth *n0, rte *new)
     struct adata *ad = evpn_export_targets(p, &null_adata);
     ea_set_attr_ptr(&a->eattrs, tmp_linpool, EA_BGP_EXT_COMMUNITY, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_EC_SET, ad);
 
-    ea_set_attr_u32(&a->eattrs, tmp_linpool, EA_MPLS_LABEL, 0, EAF_TYPE_INT, p->vni);
+    ea_set_attr_u32(&a->eattrs, tmp_linpool, EA_MPLS_LABEL, 0, EAF_TYPE_INT, v->vni);
 
     rte *e = rte_get_temp(a, p->p.main_source);
     rte_update2(c, n, e, p->p.main_source);
@@ -176,12 +182,12 @@ evpn_announce_mac(struct evpn_proto *p, const net_addr_eth *n0, rte *new)
 }
 
 static void
-evpn_announce_imet(struct evpn_proto *p, int new)
+evpn_announce_imet(struct evpn_proto *p, struct evpn_vlan *v, int new)
 {
   struct channel *c = p->evpn_channel;
 
   net_addr *n = alloca(sizeof(net_addr_evpn_imet));
-  net_fill_evpn_imet(n, p->rd, 0, p->router_addr);
+  net_fill_evpn_imet(n, p->rd, v->tag, p->router_addr);
 
   if (new)
   {
@@ -195,7 +201,7 @@ evpn_announce_imet(struct evpn_proto *p, int new)
     struct adata *ad = evpn_export_targets(p, &null_adata);
     ea_set_attr_ptr(&a->eattrs, tmp_linpool, EA_BGP_EXT_COMMUNITY, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_EC_SET, ad);
 
-    ad = bgp_pmsi_new_ingress_replication(tmp_linpool, p->router_addr, p->vni);
+    ad = bgp_pmsi_new_ingress_replication(tmp_linpool, p->router_addr, v->vni);
     ea_set_attr_ptr(&a->eattrs, tmp_linpool, EA_BGP_PMSI_TUNNEL, BAF_OPTIONAL | BAF_TRANSITIVE, EAF_TYPE_OPAQUE, ad);
 
     rte *e = rte_get_temp(a, p->p.main_source);
@@ -215,9 +221,10 @@ static void
 evpn_receive_mac(struct evpn_proto *p, const net_addr_evpn_mac *n0, rte *new)
 {
   struct channel *c = p->eth_channel;
+  struct evpn_vlan *v = evpn_get_vlan_by_tag(p, n0->tag);
 
   net_addr *n = alloca(sizeof(net_addr_eth));
-  net_fill_eth(n, n0->mac, p->vid);
+  net_fill_eth(n, n0->mac, v->vid);
 
   if (new && rte_resolvable(new))
   {
@@ -243,7 +250,7 @@ evpn_receive_mac(struct evpn_proto *p, const net_addr_evpn_mac *n0, rte *new)
     memcpy(a->nh.label, ms->u.ptr->data, a->nh.labels * 4);
 
     /* Hack to handle src_vni in bridge code */
-    ea_set_attr_u32(&a->eattrs, tmp_linpool, EA_MPLS_LABEL, 0, EAF_TYPE_INT, p->vni);
+    ea_set_attr_u32(&a->eattrs, tmp_linpool, EA_MPLS_LABEL, 0, EAF_TYPE_INT, v->vni);
 
     rte *e = rte_get_temp(a, p->p.main_source);
     rte_update2(c, n, e, p->p.main_source);
@@ -260,9 +267,10 @@ evpn_receive_imet(struct evpn_proto *p, const net_addr_evpn_imet *n0, rte *new)
 {
   struct channel *c = p->eth_channel;
   struct rte_src *s = rt_get_source(&p->p, rd_to_u64(n0->rd));
+  struct evpn_vlan *v = evpn_get_vlan_by_tag(p, n0->tag);
 
   net_addr *n = alloca(sizeof(net_addr_eth));
-  net_fill_eth(n, MAC_NONE, p->vid);
+  net_fill_eth(n, MAC_NONE, v->vid);
 
   if (new && rte_resolvable(new))
   {
@@ -288,7 +296,7 @@ evpn_receive_imet(struct evpn_proto *p, const net_addr_evpn_imet *n0, rte *new)
     a->nh.label[0] = bgp_pmsi_get_label(pt->u.ptr);
 
     /* Hack to handle src_vni in bridge code */
-    ea_set_attr_u32(&a->eattrs, tmp_linpool, EA_MPLS_LABEL, 0, EAF_TYPE_INT, p->vni);
+    ea_set_attr_u32(&a->eattrs, tmp_linpool, EA_MPLS_LABEL, 0, EAF_TYPE_INT, v->vni);
 
     rte *e = rte_get_temp(a, s);
     rte_update2(c, n, e, s);
@@ -301,7 +309,6 @@ evpn_receive_imet(struct evpn_proto *p, const net_addr_evpn_imet *n0, rte *new)
 }
 
 
-
 static void
 evpn_rt_notify(struct proto *P, struct channel *c0 UNUSED, net *net, rte *new, rte *old UNUSED)
 {
@@ -346,13 +353,27 @@ evpn_preexport(struct channel *C, rte *e)
   switch (n->type)
   {
   case NET_ETH:
-    if (((const net_addr_eth *) n)->vid != p->vid)
+  {
+    u32 vid = ((const net_addr_eth *) n)->vid;
+
+    if ((vid != p->vid) && !evpn_find_vlan_by_vid(p, vid))
       return -1;
 
     return 0;
+  }
 
   case NET_EVPN:
-    return evpn_import_targets(p, ea_get_adata(e->attrs->eattrs, EA_BGP_EXT_COMMUNITY)) ? 0 : -1;
+  {
+    u32 tag = ((const net_addr_evpn *) n)->tag;
+
+    if (!evpn_import_targets(p, ea_get_adata(e->attrs->eattrs, EA_BGP_EXT_COMMUNITY)))
+      return -1;
+
+    if ((tag != p->tagX) && !evpn_find_vlan_by_tag(p, tag))
+      return -1;
+
+    return 0;
+  }
 
   case NET_MPLS:
     return -1;
@@ -384,6 +405,34 @@ evpn_reload_routes(struct channel *C)
   }
 }
 
+static void
+evpn_feed_end(struct channel *C)
+{
+  struct evpn_proto *p = (void *) C->proto;
+
+  switch (C->net_type)
+  {
+  case NET_ETH:
+    if (p->evpn_refreshing)
+    {
+      rt_refresh_end(p->evpn_channel->table, p->evpn_channel);
+      p->evpn_refreshing = false;
+    }
+    break;
+
+  case NET_EVPN:
+    if (p->eth_refreshing)
+    {
+      rt_refresh_end(p->eth_channel->table, p->eth_channel);
+      p->eth_refreshing = false;
+    }
+    break;
+
+  case NET_MPLS:
+    break;
+  }
+}
+
 static inline u32
 evpn_metric(rte *e)
 {
@@ -398,6 +447,225 @@ evpn_rte_better(rte *new, rte *old)
   return evpn_metric(new) < evpn_metric(old);
 }
 
+
+/*
+ *     EVPN VLANs
+ */
+
+#define VLAN_TAG_KEY(v)                v->tag
+#define VLAN_TAG_NEXT(v)       v->next_tag
+#define VLAN_TAG_EQ(v1,v2)     v1 == v2
+#define VLAN_TAG_FN(v)         u32_hash(v)
+
+#define VLAN_TAG_REHASH                evpn_tag_rehash
+#define VLAN_TAG_PARAMS                /8, *2, 2, 2, 4, 24
+
+
+#define VLAN_VID_KEY(v)                v->vid
+#define VLAN_VID_NEXT(v)       v->next_vid
+#define VLAN_VID_EQ(v1,v2)     v1 == v2
+#define VLAN_VID_FN(v)         u32_hash(v)
+
+#define VLAN_VID_REHASH                evpn_vid_rehash
+#define VLAN_VID_PARAMS                /8, *2, 2, 2, 4, 16
+
+
+HASH_DEFINE_REHASH_FN(VLAN_TAG, struct evpn_vlan)
+HASH_DEFINE_REHASH_FN(VLAN_VID, struct evpn_vlan)
+
+
+static struct evpn_vlan *
+evpn_new_vlan(struct evpn_proto *p, struct evpn_vlan_config *cf, uint index)
+{
+  struct evpn_vlan *v = mb_allocz(p->p.pool, sizeof(struct evpn_vlan));
+
+  v->tag = cf->id + index;
+  v->vni = cf->vni + index;
+  v->vid = cf->vid + index;
+
+  if (!p->vlan_tag_hash.data)
+    HASH_INIT(p->vlan_tag_hash, p->p.pool, 4);
+
+  if (!p->vlan_vid_hash.data)
+    HASH_INIT(p->vlan_vid_hash, p->p.pool, 4);
+
+  add_tail(&p->vlans, &v->n);
+  HASH_INSERT2(p->vlan_tag_hash, VLAN_TAG, p->p.pool, v);
+  HASH_INSERT2(p->vlan_vid_hash, VLAN_VID, p->p.pool, v);
+
+  return v;
+}
+
+static struct evpn_vlan *
+evpn_find_vlan_by_tag(struct evpn_proto *p, u32 tag)
+{
+  return p->vlan_tag_hash.data ? HASH_FIND(p->vlan_tag_hash, VLAN_TAG, tag) : NULL;
+}
+
+static struct evpn_vlan *
+evpn_find_vlan_by_vid(struct evpn_proto *p, u32 vid)
+{
+  return p->vlan_vid_hash.data ? HASH_FIND(p->vlan_vid_hash, VLAN_VID, vid) : NULL;
+}
+
+static void
+evpn_remove_vlan(struct evpn_proto *p, struct evpn_vlan *v)
+{
+  rem_node(&v->n);
+  HASH_REMOVE2(p->vlan_tag_hash, VLAN_TAG, p->p.pool, v);
+  HASH_REMOVE2(p->vlan_vid_hash, VLAN_VID, p->p.pool, v);
+
+  mb_free(v);
+}
+
+static inline int
+evpn_reconfigure_vlan(struct evpn_proto *p UNUSED, struct evpn_vlan *v, struct evpn_vlan_config *cf, uint index)
+{
+  return
+    (v->vni == cf->vni + index) &&
+    (v->vid == cf->vid + index);
+}
+
+static int
+evpn_reconfigure_vlans(struct evpn_proto *p, struct evpn_config *cf)
+{
+  list old_vlans;
+  init_list(&old_vlans);
+  add_tail_list(&old_vlans, &p->vlans);
+  init_list(&p->vlans);
+  int changed = 0;
+
+  struct evpn_vlan_config *vc;
+  WALK_LIST(vc, cf->vlans)
+    for (uint i = 0; i < vc->range; i++)
+    {
+      struct evpn_vlan *v = evpn_find_vlan_by_tag(p, vc->id + i);
+
+      if (v && evpn_reconfigure_vlan(p, v, vc, i))
+      {
+       rem_node(&v->n);
+       add_tail(&p->vlans, &v->n);
+       continue;
+      }
+
+      if (v)
+       evpn_remove_vlan(p, v);
+
+      v = evpn_new_vlan(p, vc, i);
+      // evpn_announce_imet(p, v, 1);
+      changed = 1;
+    }
+
+  struct evpn_vlan *v, *v2;
+  WALK_LIST_DELSAFE(v, v2, old_vlans)
+  {
+    // evpn_announce_imet(p, v, 0);
+    evpn_remove_vlan(p, v);
+    changed = 1;
+  }
+
+  if (changed)
+  {
+    TRACE(D_EVENTS, "VLANs changed");
+
+    /* Should not happend */
+    if ((p->eth_channel->channel_state != CS_UP) ||
+       (p->evpn_channel->channel_state != CS_UP))
+      return 0;
+
+    /*
+     * We hard-reload channels by switching to CS_START and CS_UP, this resets
+     * export maps so we do not get withdraws related to old exports, only
+     * updates that are processed through the new VLAN mapping. On export,
+     * rt_refresh_begin() / rt_refresh_end() is used for clean-up of old routes.
+     */
+
+    rt_refresh_begin(p->eth_channel->table, p->eth_channel);
+    p->eth_refreshing = true;
+
+    rt_refresh_begin(p->evpn_channel->table, p->evpn_channel);
+    p->evpn_refreshing = true;
+
+    channel_set_state(p->eth_channel, CS_START);
+    channel_set_state(p->eth_channel, CS_UP);
+
+    channel_set_state(p->evpn_channel, CS_START);
+    channel_set_state(p->evpn_channel, CS_UP);
+
+    WALK_LIST(v, p->vlans)
+      evpn_announce_imet(p, v, 1);
+  }
+
+  return 1;
+}
+
+static inline u32 evpn_vlan_config_field(const struct evpn_vlan_config *vc, size_t offset)
+{ return *(const u32 *)((const char *)vc + offset); }
+
+static int
+evpn_compare_vlan_configs(const void *vc1_, const void *vc2_, void *offset_)
+{
+  const struct evpn_vlan_config * const *vc1 = vc1_;
+  const struct evpn_vlan_config * const *vc2 = vc2_;
+  size_t offset = (size_t) offset_;
+
+  /* Compare tag, VNI or VID based on offset */
+  return uint_cmp(evpn_vlan_config_field(*vc1, offset),
+                 evpn_vlan_config_field(*vc2, offset));
+}
+
+static void
+evpn_check_intersections(struct evpn_vlan_config **vlans, int num_vlans, char *name, size_t offset)
+{
+  qsort_r(vlans, num_vlans, sizeof(struct evpn_vlan_config *), evpn_compare_vlan_configs, (void *) offset);
+
+  const struct evpn_vlan_config *o = NULL;
+  uint max = 0;
+
+  for (int i = 0; i < num_vlans; i++)
+  {
+    const struct evpn_vlan_config *v = vlans[i];
+    uint lo = evpn_vlan_config_field(v, offset);
+
+    if (lo < max)
+    {
+      if ((o->range == 1) && (v->range == 1))
+       cf_error("VLAN %s %u defined multiple times", name, lo);
+      else
+       cf_error("VLAN %s %u collision between vlan %u-%u and vlan %u-%u",
+                name, lo, o->id, o->id + o->range - 1, v->id, v->id + v->range - 1);
+    }
+
+    o = v;
+    max = lo + v->range;
+  }
+}
+
+static void
+evpn_postconfig_vlans(struct evpn_config *cf)
+{
+  /* VLAN non-intersection check */
+
+  int num_vlans = list_length(&cf->vlans);
+  struct evpn_vlan_config **vlans = tmp_alloc(num_vlans * sizeof(struct evpn_vlan_config *));
+
+  {
+    int i = 0;
+    struct evpn_vlan_config *vc;
+    WALK_LIST(vc, cf->vlans)
+      vlans[i++] = vc;
+  }
+
+  evpn_check_intersections(vlans, num_vlans, "tag", OFFSETOF(struct evpn_vlan_config, id));
+  evpn_check_intersections(vlans, num_vlans, "VNI", OFFSETOF(struct evpn_vlan_config, vni));
+  evpn_check_intersections(vlans, num_vlans, "VID", OFFSETOF(struct evpn_vlan_config, vid));
+}
+
+
+/*
+ *     EVPN protocol glue
+ */
+
 static void
 evpn_postconfig(struct proto_config *CF)
 {
@@ -423,6 +691,8 @@ evpn_postconfig(struct proto_config *CF)
 
   if (!cf->export_target)
     cf_error("Export target not specified");
+
+  evpn_postconfig_vlans(cf);
 }
 
 static struct proto *
@@ -439,6 +709,7 @@ evpn_init(struct proto_config *CF)
   P->rt_notify = evpn_rt_notify;
   P->preexport = evpn_preexport;
   P->reload_routes = evpn_reload_routes;
+  P->feed_end = evpn_feed_end;
   P->rte_better = evpn_rte_better;
 
   return P;
@@ -459,6 +730,16 @@ evpn_start(struct proto *P)
   p->router_addr = cf->router_addr;
   p->vni = cf->vni;
   p->vid = cf->vid;
+  p->tagX = cf->tagX;
+
+  init_list(&p->vlans);
+  memset(&p->vlan_tag_hash, 0, sizeof(p->vlan_tag_hash));
+  memset(&p->vlan_vid_hash, 0, sizeof(p->vlan_vid_hash));
+
+  struct evpn_vlan_config *vc;
+  WALK_LIST(vc, cf->vlans)
+    for (uint i = 0; i < vc->range; i++)
+      evpn_new_vlan(p, vc, i);
 
   evpn_prepare_import_targets(p);
   evpn_prepare_export_targets(p);
@@ -471,7 +752,11 @@ evpn_start(struct proto *P)
 
   proto_notify_state(P, PS_UP);
 
-  evpn_announce_imet(p, 1);
+  evpn_announce_imet(p, EVPN_ROOT_VLAN(p), 1);
+
+  struct evpn_vlan *v;
+  WALK_LIST(v, p->vlans)
+    evpn_announce_imet(p, v, 1);
 
   return PS_UP;
 }
@@ -519,7 +804,7 @@ evpn_reconfigure(struct proto *P, struct proto_config *CF)
 
     evpn_prepare_import_targets(p);
 
-    if (p->evpn_channel && (p->evpn_channel->channel_state == CS_UP))
+    if (p->evpn_channel->channel_state == CS_UP)
       channel_request_feeding(p->evpn_channel);
   }
 
@@ -529,10 +814,13 @@ evpn_reconfigure(struct proto *P, struct proto_config *CF)
 
     evpn_prepare_export_targets(p);
 
-    if (p->eth_channel && (p->eth_channel->channel_state == CS_UP))
+    if (p->eth_channel->channel_state == CS_UP)
       channel_request_feeding(p->eth_channel);
   }
 
+  if (!evpn_reconfigure_vlans(p, cf))
+    return 0;
+
   return 1;
 }
 
index d2739bcf0ba7e26a4dc91cd526067c443d244727..a4633c12378588cabdd99dafb3b14d1045242449 100644 (file)
@@ -21,6 +21,18 @@ struct evpn_config {
   ip_addr router_addr;
   u32 vni;
   u32 vid;
+  u32 tagX;
+
+  list vlans;                          /* List of VLANs (struct evpn_vlan_config) */
+};
+
+struct evpn_vlan_config {
+  node n;                              /* Node in evpn_config.vlans */
+
+  u32 id;
+  u32 range;
+  u32 vni;
+  u32 vid;
 };
 
 struct evpn_proto {
@@ -33,12 +45,31 @@ struct evpn_proto {
   struct f_tree *export_target;
   u32 *export_target_data;
   uint export_target_length;
-  uint import_target_one;
+  bool import_target_one;
+  bool eth_refreshing;
+  bool evpn_refreshing;
 
   struct iface *tunnel_dev;
   ip_addr router_addr;
   u32 vni;
   u32 vid;
+  u32 tagX;
+
+  list vlans;                          /* List of VLANs (struct evpn_vlan) */
+  HASH(struct evpn_vlan) vlan_tag_hash;
+  HASH(struct evpn_vlan) vlan_vid_hash;
+};
+
+struct evpn_vlan {
+  node n;                              /* Node in evpn_proto.vlans */
+
+  u32 tag;
+  u32 vni;
+  u32 vid;
+
+  struct evpn_vlan *next_tag;
+  struct evpn_vlan *next_vid;
 };
 
+
 #endif