]> git.ipfire.org Git - thirdparty/bird.git/commitdiff
BGP: EVPN NLRI - preliminary support
authorOndrej Zajicek <santiago@crfreenet.org>
Mon, 29 May 2023 03:37:26 +0000 (05:37 +0200)
committerMaria Matejka <mq@ucw.cz>
Wed, 13 May 2026 08:57:37 +0000 (10:57 +0200)
Add basic support for EVPN NLRI in BGP (RFC 7432). Route types 1-4
(E-AD, MAC, IMET, ES) are supported as well as routes of unknown type.

MPLS labels are decoded as VNIs in style of RFC 8365, but stored using
existing BIRD MPLS attributes.

Note that ethernet segment ID on MAC route type is currently ignored.

Thanks to Pim van Pelt and Tomáš Matuš for comments and patches!

proto/bgp/bgp.c
proto/bgp/bgp.h
proto/bgp/config.Y
proto/bgp/packets.c

index 20267208ff78453034d40d93625e810ed3f88427..d3db1c2706f4b74a4f981f815b2de4781f3d6f4e 100644 (file)
@@ -93,6 +93,7 @@
  * RFC 6793 - BGP Support for 4-Octet AS Numbers
  * RFC 7311 - Accumulated IGP Metric Attribute for BGP
  * RFC 7313 - Enhanced Route Refresh Capability for BGP
+ * RFC 7432 - BGP MPLS-Based Ethernet VPN
  * RFC 7606 - Revised Error Handling for BGP UPDATE Messages
  * RFC 7911 - Advertisement of Multiple Paths in BGP
  * RFC 7947 - Internet Exchange BGP Route Server
@@ -2686,6 +2687,9 @@ bgp_channel_start(struct channel *C)
 
     if (bgp_channel_is_ipv6(c) && (ipa_is_ip6(src) || c->ext_next_hop))
       c->next_hop_addr = src;
+
+    if (bgp_channel_is_l2vpn(c))
+      c->next_hop_addr = src;
   }
 
   /* Use preferred addresses associated with interface / source address */
@@ -3038,10 +3042,10 @@ bgp_postconfig(struct proto_config *CF)
     /* Default values of IGP tables */
     if ((cc->gw_mode == GW_RECURSIVE) && !cc->desc->no_igp)
     {
-      if (!cc->igp_table_ip4 && (bgp_cc_is_ipv4(cc) || cc->ext_next_hop))
+      if (!cc->igp_table_ip4 && (bgp_cc_is_ipv4(cc) || bgp_cc_is_l2vpn(cc) || cc->ext_next_hop))
        cc->igp_table_ip4 = bgp_default_igp_table(cf, cc, NET_IP4);
 
-      if (!cc->igp_table_ip6 && (bgp_cc_is_ipv6(cc) || cc->ext_next_hop))
+      if (!cc->igp_table_ip6 && (bgp_cc_is_ipv6(cc) || bgp_cc_is_l2vpn(cc) || cc->ext_next_hop))
        cc->igp_table_ip6 = bgp_default_igp_table(cf, cc, NET_IP6);
 
       if (cc->igp_table_ip4 && bgp_cc_is_ipv6(cc) && !cc->ext_next_hop)
@@ -3726,7 +3730,7 @@ struct protocol proto_bgp = {
   .template =          "bgp%d",
   .class =             PROTOCOL_BGP,
   .preference =        DEF_PREF_BGP,
-  .channel_mask =      NB_IP | NB_VPN | NB_FLOW | NB_MPLS | NB_NEIGHBOR,
+  .channel_mask =      NB_IP | NB_VPN | NB_FLOW | NB_EVPN | NB_MPLS | NB_NEIGHBOR,
   .proto_size =                sizeof(struct bgp_proto),
   .config_size =       sizeof(struct bgp_config),
   .postconfig =                bgp_postconfig,
index 077fbd69678f86c56049e83efd59c37824786425..9834030a95360eeda50977623d62fab62351f657 100644 (file)
@@ -27,10 +27,12 @@ struct eattr;
 
 #define BGP_AFI_IPV4           1
 #define BGP_AFI_IPV6           2
+#define BGP_AFI_L2VPN          25
 
 #define BGP_SAFI_UNICAST       1
 #define BGP_SAFI_MULTICAST     2
 #define BGP_SAFI_MPLS          4
+#define BGP_SAFI_EVPN          70
 #define BGP_SAFI_MPLS_VPN      128
 #define BGP_SAFI_VPN_MULTICAST 129
 #define BGP_SAFI_FLOW          133
@@ -53,6 +55,7 @@ struct eattr;
 #define BGP_AF_VPN6_MC         BGP_AF( BGP_AFI_IPV6, BGP_SAFI_VPN_MULTICAST )
 #define BGP_AF_FLOW4           BGP_AF( BGP_AFI_IPV4, BGP_SAFI_FLOW )
 #define BGP_AF_FLOW6           BGP_AF( BGP_AFI_IPV6, BGP_SAFI_FLOW )
+#define BGP_AF_EVPN            BGP_AF( BGP_AFI_L2VPN, BGP_SAFI_EVPN )
 
 
 struct bgp_write_state;
@@ -584,12 +587,18 @@ static inline int bgp_channel_is_ipv4(struct bgp_channel *c)
 static inline int bgp_channel_is_ipv6(struct bgp_channel *c)
 { return BGP_AFI(c->afi) == BGP_AFI_IPV6; }
 
+static inline int bgp_channel_is_l2vpn(struct bgp_channel *c)
+{ return BGP_AFI(c->afi) == BGP_AFI_L2VPN; }
+
 static inline int bgp_cc_is_ipv4(struct bgp_channel_config *c)
 { return BGP_AFI(c->afi) == BGP_AFI_IPV4; }
 
 static inline int bgp_cc_is_ipv6(struct bgp_channel_config *c)
 { return BGP_AFI(c->afi) == BGP_AFI_IPV6; }
 
+static inline int bgp_cc_is_l2vpn(struct bgp_channel_config *c)
+{ return BGP_AFI(c->afi) == BGP_AFI_L2VPN; }
+
 static inline int bgp_channel_is_role_applicable(struct bgp_channel *c)
 { return (c->afi == BGP_AF_IPV4 || c->afi == BGP_AF_IPV6); }
 
index 09ca85a889d48b6278f30eddaf09a0271862d6da..cc1bfcef6922e2f1220dd229d688dc31654312ad 100644 (file)
@@ -266,6 +266,7 @@ bgp_afi:
  | VPN6 MULTICAST      { $$ = BGP_AF_VPN6_MC; }
  | FLOW4               { $$ = BGP_AF_FLOW4; }
  | FLOW6               { $$ = BGP_AF_FLOW6; }
+ | EVPN                        { $$ = BGP_AF_EVPN; }
  ;
 
 tcp_ao_key_start: KEY {
index fcaff7459077fda71c3831809026b68ed5d72438..522965d1059afb15dc21abc40303dd3fe395f30d 100644 (file)
@@ -34,6 +34,7 @@
 #define BGP_RR_END             2
 
 #define BGP_NLRI_MAX           (4 + 1 + 32)
+#define BGP_NLRI_EVPN_MAX      (4 + 2 + 52)
 
 #define BGP_MPLS_BOS           1       /* Bottom-of-stack bit */
 #define BGP_MPLS_MAX           10      /* Max number of labels that 24*n <= 255 */
@@ -1075,6 +1076,25 @@ bgp_rx_open(struct bgp_conn *conn, byte *pkt, uint len)
 
 #define MISMATCHED_AF  " - mismatched address family (%I for %s)"
 
+static int
+bgp_channel_match_next_hop_af(struct bgp_channel *c, ip_addr nh)
+{
+  switch (BGP_AFI(c->afi))
+  {
+  case BGP_AFI_IPV4:
+    return ipa_is_ip4(nh) || c->ext_next_hop;
+
+  case BGP_AFI_IPV6:
+    return ipa_is_ip6(nh) || c->ext_next_hop;
+
+  case BGP_AFI_L2VPN:
+    return 1;
+
+  default:
+    return 0;
+  }
+}
+
 static void
 bgp_apply_next_hop(struct bgp_parse_state *s, rta *a, ip_addr gw, ip_addr ll)
 {
@@ -1205,7 +1225,7 @@ bgp_use_next_hop(struct bgp_export_state *s, eattr *a)
     return 1;
 
   /* Check for non-matching AF */
-  if ((ipa_is_ip4(*nh) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
+  if (!bgp_channel_match_next_hop_af(c, *nh))
     return 0;
 
   /* Do not pass NEXT_HOP between different VRFs */
@@ -1216,6 +1236,10 @@ bgp_use_next_hop(struct bgp_export_state *s, eattr *a)
   if (p->is_interior && ipa_nonzero(*nh))
     return 1;
 
+  /* Locally originated EVPN routes came with their own BGP next hop */
+  if (!s->src && bgp_channel_is_l2vpn(c))
+    return 1;
+
   /* Keep it when forwarded between single-hop BGPs on the same iface */
   struct iface *ifa = (s->src && s->src->neigh && (s->src->p.proto_state != PS_DOWN)) ?
     s->src->neigh->iface : NULL;
@@ -1238,7 +1262,7 @@ bgp_use_gateway(struct bgp_export_state *s)
     return 0;
 
   /* Check for non-matching AF */
-  if ((ipa_is_ip4(ra->nh.gw) != bgp_channel_is_ipv4(c)) && !c->ext_next_hop)
+  if (!bgp_channel_match_next_hop_af(c, ra->nh.gw))
     return 0;
 
   /* Do not use gateway from different VRF */
@@ -1308,12 +1332,11 @@ bgp_update_next_hop_ip(struct bgp_export_state *s, eattr *a, ea_list **to)
     REJECT(BAD_NEXT_HOP " - neighbor address %I", peer);
 
   /* Forbid next hop with non-matching AF */
-  if ((ipa_is_ip4(nh[0]) != bgp_channel_is_ipv4(s->channel)) &&
-      !s->channel->ext_next_hop)
+  if (!bgp_channel_match_next_hop_af(s->channel, nh[0]))
     REJECT(BAD_NEXT_HOP MISMATCHED_AF, nh[0], s->channel->desc->name);
 
-  /* Just check if MPLS stack */
-  if (s->mpls && !bgp_find_attr(*to, BA_MPLS_LABEL_STACK))
+  /* Just check if there is MPLS stack - not applicable for EVPN */
+  if (s->mpls && (s->channel->afi != BGP_AF_EVPN) && !bgp_find_attr(*to, BA_MPLS_LABEL_STACK))
     REJECT(NO_LABEL_STACK);
 }
 
@@ -1360,11 +1383,11 @@ bgp_encode_next_hop_ip(struct bgp_write_state *s, eattr *a, byte *buf, uint size
   /*
    * Both IPv4 and IPv6 next hops can be used (with ext_next_hop enabled). This
    * is specified in RFC 8950 for IPv4 and in RFC 4798 for IPv6. The difference
-   * is that IPv4 address is directly encoded with IPv4 NLRI, but as IPv4-mapped
-   * IPv6 address with IPv6 NLRI.
+   * is that IPv4 address is directly encoded with IPv4 NLRI (and EVPN), but as
+   * IPv4-mapped IPv6 address with IPv6 NLRI.
    */
 
-  if (bgp_channel_is_ipv4(s->channel) && ipa_is_ip4(nh[0]))
+  if ((bgp_channel_is_ipv4(s->channel) || bgp_channel_is_l2vpn(s->channel)) && ipa_is_ip4(nh[0]))
   {
     put_ip4(buf, ipa_to_ip4(nh[0]));
     return 4;
@@ -1422,7 +1445,7 @@ bgp_decode_next_hop_ip(struct bgp_parse_state *s, byte *data, uint len, rta *a)
   if (ipa_zero(nh[1]))
     ad->length = 16;
 
-  if ((bgp_channel_is_ipv4(c) != ipa_is_ip4(nh[0])) && !c->ext_next_hop)
+  if (!bgp_channel_match_next_hop_af(c, nh[0]))
     WITHDRAW(BAD_NEXT_HOP MISMATCHED_AF, nh[0], c->desc->name);
 
   // XXXX validate next hop
@@ -1513,7 +1536,7 @@ bgp_decode_next_hop_vpn(struct bgp_parse_state *s, byte *data, uint len, rta *a)
   if ((get_u64(data) != 0) || ((len == 48) && (get_u64(data+24) != 0)))
     bgp_parse_error(s, 9);
 
-  if ((bgp_channel_is_ipv4(c) != ipa_is_ip4(nh[0])) && !c->ext_next_hop)
+  if (!bgp_channel_match_next_hop_af(c, nh[0]))
     WITHDRAW(BAD_NEXT_HOP MISMATCHED_AF, nh[0], c->desc->name);
 
   // XXXX validate next hop
@@ -2222,6 +2245,450 @@ bgp_decode_nlri_flow6(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
   }
 }
 
+static inline void
+bgp_encode_evpn_ip(byte **pos, uint *size, ip_addr ip)
+{
+  /* IP address length is in bits! */
+
+  if (ipa_is_ip4(ip))
+  {
+    **pos = IP4_MAX_PREFIX_LENGTH;
+    put_ip4(*pos+1, ipa_to_ip4(ip));
+    ADVANCE(*pos, *size, 1+4);
+  }
+  else
+  {
+    **pos = IP6_MAX_PREFIX_LENGTH;
+    put_ip6(*pos+1, ipa_to_ip6(ip));
+    ADVANCE(*pos, *size, 1+16);
+  }
+}
+
+static inline ip_addr
+bgp_decode_evpn_ip(struct bgp_parse_state *s, byte **pos, uint *len)
+{
+  /* IP address length is in bits! */
+
+  uint alen = **pos;   /* Assume this is validated by caller */
+  uint blen = 1 + (alen >> 3);
+
+  if (*len < blen)
+    bgp_parse_error(s, 1);
+
+  ip_addr ip;
+  if (alen == IP4_MAX_PREFIX_LENGTH)
+    ip = ipa_from_ip4(get_ip4(*pos + 1));
+  else if (alen == IP6_MAX_PREFIX_LENGTH)
+    ip = ipa_from_ip6(get_ip6(*pos + 1));
+  else
+    bgp_parse_error(s, 10); /* ? */
+
+  ADVANCE(*pos, *len, blen);
+  return ip;
+}
+
+static inline u32 bgp_label_ready(const adata *m, uint pos)
+{ return m && (m->length >= 4*(pos+1)); }
+
+static inline u32 bgp_get_label_(const adata *m, uint pos)
+{ return ((u32 *) m->data)[pos]; }
+
+static inline u32 bgp_get_label(const adata *m, uint pos)
+{ return bgp_label_ready(m, pos) ? bgp_get_label_(m, pos) : 0; }
+
+static uint
+bgp_encode_evpn_ead(struct bgp_write_state *s UNUSED, const net_addr_evpn *net, byte *buf, uint size)
+{
+  byte *pos = buf;
+
+  /* Encode route distinguisher */
+  put_rd(pos, net->rd);
+  ADVANCE(pos, size, 8);
+
+  /* Encode ethernet segment ID */
+  memcpy(pos, &net->ead.esi, 10);
+  ADVANCE(pos, size, 10);
+
+  /* Encode ethernet tag ID */
+  put_u32(pos, net->tag);
+  ADVANCE(pos, size, 4);
+
+  /* Encode MPLS label */
+  u32 label = bgp_get_label(s->mpls_labels, 0);
+  put_u24(pos, label); // << 4);
+  ADVANCE(pos, size, 3);
+
+  return pos - buf;
+}
+
+static void
+bgp_decode_evpn_ead(struct bgp_parse_state *s, net_addr_evpn *net, byte *pos, uint len)
+{
+  if (len < (8+10+4+3))
+    bgp_parse_error(s, 1);
+
+  /* Decode route distinguisher */
+  vpn_rd rd = get_rd(pos);
+  ADVANCE(pos, len, 8);
+
+  /* Decode ethernet segment ID */
+  evpn_esi esi;
+  memcpy(&esi, pos, 10);
+  ADVANCE(pos, len, 10);
+
+  /* Decode ethernet tag ID */
+  u32 tag = get_u32(pos);
+  ADVANCE(pos, len, 4);
+
+  /* Decode MPLS label */
+  u32 label = get_u24(pos); // >> 4;
+  ADVANCE(pos, len, 3);
+
+  s->mpls_labels = lp_alloc_adata(s->pool, 4);
+  memcpy(s->mpls_labels->data, &label, 4);
+
+  if (len)
+    bgp_parse_error(s, 1);
+
+  net->ead = NET_ADDR_EVPN_EAD(rd, tag, esi);
+}
+
+static uint
+bgp_encode_evpn_mac(struct bgp_write_state *s UNUSED, const net_addr_evpn *net, byte *buf, uint size)
+{
+  byte *pos = buf;
+
+  /* Encode route distinguisher */
+  put_rd(pos, net->rd);
+  ADVANCE(pos, size, 8);
+
+  /* Encode ethernet segment ID - XXX */
+  memset(pos, 0, 10);
+  ADVANCE(pos, size, 10);
+
+  /* Encode ethernet tag ID */
+  put_u32(pos, net->tag);
+  ADVANCE(pos, size, 4);
+
+  /* Encode MAC address */
+  pos[0] = 48;
+  memcpy(pos+1, &net->mac.mac, 6);
+  ADVANCE(pos, size, 7);
+
+  /* Encode IP address */
+  pos[0] = 0;
+  if (net->length == sizeof(net_addr_evpn_mac_ip))
+    bgp_encode_evpn_ip(&pos, &size, net->mac_ip.ip);
+  else
+    ADVANCE(pos, size, 1);
+
+  /* Encode MPLS label */
+  u32 label1 = bgp_get_label(s->mpls_labels, 0);
+  put_u24(pos, label1); // << 4);
+  ADVANCE(pos, size, 3);
+
+  if (bgp_label_ready(s->mpls_labels, 1))
+  {
+    u32 label2 = bgp_get_label_(s->mpls_labels, 1);
+    put_u24(pos, label2); // << 4);
+    ADVANCE(pos, size, 3);
+  }
+
+  return pos - buf;
+}
+
+static void
+bgp_decode_evpn_mac(struct bgp_parse_state *s, net_addr_evpn *net, byte *pos, uint len)
+{
+  if (len < (8+10+4+7+1))
+    bgp_parse_error(s, 1);
+
+  /* Decode route distinguisher */
+  vpn_rd rd = get_rd(pos);
+  ADVANCE(pos, len, 8);
+
+  /* Decode ethernet segment ID - XXX */
+  evpn_esi esi;
+  memcpy(&esi, pos, 10);
+  ADVANCE(pos, len, 10);
+
+  /* Decode ethernet tag ID */
+  u32 tag = get_u32(pos);
+  ADVANCE(pos, len, 4);
+
+  /* Decode MAC address */
+  if (pos[0] != 48)
+    bgp_parse_error(s, 10); /* ? */
+
+  mac_addr mac;
+  memcpy(&mac, pos+1, 6);
+  ADVANCE(pos, len, 7);
+
+  /* Decode IP address */
+  ip_addr ip = IPA_NONE;
+  if (pos[0])
+    ip = bgp_decode_evpn_ip(s, &pos, &len);
+  else
+    ADVANCE(pos, len, 1);
+
+  /* Decode MPLS labels */
+  if (len < 3)
+    bgp_parse_error(s, 1);
+
+  u32 label[2], lnum = 1;
+  label[0] = get_u24(pos); // >> 4;
+  ADVANCE(pos, len, 3);
+
+  if (len >= 3)
+  {
+    label[1] = get_u24(pos); // >> 4;
+    ADVANCE(pos, len, 3);
+    lnum++;
+  }
+
+  s->mpls_labels = lp_alloc_adata(s->pool, 4 * lnum);
+  memcpy(s->mpls_labels->data, label, 4 * lnum);
+
+  if (len)
+    bgp_parse_error(s, 1);
+
+  if (ipa_zero(ip))
+    net->mac = NET_ADDR_EVPN_MAC(rd, tag, mac);
+  else
+    net->mac_ip = NET_ADDR_EVPN_MAC_IP(rd, tag, mac, ip);
+}
+
+static uint
+bgp_encode_evpn_imet(struct bgp_write_state *s UNUSED, const net_addr_evpn *net, byte *buf, uint size)
+{
+  byte *pos = buf;
+
+  /* Encode route distinguisher */
+  put_rd(pos, net->rd);
+  ADVANCE(pos, size, 8);
+
+  /* Encode ethernet tag ID */
+  put_u32(pos, net->tag);
+  ADVANCE(pos, size, 4);
+
+  /* Encode router IP address */
+  bgp_encode_evpn_ip(&pos, &size, net->imet.rtr);
+
+  return pos - buf;
+}
+
+static void
+bgp_decode_evpn_imet(struct bgp_parse_state *s, net_addr_evpn *net, byte *pos, uint len)
+{
+  if (len < (8+4+1))
+    bgp_parse_error(s, 1);
+
+  /* Decode route distinguisher */
+  vpn_rd rd = get_rd(pos);
+  ADVANCE(pos, len, 8);
+
+  /* Decode ethernet tag ID */
+  u32 tag = get_u32(pos);
+  ADVANCE(pos, len, 4);
+
+  /* Decode router IP address */
+  ip_addr rtr = bgp_decode_evpn_ip(s, &pos, &len);
+
+  if (len)
+    bgp_parse_error(s, 1);
+
+  net->imet = NET_ADDR_EVPN_IMET(rd, tag, rtr);
+}
+
+static uint
+bgp_encode_evpn_es(struct bgp_write_state *s UNUSED, const net_addr_evpn *net, byte *buf, uint size)
+{
+  byte *pos = buf;
+
+  /* Encode route distinguisher */
+  put_rd(pos, net->rd);
+  ADVANCE(pos, size, 8);
+
+  /* Encode ethernet segment ID */
+  memcpy(pos, &net->es.esi, 10);
+  ADVANCE(pos, size, 10);
+
+  /* Encode router IP address */
+  bgp_encode_evpn_ip(&pos, &size, net->es.rtr);
+
+  return pos - buf;
+}
+
+static void
+bgp_decode_evpn_es(struct bgp_parse_state *s, net_addr_evpn *net, byte *pos, uint len)
+{
+  if (len < (8+10+1))
+    bgp_parse_error(s, 1);
+
+  /* Decode route distinguisher */
+  vpn_rd rd = get_rd(pos);
+  ADVANCE(pos, len, 8);
+
+  /* Decode ethernet segment ID */
+  evpn_esi esi;
+  memcpy(&esi, pos, 10);
+  ADVANCE(pos, len, 10);
+
+  /* Decode router IP address */
+  ip_addr rtr = bgp_decode_evpn_ip(s, &pos, &len);
+
+  if (len)
+    bgp_parse_error(s, 1);
+
+  net->es = NET_ADDR_EVPN_ES(rd, esi, rtr);
+}
+
+static int
+bgp_encode_evpn_unknown(struct bgp_write_state *s UNUSED, const net_addr_evpn *net, byte *buf, uint size)
+{
+  uint len = net_evpn_data_length(net);
+
+  if (len > size)
+    return -1;
+
+  /* Write raw data */
+  memcpy(buf, &net->data, len);
+
+  return len;
+}
+
+static void
+bgp_decode_evpn_unknown(struct bgp_parse_state *s, net_addr_evpn *net, uint type, byte *pos, uint len)
+{
+  /* Store raw data */
+  *net = NET_ADDR_EVPN_RAW(type, len);
+  memcpy(net->data, pos, len);
+
+  /* At least decode route distinguisher */
+  if ((type > 0) && (type < 11))
+  {
+    if (len < 8)
+      bgp_parse_error(s, 1);
+
+    net->rd = get_rd(pos);
+  }
+}
+
+static uint
+bgp_encode_nlri_evpn(struct bgp_write_state *s, struct bgp_bucket *buck, byte *buf, uint size)
+{
+  byte *pos = buf, *end = buf;
+
+  while (!EMPTY_LIST(buck->prefixes) && (size >= BGP_NLRI_EVPN_MAX))
+  {
+    struct bgp_prefix *px = HEAD(buck->prefixes);
+    const net_addr_evpn *net = (void *) px->net;
+
+    /* Encode path ID */
+    if (s->add_path)
+    {
+      put_u32(pos, px->path_id);
+      ADVANCE(pos, size, 4);
+    }
+
+    /* Encode EVPN header */
+    pos[0] = net->subtype;
+    pos[1] = 0;
+    ADVANCE(pos, size, 2);
+
+    int rlen;
+    switch (net->subtype)
+    {
+    case NET_EVPN_EAD: rlen = bgp_encode_evpn_ead(s, net, pos, size); break;
+    case NET_EVPN_MAC: rlen = bgp_encode_evpn_mac(s, net, pos, size); break;
+    case NET_EVPN_IMET:        rlen = bgp_encode_evpn_imet(s, net, pos, size); break;
+    case NET_EVPN_ES:  rlen = bgp_encode_evpn_es(s, net, pos, size); break;
+    default:           rlen = bgp_encode_evpn_unknown(s, net, pos, size); break;
+    }
+
+    /* Cannot fit EVPN body */
+    if (rlen < 0)
+      break;
+
+    /* Fix length */
+    pos[-1] = rlen;
+
+    ADVANCE(pos, size, rlen);
+
+    if (!s->sham)
+      bgp_free_prefix(s->channel, px);
+    else
+      rem_node(&px->buck_node);
+
+    end = pos;
+  }
+
+  return end - buf;
+}
+
+static void
+bgp_decode_nlri_evpn(struct bgp_parse_state *s, byte *pos, uint len, rta *a)
+{
+  char net_buf[NET_EVPN_MAX_LENGTH];
+  ea_list *base_eattrs = a ? a->eattrs : NULL;
+
+  while (len)
+  {
+    net_addr_evpn *net = (void *) net_buf;
+    u32 path_id = 0;
+
+    s->mpls_labels = NULL;
+
+    /* Reset attributes */
+    if (a)
+      a->eattrs = base_eattrs;
+
+    /* Decode path ID */
+    if (s->add_path)
+    {
+      if (len < 5)
+       bgp_parse_error(s, 1);
+
+      path_id = get_u32(pos);
+      ADVANCE(pos, len, 4);
+    }
+
+    if (len < 2)
+      bgp_parse_error(s, 1);
+
+    /* Decode EVPN header */
+    uint type = pos[0];
+    uint rlen = pos[1];
+    ADVANCE(pos, len, 2);
+
+    if (len < rlen)
+      bgp_parse_error(s, 1);
+
+    switch (type)
+    {
+    case NET_EVPN_EAD: bgp_decode_evpn_ead(s, net, pos, rlen); break;
+    case NET_EVPN_MAC: bgp_decode_evpn_mac(s, net, pos, rlen); break;
+    case NET_EVPN_IMET:        bgp_decode_evpn_imet(s, net, pos, rlen); break;
+    case NET_EVPN_ES:  bgp_decode_evpn_es(s, net, pos, rlen); break;
+    default:           bgp_decode_evpn_unknown(s, net, type, pos, rlen); break;
+    }
+
+    ADVANCE(pos, len, rlen);
+
+    if (a && s->mpls_labels)
+    {
+      adata *m = s->mpls_labels;
+      bgp_set_attr_ptr(&(a->eattrs), s->pool, BA_MPLS_LABEL_STACK, 0, m);
+      bgp_apply_mpls_labels(s, a, (u32 *) m->data, m->length / 4);
+    }
+
+    bgp_rte_update(s, (net_addr *) net, path_id, a);
+
+    rta_free(s->cached_rta);
+    s->cached_rta = NULL;
+  }
+}
+
 
 static const struct bgp_af_desc bgp_af_table[] = {
   {
@@ -2350,6 +2817,17 @@ static const struct bgp_af_desc bgp_af_table[] = {
     .decode_next_hop = bgp_decode_next_hop_none,
     .update_next_hop = bgp_update_next_hop_none,
   },
+  {
+    .afi = BGP_AF_EVPN,
+    .net = NET_EVPN,
+    .mpls = 1,
+    .name = "evpn",
+    .encode_nlri = bgp_encode_nlri_evpn,
+    .decode_nlri = bgp_decode_nlri_evpn,
+    .encode_next_hop = bgp_encode_next_hop_ip,
+    .decode_next_hop = bgp_decode_next_hop_ip,
+    .update_next_hop = bgp_update_next_hop_ip,
+  },
 };
 
 const struct bgp_af_desc *