]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: encap: add attribute handling
authorLou Berger <lberger@labn.net>
Tue, 12 Jan 2016 18:42:01 +0000 (13:42 -0500)
committervivek <vivek@cumulusnetworks.com>
Fri, 10 Jun 2016 01:36:40 +0000 (18:36 -0700)
Signed-off-by: Lou Berger <lberger@labn.net>
Reviewed-by: David Lamparter <equinox@opensourcerouting.org>
(cherry picked from commit c3741789530ee824693fd606356acac2ad695f83)

Conflicts:
bgpd/Makefile.am
bgpd/bgp_attr.c
bgpd/bgp_attr.h
lib/memtypes.c

bgpd/Makefile.am
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_encap_tlv.c [new file with mode: 0644]
bgpd/bgp_encap_tlv.h [new file with mode: 0644]
bgpd/bgp_encap_types.h [new file with mode: 0644]
bgpd/bgpd.h
lib/memtypes.c

index a7feb5cc1a11eeb7b8347cddc5aa810bd1054457..9183734a3c5992bc51c0a0d7d899d5b536b4ba32 100644 (file)
@@ -16,7 +16,8 @@ libbgp_a_SOURCES = \
        bgp_packet.c bgp_network.c bgp_filter.c bgp_regex.c bgp_clist.c \
        bgp_dump.c bgp_snmp.c bgp_ecommunity.c bgp_mplsvpn.c bgp_nexthop.c \
        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_nht.c bgp_updgrp.c bgp_updgrp_packet.c bgp_updgrp_adv.c bgp_bfd.c \
+       bgp_encap_tlv.c
 
 noinst_HEADERS = \
        bgp_aspath.h bgp_attr.h bgp_community.h bgp_debug.h bgp_fsm.h \
@@ -24,7 +25,7 @@ noinst_HEADERS = \
        bgpd.h bgp_filter.h bgp_clist.h bgp_dump.h bgp_zebra.h \
        bgp_ecommunity.h bgp_mplsvpn.h bgp_nexthop.h bgp_damp.h bgp_table.h \
        bgp_advertise.h bgp_snmp.h bgp_vty.h bgp_mpath.h bgp_nht.h \
-        bgp_updgrp.h bgp_bfd.h
+        bgp_updgrp.h bgp_bfd.h bgp_encap_tlv.h bgp_encap_types.h
 
 bgpd_SOURCES = bgp_main.c
 bgpd_LDADD = libbgp.a ../lib/libzebra.la @LIBCAP@ @LIBM@
index 2ffe464f0a8d7314a3d0bff0b1a600b49b2caea0..433924b54498f29dbe8d2903a908b980b3a930e6 100644 (file)
@@ -30,6 +30,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "hash.h"
 #include "jhash.h"
 #include "queue.h"
+#include "table.h"
 
 #include "bgpd/bgpd.h"
 #include "bgpd/bgp_attr.h"
@@ -40,6 +41,7 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "bgpd/bgp_packet.h"
 #include "bgpd/bgp_ecommunity.h"
 #include "bgpd/bgp_updgrp.h"
+#include "bgp_encap_types.h"
 
 /* Attribute strings for logging. */
 static const struct message attr_str [] = 
@@ -63,6 +65,7 @@ static const struct message attr_str [] =
   { BGP_ATTR_AS4_PATH,         "AS4_PATH" }, 
   { BGP_ATTR_AS4_AGGREGATOR,   "AS4_AGGREGATOR" }, 
   { BGP_ATTR_AS_PATHLIMIT,     "AS_PATHLIMIT" },
+  { BGP_ATTR_ENCAP,            "ENCAP" },
 };
 static const int attr_str_max = array_size(attr_str);
 
@@ -207,6 +210,105 @@ cluster_finish (void)
   cluster_hash = NULL;
 }
 
+struct bgp_attr_encap_subtlv *
+encap_tlv_dup(struct bgp_attr_encap_subtlv *orig)
+{
+    struct bgp_attr_encap_subtlv *new;
+    struct bgp_attr_encap_subtlv *tail;
+    struct bgp_attr_encap_subtlv *p;
+
+    for (p = orig, tail = new = NULL; p; p = p->next) {
+       int size = sizeof(struct bgp_attr_encap_subtlv) - 1 + p->length;
+       if (tail) {
+           tail->next = XCALLOC(MTYPE_ENCAP_TLV, size);
+           tail = tail->next;
+       } else {
+           tail = new = XCALLOC(MTYPE_ENCAP_TLV, size);
+       }
+       assert(tail);
+       memcpy(tail, p, size);
+       tail->next = NULL;
+    }
+
+    return new;
+}
+
+static void
+encap_free(struct bgp_attr_encap_subtlv *p)
+{
+    struct bgp_attr_encap_subtlv *next;
+    while (p) {
+        next    = p->next;
+        p->next = NULL;
+        XFREE(MTYPE_ENCAP_TLV, p);
+        p       = next;
+    }
+}
+
+void
+bgp_attr_flush_encap(struct attr *attr)
+{
+    if (!attr || !attr->extra)
+       return;
+
+    if (attr->extra->encap_subtlvs) {
+       encap_free(attr->extra->encap_subtlvs);
+       attr->extra->encap_subtlvs = NULL;
+    }
+}
+
+/*
+ * Compare encap sub-tlv chains
+ *
+ *     1 = equivalent
+ *     0 = not equivalent
+ *
+ * This algorithm could be made faster if needed
+ */
+static int
+encap_same(struct bgp_attr_encap_subtlv *h1, struct bgp_attr_encap_subtlv *h2)
+{
+    struct bgp_attr_encap_subtlv *p;
+    struct bgp_attr_encap_subtlv *q;
+
+    if (!h1 && !h2)
+       return 1;
+    if (h1 && !h2)
+       return 0;
+    if (!h1 && h2)
+       return 0;
+    if (h1 == h2)
+       return 1;
+
+    for (p = h1; p; p = p->next) {
+       for (q = h2; q; q = q->next) {
+           if ((p->type == q->type) &&
+               (p->length == q->length) &&
+               !memcmp(p->value, q->value, p->length)) {
+
+               break;
+           }
+       }
+       if (!q)
+           return 0;
+    }
+
+    for (p = h2; p; p = p->next) {
+       for (q = h1; q; q = q->next) {
+           if ((p->type == q->type) &&
+               (p->length == q->length) &&
+               !memcmp(p->value, q->value, p->length)) {
+
+               break;
+           }
+       }
+       if (!q)
+           return 0;
+    }
+
+    return 1;
+}
+
 /* Unknown transit attribute. */
 static struct hash *transit_hash;
 
@@ -314,6 +416,10 @@ bgp_attr_extra_free (struct attr *attr)
 {
   if (attr->extra)
     {
+      if (attr->extra->encap_subtlvs) {
+       encap_free(attr->extra->encap_subtlvs);
+       attr->extra->encap_subtlvs = NULL;
+      }
       XFREE (MTYPE_ATTR_EXTRA, attr->extra);
       attr->extra = NULL;
     }
@@ -349,13 +455,20 @@ bgp_attr_dup (struct attr *new, struct attr *orig)
     {
       new->extra = extra;
       memset(new->extra, 0, sizeof(struct attr_extra));
-      if (orig->extra)
+      if (orig->extra) {
         *new->extra = *orig->extra;
+        if (orig->extra->encap_subtlvs) {
+          new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs);
+        }
+      }
     }
   else if (orig->extra)
     {
       new->extra = bgp_attr_extra_new();
       *new->extra = *orig->extra;
+      if (orig->extra->encap_subtlvs) {
+       new->extra->encap_subtlvs = encap_tlv_dup(orig->extra->encap_subtlvs);
+      }
     }
 }
 
@@ -495,6 +608,8 @@ attrhash_cmp (const void *p1, const void *p2)
           && ae1->ecommunity == ae2->ecommunity
           && ae1->cluster == ae2->cluster
           && ae1->transit == ae2->transit
+         && (ae1->encap_tunneltype == ae2->encap_tunneltype)
+         && encap_same(ae1->encap_subtlvs, ae2->encap_subtlvs)
           && IPV4_ADDR_SAME (&ae1->originator_id, &ae2->originator_id))
         return 1;
       else if (ae1 || ae2)
@@ -549,6 +664,10 @@ bgp_attr_hash_alloc (void *p)
     {
       attr->extra = bgp_attr_extra_new ();
       *attr->extra = *val->extra;
+
+      if (attr->extra->encap_subtlvs) {
+       attr->extra->encap_subtlvs = encap_tlv_dup(attr->extra->encap_subtlvs);
+      }
     }
   attr->refcnt = 0;
   return attr;
@@ -673,6 +792,9 @@ bgp_attr_default_intern (u_char origin)
   struct attr attr;
   struct attr *new;
 
+  memset (&attr, 0, sizeof (struct attr));
+  bgp_attr_extra_get (&attr);
+
   bgp_attr_default_set(&attr, origin);
 
   new = bgp_attr_intern (&attr);
@@ -814,6 +936,8 @@ bgp_attr_flush (struct attr *attr)
         cluster_free (attre->cluster);
       if (attre->transit && ! attre->transit->refcnt)
         transit_free (attre->transit);
+      encap_free(attre->encap_subtlvs);
+      attre->encap_subtlvs = NULL;
     }
 }
 
@@ -1771,6 +1895,118 @@ bgp_attr_ext_communities (struct bgp_attr_parser_args *args)
   return BGP_ATTR_PARSE_PROCEED;
 }
 
+/* Parse Tunnel Encap attribute in an UPDATE */
+static int
+bgp_attr_encap(
+  uint8_t      type,
+  struct peer  *peer,  /* IN */
+  bgp_size_t   length, /* IN: attr's length field */
+  struct attr  *attr,  /* IN: caller already allocated */
+  u_char       flag,   /* IN: attr's flags field */
+  u_char       *startp)
+{
+  bgp_size_t                   total;
+  struct attr_extra            *attre = NULL;
+  struct bgp_attr_encap_subtlv *stlv_last = NULL;
+  uint16_t                     tunneltype;
+
+  total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+  if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS)
+       || !CHECK_FLAG(flag, BGP_ATTR_FLAG_OPTIONAL))
+    {
+      zlog_info ("Tunnel Encap attribute flag isn't optional and transitive %d", flag);
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+                                startp, total);
+      return -1;
+    }
+
+  if (BGP_ATTR_ENCAP == type) {
+    /* read outer TLV type and length */
+    uint16_t   tlv_length;
+
+    if (length < 4) {
+       zlog_info ("Tunnel Encap attribute not long enough to contain outer T,L");
+       bgp_notify_send_with_data(peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                startp, total);
+       return -1;
+    }
+    tunneltype = stream_getw (BGP_INPUT (peer));
+    tlv_length = stream_getw (BGP_INPUT (peer));
+    length -= 4;
+
+    if (tlv_length != length) {
+       zlog_info ("%s: tlv_length(%d) != length(%d)",
+           __func__, tlv_length, length);
+    }
+  }
+
+  while (length >= 4) {
+    uint16_t   subtype;
+    uint16_t   sublength;
+    struct bgp_attr_encap_subtlv *tlv;
+
+    subtype = stream_getw (BGP_INPUT (peer));
+    sublength = stream_getw (BGP_INPUT (peer));
+    length -= 4;
+
+    if (sublength > length) {
+      zlog_info ("Tunnel Encap attribute sub-tlv length %d exceeds remaining length %d",
+           sublength, length);
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                startp, total);
+      return -1;
+    }
+
+    /* alloc and copy sub-tlv */
+    /* TBD make sure these are freed when attributes are released */
+    tlv = XCALLOC (MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv)-1+sublength);
+    tlv->type = subtype;
+    tlv->length = sublength;
+    stream_get(tlv->value, peer->ibuf, sublength);
+    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;
+           }
+       }
+    } else {
+       stlv_last->next = tlv;
+    }
+    stlv_last = tlv;
+  }
+
+  if (attre && (BGP_ATTR_ENCAP == type)) {
+      attre->encap_tunneltype = tunneltype;
+  }
+
+  if (length) {
+    /* spurious leftover data */
+      zlog_info ("Tunnel Encap attribute length is bad: %d leftover octets", length);
+      bgp_notify_send_with_data (peer,
+                                BGP_NOTIFY_UPDATE_ERR,
+                                BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                startp, total);
+      return -1;
+  }
+
+  return 0;
+}
+
 /* BGP unknown attribute treatment. */
 static bgp_attr_parse_ret_t
 bgp_attr_unknown (struct bgp_attr_parser_args *args)
@@ -2060,6 +2296,9 @@ bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
        case BGP_ATTR_EXT_COMMUNITIES:
          ret = bgp_attr_ext_communities (&attr_args);
          break;
+        case BGP_ATTR_ENCAP:
+          ret = bgp_attr_encap (type, peer, length, attr, flag, startp);
+          break;
        default:
          ret = bgp_attr_unknown (&attr_args);
          break;
@@ -2316,6 +2555,89 @@ bgp_packet_mpattr_prefix_size (afi_t afi, safi_t safi, struct prefix *p)
   return size;
 }
 
+#if 0
+/*
+ * Encodes the tunnel encapsulation attribute
+ */
+static void
+bgp_packet_mpattr_tea(
+    struct bgp         *bgp,
+    struct peer                *peer,
+    struct stream      *s,
+    struct attr                *attr,
+    uint8_t            attrtype)
+{
+    unsigned int                       attrlenfield = 0;
+    struct bgp_attr_encap_subtlv       *subtlvs;
+    struct bgp_attr_encap_subtlv       *st;
+    const char                         *attrname;
+
+    if (!attr || !attr->extra)
+       return;
+
+    switch (attrtype) {
+       case BGP_ATTR_ENCAP:
+           attrname = "Tunnel Encap";
+           subtlvs = attr->extra->encap_subtlvs;
+
+           /*
+            * The tunnel encap attr has an "outer" tlv.
+            * T = tunneltype,
+            * L = total length of subtlvs,
+            * V = concatenated subtlvs.
+            */
+           attrlenfield = 2 + 2;       /* T + L */
+           break;
+
+       default:
+           assert(0);
+    }
+
+
+    /* compute attr length */
+    for (st = subtlvs; st; st = st->next) {
+       attrlenfield += (4 + st->length);
+    }
+
+    /* if no tlvs, don't make attr */
+    if (!attrlenfield)
+       return;
+
+    if (attrlenfield > 0xffff) {
+       zlog_info ("%s attribute is too long (length=%d), can't send it",
+           attrname,
+           attrlenfield);
+       return;
+    }
+
+    if (attrlenfield > 0xff) {
+       /* 2-octet length field */
+       stream_putc (s,
+           BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_EXTLEN);
+       stream_putc (s, attrtype);
+       stream_putw (s, attrlenfield & 0xffff);
+    } else {
+       /* 1-octet length field */
+       stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_OPTIONAL);
+       stream_putc (s, attrtype);
+       stream_putc (s, attrlenfield & 0xff);
+    }
+
+    if (attrtype == BGP_ATTR_ENCAP) {
+       /* write outer T+L */
+       stream_putw(s, attr->extra->encap_tunneltype);
+       stream_putw(s, attrlenfield - 4);
+    }
+
+    /* write each sub-tlv */
+    for (st = subtlvs; st; st = st->next) {
+       stream_putw (s, st->type);
+       stream_putw (s, st->length);
+       stream_put (s, st->value, st->length);
+    }
+}
+#endif
+
 void
 bgp_packet_mpattr_end (struct stream *s, size_t sizep)
 {
index 5cd6b8544a336cf69d7049ca5a346ac46c6520ec..1117f6269d98646a0c2bdfd96c2e16ccd49486b7 100644 (file)
@@ -55,6 +55,14 @@ Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #define BGP_ATTR_NHLEN_VPNV6_GLOBAL       8+IPV6_MAX_BYTELEN
 #define BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL ((8+IPV6_MAX_BYTELEN) * 2)
 
+
+struct bgp_attr_encap_subtlv {
+    struct bgp_attr_encap_subtlv       *next;          /* for chaining */
+    uint16_t                           type;
+    uint16_t                           length;
+    uint8_t                            value[1];       /* will be extended */
+};
+
 /* Additional/uncommon BGP attributes.
  * lazily allocated as and when a struct attr
  * requires it.
@@ -93,6 +101,9 @@ struct attr_extra
 
   /* route tag */
   u_short tag;
+
+  uint16_t                     encap_tunneltype;       /* grr */
+  struct bgp_attr_encap_subtlv *encap_subtlvs;         /* rfc5512 */
 };
 
 /* BGP core attribute structure. */
@@ -225,6 +236,12 @@ extern int bgp_mp_reach_parse (struct bgp_attr_parser_args *args,
 extern int bgp_mp_unreach_parse (struct bgp_attr_parser_args *args,
                                  struct bgp_nlri *);
 
+extern struct bgp_attr_encap_subtlv *
+encap_tlv_dup(struct bgp_attr_encap_subtlv *orig);
+
+extern void
+bgp_attr_flush_encap(struct attr *attr);
+
 /**
  * Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
  * Typical call sequence is to call _start(), followed by multiple _prefix(),
diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c
new file mode 100644 (file)
index 0000000..058b41e
--- /dev/null
@@ -0,0 +1,874 @@
+/*
+ * Copyright 2015, LabN Consulting, L.L.C.
+ *
+ * This program 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
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <zebra.h>
+
+#include "memory.h"
+#include "prefix.h"
+#include "vty.h"
+#include "filter.h"
+
+#include "bgpd.h"
+#include "bgp_attr.h"
+
+#include "bgp_encap_types.h"
+#include "bgp_encap_tlv.h"
+
+/***********************************************************************
+ *                     SUBTLV ENCODE
+ ***********************************************************************/
+
+/* rfc5512 4.1 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_encap_l2tpv3_over_ip(
+    struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int                                        total = 4 + st->cookie_length;
+
+    /* sanity check */
+    assert(st->cookie_length <= sizeof(st->cookie));
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->sessionid & 0xff000000) >> 24;
+    *p++ = (st->sessionid & 0xff0000) >> 16;
+    *p++ = (st->sessionid & 0xff00) >> 8;
+    *p++ = (st->sessionid & 0xff);
+    memcpy(p, st->cookie, st->cookie_length);
+    return new;
+}
+
+/* rfc5512 4.1 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_encap_gre(
+    struct bgp_tea_subtlv_encap_gre_key *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int                                        total = 4;
+
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->gre_key & 0xff000000) >> 24;
+    *p++ = (st->gre_key & 0xff0000) >> 16;
+    *p++ = (st->gre_key & 0xff00) >> 8;
+    *p++ = (st->gre_key & 0xff);
+    return new;
+}
+
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_encap_pbb(
+    struct bgp_tea_subtlv_encap_pbb    *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int                total = 1 + 3 + 6 + 2;  /* flags + isid + madaddr + vid */
+
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->flag_isid? 0x80: 0) |
+            (st->flag_vid? 0x40: 0) |
+            0;
+    if (st->flag_isid) {
+       *p = (st->isid & 0xff0000) >> 16;
+       *(p+1) = (st->isid & 0xff00) >> 8;
+       *(p+2) = (st->isid & 0xff);
+    }
+    p += 3;
+    memcpy(p, st->macaddr, 6);
+    p += 6;
+    if (st->flag_vid) {
+       *p++ = (st->vid & 0xf00) >> 8;
+       *p++ = st->vid & 0xff;
+    }
+    return new;
+}
+
+/* rfc5512 4.2 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_proto_type(
+    struct bgp_tea_subtlv_proto_type   *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int        total = 2;
+
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->proto & 0xff00) >> 8;
+    *p++ = (st->proto & 0xff);
+    return new;
+}
+
+/* rfc5512 4.3 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_color(
+    struct bgp_tea_subtlv_color        *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int        total = 8;
+
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_COLOR;
+    new->length = total;
+    p = new->value;
+
+    *p++ = 0x03;                               /* transitive*/
+    *p++ = 0x0b;
+    *p++ = 0;                                  /* reserved */
+    *p++ = 0;                                  /* reserved */
+
+    *p++ = (st->color & 0xff000000) >> 24;
+    *p++ = (st->color & 0xff0000) >> 16;
+    *p++ = (st->color & 0xff00) >> 8;
+    *p++ = (st->color & 0xff);
+
+    return new;
+}
+
+/* rfc 5566 4. */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_ipsec_ta(
+    struct bgp_tea_subtlv_ipsec_ta     *st)
+{
+    struct bgp_attr_encap_subtlv       *new;
+    uint8_t                            *p;
+    int        total = 2 + st->authenticator_length;
+
+    /* sanity check */
+    assert(st->authenticator_length <= sizeof(st->value));
+    assert(total <= 0xff);
+
+    new = XCALLOC(MTYPE_ENCAP_TLV, sizeof(struct bgp_attr_encap_subtlv) - 1 + total);
+    assert(new);
+    new->type = BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA;
+    new->length = total;
+    p = new->value;
+
+    *p++ = (st->authenticator_type & 0xff00) >> 8;
+    *p++ = st->authenticator_type & 0xff;
+    memcpy(p, st->value, st->authenticator_length);
+    return new;
+}
+
+
+/***********************************************************************
+ *             TUNNEL TYPE-SPECIFIC TLV ENCODE
+ ***********************************************************************/
+
+/*
+ * requires "extra" and "last" to be defined in caller
+ */
+#define ENC_SUBTLV(flag, function, field) do {\
+    struct bgp_attr_encap_subtlv       *new;\
+    if (CHECK_FLAG(bet->valid_subtlvs, (flag))) {\
+       new = function(&bet->field);\
+       if (last) {\
+           last->next = new;\
+       } else {\
+           extra->encap_subtlvs = new;\
+       }\
+       last = new;\
+    }\
+} while (0)
+
+void
+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);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_L2TPV3_OVER_IP;
+
+    assert(CHECK_FLAG(bet->valid_subtlvs, BGP_TEA_SUBTLV_ENCAP));
+
+    ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_l2tpv3_over_ip, st_encap);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type, st_proto);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color);
+}
+
+void
+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);
+
+    extra->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);
+    ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color);
+}
+
+void
+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);
+
+    extra->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);
+}
+
+void
+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);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT;
+
+    /* no subtlvs for this type */
+}
+
+void
+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);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE;
+
+    ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta, st_ipsec_ta);
+}
+
+void
+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);
+
+    extra->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);
+}
+
+void
+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);
+
+    extra->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);
+}
+
+void
+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);
+
+    extra->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);
+}
+
+void
+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);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN;
+}
+
+void
+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;
+}
+
+void
+bgp_encap_type_mpls_to_tlv(
+    struct bgp_encap_type_mpls *bet,   /* input structure */
+    struct attr                                *attr)
+{
+    struct attr_extra                  *extra = bgp_attr_extra_get(attr);
+
+    extra->encap_tunneltype = BGP_ENCAP_TYPE_MPLS;
+}
+
+void
+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;
+}
+
+void
+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;
+}
+
+void
+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;
+}
+
+
+/***********************************************************************
+ *                     SUBTLV DECODE
+ ***********************************************************************/
+/* rfc5512 4.1 */
+static int
+subtlv_decode_encap_l2tpv3_over_ip(
+    struct bgp_attr_encap_subtlv               *subtlv,
+    struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st)
+{
+    if (subtlv->length < 4) {
+       zlog_debug("%s, subtlv length %d is less than 4",
+           __func__, subtlv->length);
+       return -1;
+    }
+
+    st->sessionid = (subtlv->value[0] << 24) |
+                   (subtlv->value[1] << 16) |
+                   (subtlv->value[2] << 8)  |
+                   subtlv->value[3];
+    st->cookie_length = subtlv->length - 4;
+    if (st->cookie_length > sizeof(st->cookie)) {
+       zlog_debug("%s, subtlv length %d is greater than %d",
+           __func__, st->cookie_length, (int)sizeof(st->cookie));
+       return -1;
+    }
+    memcpy(st->cookie, subtlv->value + 4, st->cookie_length);
+    return 0;
+}
+
+/* rfc5512 4.1 */
+static int
+subtlv_decode_encap_gre(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_encap_gre_key *st)
+{
+    if (subtlv->length != 4) {
+       zlog_debug("%s, subtlv length %d does not equal 4",
+           __func__, subtlv->length);
+       return -1;
+    }
+    st->gre_key = (subtlv->value[0] << 24) |
+                   (subtlv->value[1] << 16) |
+                   (subtlv->value[2] << 8)  |
+                   subtlv->value[3];
+    return 0;
+}
+
+static int
+subtlv_decode_encap_pbb(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_encap_pbb    *st)
+{
+    if (subtlv->length != 1 + 3 + 6 + 2) {
+       zlog_debug("%s, subtlv length %d does not equal %d",
+           __func__, subtlv->length, 1 + 3 + 6 + 2);
+       return -1;
+    }
+    if (subtlv->value[0] & 0x80) {
+       st->flag_isid = 1;
+       st->isid = (subtlv->value[1] << 16) |
+                   (subtlv->value[2] << 8) |
+                   subtlv->value[3];
+    }
+    if (subtlv->value[0] & 0x40) {
+       st->flag_vid  = 1;
+       st->vid = ((subtlv->value[10] & 0x0f) << 8) | subtlv->value[11];
+    }
+    memcpy(st->macaddr, subtlv->value + 4, 6);
+    return 0;
+}
+
+/* rfc5512 4.2 */
+static int
+subtlv_decode_proto_type(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_proto_type   *st)
+{
+    if (subtlv->length != 2) {
+       zlog_debug("%s, subtlv length %d does not equal 2",
+           __func__, subtlv->length);
+       return -1;
+    }
+    st->proto = (subtlv->value[0] << 8) | subtlv->value[1];
+    return 0;
+}
+
+/* rfc5512 4.3 */
+static int
+subtlv_decode_color(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_color                *st)
+{
+    if (subtlv->length != 8) {
+       zlog_debug("%s, subtlv length %d does not equal 8",
+           __func__, subtlv->length);
+       return -1;
+    }
+    if ((subtlv->value[0] != 0x03) ||
+       (subtlv->value[1] != 0x0b) ||
+       (subtlv->value[2] != 0)    ||
+       (subtlv->value[3] != 0)) {
+       zlog_debug("%s, subtlv value 1st 4 bytes are not 0x030b0000", __func__);
+       return -1;
+    }
+    st->color = (subtlv->value[4] << 24) |
+               (subtlv->value[5] << 16) |
+               (subtlv->value[6] << 8)  |
+               subtlv->value[7];
+    return 0;
+}
+
+/* rfc 5566 4. */
+static int
+subtlv_decode_ipsec_ta(
+    struct bgp_attr_encap_subtlv       *subtlv,
+    struct bgp_tea_subtlv_ipsec_ta     *st)
+{
+    st->authenticator_length = subtlv->length - 2;
+    if (st->authenticator_length > sizeof(st->value)) {
+       zlog_debug("%s, authenticator length %d exceeds storage maximum %d",
+           __func__, st->authenticator_length, (int)sizeof(st->value));
+       return -1;
+    }
+    st->authenticator_type = (subtlv->value[0] << 8) | subtlv->value[1];
+    memcpy(st->value, subtlv->value + 2,  st->authenticator_length);
+    return 0;
+}
+
+/***********************************************************************
+ *             TUNNEL TYPE-SPECIFIC TLV DECODE
+ ***********************************************************************/
+
+int
+tlv_to_bgp_encap_type_l2tpv3overip(
+    struct bgp_attr_encap_subtlv               *stlv,  /* subtlv chain */
+    struct bgp_encap_type_l2tpv3_over_ip       *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+               rc |= subtlv_decode_encap_l2tpv3_over_ip(st, &bet->st_encap);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_ENCAP;
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE:
+               rc |= subtlv_decode_proto_type(st, &bet->st_proto);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_PROTO_TYPE;
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_COLOR:
+               rc |= subtlv_decode_color(st, &bet->st_color);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_COLOR;
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_gre(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_gre          *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+               rc |= subtlv_decode_encap_gre(st, &bet->st_encap);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_ENCAP;
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE:
+               rc |= subtlv_decode_proto_type(st, &bet->st_proto);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_PROTO_TYPE;
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_COLOR:
+               rc |= subtlv_decode_color(st, &bet->st_color);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_COLOR;
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_ip_in_ip(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_ip_in_ip     *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE:
+               rc |= subtlv_decode_proto_type(st, &bet->st_proto);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_PROTO_TYPE;
+               break;
+
+           case BGP_ENCAP_SUBTLV_TYPE_COLOR:
+               rc |= subtlv_decode_color(st, &bet->st_color);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_COLOR;
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_transmit_tunnel_endpoint(
+    struct bgp_attr_encap_subtlv                       *stlv,
+    struct bgp_encap_type_transmit_tunnel_endpoint     *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_ipsec_in_tunnel_mode(
+    struct bgp_attr_encap_subtlv               *stlv,  /* subtlv chain */
+    struct bgp_encap_type_ipsec_in_tunnel_mode *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA:
+               rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_IPSEC_TA;
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode(
+    struct bgp_attr_encap_subtlv                                       *stlv,
+    struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode    *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA:
+               rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_IPSEC_TA;
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode(
+    struct bgp_attr_encap_subtlv                                       *stlv,
+    struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode  *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA:
+               rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_IPSEC_TA;
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_vxlan(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_vxlan                *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_nvgre(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_nvgre                *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_mpls(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls         *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_mpls_in_gre(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls_in_gre  *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_vxlan_gpe(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_vxlan_gpe    *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_mpls_in_udp(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls_in_udp  *bet)
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
+int
+tlv_to_bgp_encap_type_pbb(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_pbb          *bet)   /* caller-allocated */
+{
+    struct bgp_attr_encap_subtlv               *st;
+    int                                                rc = 0;
+
+    for (st = stlv; st; st = st->next) {
+       switch (st->type) {
+           case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+               rc |= subtlv_decode_encap_pbb(st, &bet->st_encap);
+               bet->valid_subtlvs |= BGP_TEA_SUBTLV_ENCAP;
+               break;
+
+           default:
+               zlog_debug("%s: unexpected subtlv type %d", __func__, st->type);
+               rc |= -1;
+               break;
+       }
+    }
+    return rc;
+}
+
diff --git a/bgpd/bgp_encap_tlv.h b/bgpd/bgp_encap_tlv.h
new file mode 100644 (file)
index 0000000..d94d544
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2015, LabN Consulting, L.L.C.
+ *
+ * This program 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
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_BGP_ENCAP_TLV_H
+#define _QUAGGA_BGP_ENCAP_TLV_H
+
+
+/***********************************************************************
+ *             TUNNEL TYPE-SPECIFIC TLV ENCODE
+ ***********************************************************************/
+
+extern void
+bgp_encap_type_l2tpv3overip_to_tlv(
+    struct bgp_encap_type_l2tpv3_over_ip       *bet,
+    struct attr                                        *attr);
+
+extern void
+bgp_encap_type_gre_to_tlv(
+    struct bgp_encap_type_gre  *bet,
+    struct attr                        *attr);
+
+extern void
+bgp_encap_type_ip_in_ip_to_tlv(
+    struct bgp_encap_type_ip_in_ip     *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_transmit_tunnel_endpoint(
+    struct bgp_encap_type_transmit_tunnel_endpoint     *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_ipsec_in_tunnel_mode_to_tlv(
+    struct bgp_encap_type_ipsec_in_tunnel_mode *bet,
+    struct attr                                *attr);
+
+extern void
+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,
+    struct attr                                *attr);
+
+extern void
+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,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_pbb_to_tlv(
+    struct bgp_encap_type_pbb  *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_vxlan_to_tlv(
+    struct bgp_encap_type_vxlan        *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_nvgre_to_tlv(
+    struct bgp_encap_type_nvgre        *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_mpls_to_tlv(
+    struct bgp_encap_type_mpls *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_mpls_in_gre_to_tlv(
+    struct bgp_encap_type_mpls_in_gre  *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_vxlan_gpe_to_tlv(
+    struct bgp_encap_type_vxlan_gpe    *bet,
+    struct attr                                *attr);
+
+extern void
+bgp_encap_type_mpls_in_udp_to_tlv(
+    struct bgp_encap_type_mpls_in_udp  *bet,
+    struct attr                                *attr);
+
+/***********************************************************************
+ *             TUNNEL TYPE-SPECIFIC TLV DECODE
+ ***********************************************************************/
+
+extern int
+tlv_to_bgp_encap_type_l2tpv3overip(
+    struct bgp_attr_encap_subtlv               *stlv,  /* subtlv chain */
+    struct bgp_encap_type_l2tpv3_over_ip       *bet);  /* caller-allocated */
+
+extern int
+tlv_to_bgp_encap_type_gre(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_gre          *bet);  /* caller-allocated */
+
+extern int
+tlv_to_bgp_encap_type_ip_in_ip(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_ip_in_ip     *bet);  /* caller-allocated */
+
+extern int
+tlv_to_bgp_encap_type_transmit_tunnel_endpoint(
+    struct bgp_attr_encap_subtlv                       *stlv,
+    struct bgp_encap_type_transmit_tunnel_endpoint     *bet);
+
+extern int
+tlv_to_bgp_encap_type_ipsec_in_tunnel_mode(
+    struct bgp_attr_encap_subtlv               *stlv,  /* subtlv chain */
+    struct bgp_encap_type_ipsec_in_tunnel_mode *bet);  /* caller-allocated */
+
+extern int
+tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode(
+    struct bgp_attr_encap_subtlv                                       *stlv,
+    struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode    *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode(
+    struct bgp_attr_encap_subtlv                                       *stlv,
+    struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode  *bet);
+
+extern int
+tlv_to_bgp_encap_type_vxlan(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_vxlan                *bet);
+
+extern int
+tlv_to_bgp_encap_type_nvgre(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_nvgre                *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls         *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls         *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls_in_gre(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls_in_gre  *bet);
+
+extern int
+tlv_to_bgp_encap_type_vxlan_gpe(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_vxlan_gpe    *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls_in_udp(
+    struct bgp_attr_encap_subtlv       *stlv,
+    struct bgp_encap_type_mpls_in_udp  *bet);
+
+extern int
+tlv_to_bgp_encap_type_pbb(
+    struct bgp_attr_encap_subtlv       *stlv,  /* subtlv chain */
+    struct bgp_encap_type_pbb          *bet);  /* caller-allocated */
+
+#endif /* _QUAGGA_BGP_ENCAP_TLV_H */
diff --git a/bgpd/bgp_encap_types.h b/bgpd/bgp_encap_types.h
new file mode 100644 (file)
index 0000000..c81b729
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2015, LabN Consulting, L.L.C.
+ *
+ * This program 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
+ * of the License, or (at your option) any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#ifndef _QUAGGA_BGP_ENCAP_TYPES_H
+#define _QUAGGA_BGP_ENCAP_TYPES_H
+
+/* from http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#tunnel-types */
+typedef enum {
+    BGP_ENCAP_TYPE_RESERVED=0,
+    BGP_ENCAP_TYPE_L2TPV3_OVER_IP=1,
+    BGP_ENCAP_TYPE_GRE=2,
+    BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT=3,
+    BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE=4,
+    BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE=5,
+    BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE=6,
+    BGP_ENCAP_TYPE_IP_IN_IP=7,
+    BGP_ENCAP_TYPE_VXLAN=8,
+    BGP_ENCAP_TYPE_NVGRE=9,
+    BGP_ENCAP_TYPE_MPLS=10,
+    BGP_ENCAP_TYPE_MPLS_IN_GRE=11,
+    BGP_ENCAP_TYPE_VXLAN_GPE=12,
+    BGP_ENCAP_TYPE_MPLS_IN_UDP=13,
+    BGP_ENCAP_TYPE_PBB
+} bgp_encap_types;
+
+typedef enum {
+    BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION=1,
+    BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE=2,
+    BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA=3,
+    BGP_ENCAP_SUBTLV_TYPE_COLOR=4,
+} bgp_encap_subtlv_types;
+
+/*
+ * Tunnel Encapsulation Attribute subtlvs
+ */
+struct bgp_tea_subtlv_encap_l2tpv3_over_ip {
+    uint32_t   sessionid;
+    uint8_t    cookie_length;
+    uint8_t    cookie[8];
+};
+
+struct bgp_tea_subtlv_encap_gre_key {
+    uint32_t   gre_key;
+};
+
+struct bgp_tea_subtlv_encap_pbb {
+    uint32_t   flag_isid:1;
+    uint32_t   flag_vid:1;
+    uint32_t   isid:24;
+    uint16_t   vid:12;
+    uint8_t    macaddr[6];
+};
+
+struct bgp_tea_subtlv_proto_type {
+    uint16_t   proto;                  /* ether-type */
+};
+
+struct bgp_tea_subtlv_color {
+    uint32_t   color;
+};
+
+/*
+ * This is the length of the value part of the ipsec tunnel authenticator
+ * subtlv. Currently we only support the length for authenticator type 1.
+ */
+#define BGP_ENCAP_SUBTLV_IPSEC_TA_SIZE 20
+
+struct bgp_tea_subtlv_ipsec_ta {
+    uint16_t   authenticator_type;     /* only type 1 is supported so far */
+    uint8_t    authenticator_length;   /* octets in value field */
+    uint8_t    value[BGP_ENCAP_SUBTLV_IPSEC_TA_SIZE];
+};
+
+/*
+ * Subtlv valid flags
+ * TBD change names to add "VALID"
+ */
+#define BGP_TEA_SUBTLV_ENCAP           0x00000001
+#define BGP_TEA_SUBTLV_PROTO_TYPE      0x00000002
+#define BGP_TEA_SUBTLV_COLOR           0x00000004
+#define BGP_TEA_SUBTLV_IPSEC_TA                0x00000008
+
+
+/*
+ * Tunnel Type-specific APIs
+ */
+struct bgp_encap_type_reserved {
+};
+
+struct bgp_encap_type_l2tpv3_over_ip {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_encap_l2tpv3_over_ip st_encap;
+    struct bgp_tea_subtlv_proto_type           st_proto;       /* optional */
+    struct bgp_tea_subtlv_color                        st_color;       /* optional */
+};
+
+struct bgp_encap_type_gre {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_encap_gre_key                st_encap;       /* optional */
+    struct bgp_tea_subtlv_proto_type           st_proto;       /* optional */
+    struct bgp_tea_subtlv_color                        st_color;       /* optional */
+};
+
+struct bgp_encap_type_ip_in_ip {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_proto_type           st_proto;       /* optional */
+    struct bgp_tea_subtlv_color                        st_color;       /* optional */
+};
+
+struct bgp_encap_type_transmit_tunnel_endpoint {
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_ipsec_in_tunnel_mode {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_ipsec_ta             st_ipsec_ta;    /* optional */
+};
+
+struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_ipsec_ta             st_ipsec_ta;    /* optional */
+};
+
+struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_ipsec_ta             st_ipsec_ta;    /* optional */
+};
+
+struct bgp_encap_type_vxlan {
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_nvgre {
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_mpls {
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_mpls_in_gre {
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_vxlan_gpe {
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_mpls_in_udp {
+    /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_pbb {
+    uint32_t                                   valid_subtlvs;
+    struct bgp_tea_subtlv_encap_pbb            st_encap;
+};
+
+#endif /* _QUAGGA_BGP_ENCAP_TYPES_H */
index 06a50e228d881ab4c54cd2192aa6566466b13736..40bfc7d1eaca61bb6b1628d144f49727cc96b667 100644 (file)
@@ -936,6 +936,7 @@ struct bgp_nlri
 #define BGP_ATTR_AS4_PATH                       17
 #define BGP_ATTR_AS4_AGGREGATOR                 18
 #define BGP_ATTR_AS_PATHLIMIT                   21
+#define BGP_ATTR_ENCAP                          23
 
 /* BGP update origin.  */
 #define BGP_ORIGIN_IGP                           0
index 7d75f31a3d8848279d738a15211913de4101f21f..d0faecb640ffc07aebd60120793055d32ed0e3ca 100644 (file)
@@ -175,6 +175,7 @@ struct memory_list memory_list_bgp[] =
   { MTYPE_BGP_REDIST,           "BGP redistribution"            },
   { MTYPE_BGP_FILTER_NAME,      "BGP Filter Information"        },
   { MTYPE_BGP_DUMP_STR,         "BGP Dump String Information"   },
+  { MTYPE_ENCAP_TLV,           "ENCAP TLV",                    },
   { -1, NULL }
 };