bgpd: encap: add attribute handling

Signed-off-by: Lou Berger <lberger@labn.net>
Reviewed-by: David Lamparter <equinox@opensourcerouting.org>
diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c
new file mode 100644
index 0000000..058b41e
--- /dev/null
+++ b/bgpd/bgp_encap_tlv.c
@@ -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;
+}
+