summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMikael Magnusson <mikma@users.sourceforge.net>2018-09-28 01:03:42 +0200
committerMikael Magnusson <mikma@users.sourceforge.net>2021-11-06 00:07:40 +0100
commit4d2bf9c7aa9525ee682311679e85cdbd3fb2e51e (patch)
tree0122ff8f3ae163f3f3060dc58e9297013c85fa6d
parentc8eff2ae3eb1c6090b4cc85cb5d19699c45d9f26 (diff)
TunnelEncaps: Initial commit
Filter: TLV Filter: support multiple TLVs Filter: clean unused build_tunnel_encap and calc_tunnel_encap Filter: replace te_format_tlvlist using format visitor Filter: add af to ep subtlv Filter: define tlvlist_calc_tunnel_encap_new and tlvlist_decode_tunnel_encap Filter: use tlvlist_calc_tunnel_encap_new Filter: add visit_tlv_end and visit_subtlv_end Filter: use visitor in EA_SET Filter: use vistor in EA_SET Filter: add tlvlist_same fixes configure free peer Generalize tunnel encapsulation Add struct tunnel_encap Improve format function Add tunnel type names Add cloud security tunnel type Update tunnel types Add addess family Replace log with DBG Add format callback Add wireguard peer key to tunnel encap format Move wireguard formatting from tunnel_encaps library Change from eattr to adata in decode and format Support multiple TLVs Use visitor pattern Use visitor in wireguard Remove decode_tunnel_encap Replace te_format_tlvlist using format visitor Remove unused structs Use AFI_IPV4+6 Add visit_tlv_end and visit_subtlv_end Remove debug Fix format encap + ep Register encap name Filter: Simplify TLV Remove some reserved keywords: TUNNEL_ENCAP, TUNNEL_ENDPOINT, UDP_DEST_PORT, and COLOR Support unknown sub-TLV. Filter: Clean up Clean up unused functions and structs. Filter: replace asn with reserved in ep. Filter: Remove unused T_TLV Filter: Clean up commented code Filter: Remove unused empty set Filter: Refactor encoder Filter: Refactor tlvlist Filter: Implement unknown cmp Filter: Simplify encoding Filter: Add EA_GET Filter: Fix indent
-rw-r--r--filter/config.Y160
-rw-r--r--filter/data.c39
-rw-r--r--filter/data.h10
-rw-r--r--filter/f-inst.c60
-rw-r--r--lib/Makefile2
-rw-r--r--lib/tunnel_encaps.c856
-rw-r--r--lib/tunnel_encaps.h63
-rw-r--r--lib/unaligned.h3
-rw-r--r--nest/Makefile2
-rw-r--r--nest/a-tlv.c418
-rw-r--r--nest/attrs.h67
-rw-r--r--nest/iface.h1
-rw-r--r--nest/route.h1
-rw-r--r--sysdep/unix/main.c2
14 files changed, 1676 insertions, 8 deletions
diff --git a/filter/config.Y b/filter/config.Y
index d2dfcdea..af4281ec 100644
--- a/filter/config.Y
+++ b/filter/config.Y
@@ -112,6 +112,145 @@ f_new_pair_set(int fa, int ta, int fb, int tb)
#define LC_ALL 0xFFFFFFFF
static struct f_tree *
+f_new_sub_tlv_int_item(u32 type, u32 v1)
+{
+ struct f_tree *t = f_new_tree();
+ t->right = t;
+ t->from.type = t->to.type = T_SUBTLV;
+ struct te_subtlv v;
+ v.type = type;
+ switch (type) {
+ case TLV_COLOR:
+ v.u.color = v1;
+ break;
+ case TLV_UDP_DEST_PORT:
+ v.u.udp_dest_port = v1;
+ break;
+ default:
+ cf_error("Unsupported sub-TLV type: %d", type);
+ }
+ t->from.val.st = v;
+ t->to.val.st = v;
+ return t;
+}
+
+static struct f_tree *
+f_new_sub_tlv_tunnel_ep(u32 type, u32 reserved, ip_addr ip)
+{
+ struct f_tree *t;
+
+ if (type != TLV_TUNNEL_ENDPOINT)
+ cf_error("Expected sub-TLV type %d, got %d", TLV_TUNNEL_ENDPOINT, type);
+
+ t = f_new_tree();
+ t->right = t;
+ t->from.type = t->to.type = T_SUBTLV;
+ struct te_subtlv v;
+ v.type = TLV_TUNNEL_ENDPOINT;
+ v.u.tunnel_endpoint.reserved = reserved;
+ v.u.tunnel_endpoint.af = ipa_is_ip4(ip) ? AFI_IPV4 : AFI_IPV6;
+ v.u.tunnel_endpoint.ip = ip;
+ t->from.val.st = v;
+ t->to.val.st = v;
+ return t;
+}
+
+static struct f_tree *
+f_new_sub_tlv_ipa_item(u32 type, ip_addr ip)
+{
+ if (type != TLV_TUNNEL_ENDPOINT)
+ cf_error("Expected sub-TLV type %d, got %d", TLV_TUNNEL_ENDPOINT, type);
+
+ return f_new_sub_tlv_tunnel_ep(type, 0, ip);
+}
+
+static struct f_tree *
+f_new_sub_tlv_encap(u32 st_type, u32 type, const struct bytestring *bytes)
+{
+ struct f_tree *t;
+
+ if (st_type != TLV_ENCAPSULATION)
+ cf_error("Expected sub-TLV type %d, got %d", TLV_ENCAPSULATION, type);
+
+ t = f_new_tree();
+ t->right = t;
+ t->from.type = t->to.type = T_SUBTLV;
+ struct te_subtlv v;
+ v.type = TLV_ENCAPSULATION;
+ v.u.tunnel_encap.type = type;
+ v.u.tunnel_encap.data = bytes->data;
+ v.u.tunnel_encap.length = bytes->length;
+ t->from.val.st = v;
+ t->to.val.st = v;
+ return t;
+}
+
+static struct f_tree *
+f_new_sub_tlv_unknown(u32 type, const struct bytestring *bytes)
+{
+ struct f_tree *t;
+
+ switch(type) {
+ case TLV_TUNNEL_ENDPOINT:
+ case TLV_ENCAPSULATION:
+ case TLV_UDP_DEST_PORT:
+ case TLV_COLOR:
+ cf_error("Expected an unkonwn sub-TLV type got %d", type);
+ }
+
+ t = f_new_tree();
+ t->right = t;
+ t->from.type = t->to.type = T_SUBTLV;
+ struct te_subtlv v;
+ v.type = type;
+ v.u.unknown.data = bytes->data;
+ v.u.unknown.length = bytes->length;
+ t->from.val.st = v;
+ t->to.val.st = v;
+ return t;
+}
+
+static struct f_tree *
+f_new_sub_tlv_item(u32 type, struct f_inst *v1)
+{
+ struct f_val val;
+ enum filter_return ret = f_eval(f_linearize(v1), cfg_mem, &val);
+
+ if (ret > F_RETURN)
+ cf_error("Runtime error while evaluating expression; see log for details");
+
+ switch(val.type) {
+ case T_INT:
+ return f_new_sub_tlv_int_item(type, val.val.i);
+ case T_IP:
+ return f_new_sub_tlv_ipa_item(type, val.val.ip);
+ case T_BYTESTRING:
+ return f_new_sub_tlv_unknown(type, val.val.bs);
+ default:
+ cf_error("Integer or IP address expression expected");
+ }
+}
+
+static struct f_tree *
+f_new_sub_tlv_item2(u32 type, u32 v1, struct f_inst *v2)
+{
+ struct f_val val;
+ enum filter_return ret = f_eval(f_linearize(v2), cfg_mem, &val);
+
+ if (ret > F_RETURN)
+ cf_error("Runtime error while evaluating expression; see log for details");
+
+ switch(val.type) {
+ case T_IP:
+ return f_new_sub_tlv_tunnel_ep(type, v1, val.val.ip);
+ case T_BYTESTRING:
+ return f_new_sub_tlv_encap(type, v1, val.val.bs);
+ default:
+ cf_error("Integer or bytestring expression expected");
+ }
+}
+
+static struct f_tree *
f_new_ec_item(u32 kind, u32 ipv4_used, u32 key, u32 vf, u32 vt)
{
u64 fm, to;
@@ -178,6 +317,9 @@ f_generate_empty(struct f_dynamic_attr dyn)
case EAF_TYPE_LC_SET:
empty = f_const_empty_lclist;
break;
+ case EAF_TYPE_TUNNEL_ENCAP:
+ empty = f_const_empty_tlvlist;
+ break;
default:
cf_error("Can't empty that attribute");
}
@@ -275,7 +417,7 @@ CF_DECLS
CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
ACCEPT, REJECT, ERROR,
INT, BOOL, IP, TYPE, PREFIX, RD, PAIR, QUAD, EC, LC,
- SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST,
+ SET, STRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST, TLVLIST,
IF, THEN, ELSE, CASE,
TRUE, FALSE, RT, RO, UNKNOWN, GENERIC,
FROM, GW, NET, MASK, PROTO, SOURCE, SCOPE, DEST, IFNAME, IFINDEX, WEIGHT, GW_MPLS,
@@ -288,7 +430,8 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
PREPEND, FIRST, LAST, LAST_NONAGGREGATED, MATCH,
EMPTY,
FILTER, WHERE, EVAL, ATTRIBUTE,
- BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME, FORMAT)
+ BT_ASSERT, BT_TEST_SUITE, BT_CHECK_ASSIGN, BT_TEST_SAME,
+ SUBTLV, FORMAT)
%nonassoc THEN
%nonassoc ELSE
@@ -304,7 +447,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%type <ecs> ec_kind
%type <fret> break_command
%type <i32> cnum
-%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body
+%type <e> pair_item ec_item lc_item set_item switch_item set_items switch_items switch_body sub_tlv_item
%type <trie> fprefix_set
%type <v> set_atom switch_atom fipa
%type <px> fprefix
@@ -378,6 +521,8 @@ type:
| CLIST { $$ = T_CLIST; }
| ECLIST { $$ = T_ECLIST; }
| LCLIST { $$ = T_LCLIST; }
+ | SUBTLV { $$ = T_SUBTLV; }
+ | TLVLIST { $$ = T_TLVLIST; }
| type SET {
switch ($1) {
case T_INT:
@@ -387,6 +532,7 @@ type:
case T_LC:
case T_RD:
case T_IP:
+ case T_SUBTLV:
$$ = T_SET;
break;
@@ -584,10 +730,16 @@ lc_item:
{ $$ = f_new_lc_item($2, $10, $4, $12, $6, $14); }
;
+sub_tlv_item:
+ '(' SUBTLV ',' cnum ',' term ')' { $$ = f_new_sub_tlv_item($4, $6); }
+ | '(' SUBTLV ',' cnum ',' cnum ',' term ')' { $$ = f_new_sub_tlv_item2($4, $6, $8); }
+ ;
+
set_item:
pair_item
| ec_item
| lc_item
+ | sub_tlv_item
| set_atom { $$ = f_new_item($1, $1); }
| set_atom DDOT set_atom { $$ = f_new_item($1, $3); }
;
@@ -596,6 +748,7 @@ switch_item:
pair_item
| ec_item
| lc_item
+ | sub_tlv_item
| switch_atom { $$ = f_new_item($1, $1); }
| switch_atom DDOT switch_atom { $$ = f_new_item($1, $3); }
;
@@ -810,6 +963,7 @@ term:
| '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_clist); }
| '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_eclist); }
| '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_lclist); }
+ | '-' '-' '-' '-' EMPTY '-' '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, f_const_empty_tlvlist); }
| PREPEND '(' term ',' term ')' { $$ = f_new_inst(FI_PATH_PREPEND, $3, $5); }
| ADD '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_ADD, $3, $5); }
| DELETE '(' term ',' term ')' { $$ = f_new_inst(FI_CLIST_DEL, $3, $5); }
diff --git a/filter/data.c b/filter/data.c
index 7c33d2cb..78ae9431 100644
--- a/filter/data.c
+++ b/filter/data.c
@@ -8,6 +8,7 @@
*
*/
+//#define LOCAL_DEBUG
#include "nest/bird.h"
#include "lib/lists.h"
#include "lib/resource.h"
@@ -16,6 +17,7 @@
#include "lib/unaligned.h"
#include "lib/net.h"
#include "lib/ip.h"
+#include "lib/tunnel_encaps.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
@@ -53,6 +55,8 @@ static const char * const f_type_str[] = {
[T_ECLIST] = "eclist",
[T_LC] = "lc",
[T_LCLIST] = "lclist",
+ [T_SUBTLV] = "subtlv",
+ [T_TLVLIST] = "tlvlist",
[T_RD] = "rd",
};
@@ -80,6 +84,8 @@ const struct f_val f_const_empty_path = {
}, f_const_empty_lclist = {
.type = T_LCLIST,
.val.ad = &null_adata,
+}, f_const_empty_tlvlist = {
+ .type = T_TLVLIST,
};
static struct adata *
@@ -282,6 +288,8 @@ val_same(const struct f_val *v1, const struct f_val *v2)
return same_tree(v1->val.t, v2->val.t);
case T_PREFIX_SET:
return trie_same(v1->val.ti, v2->val.ti);
+ case T_TLVLIST:
+ return tlvlist_same(v1->val.tl, v2->val.tl);
default:
bug("Invalid type in val_same(): %x", v1->type);
}
@@ -382,6 +390,18 @@ lclist_match_set(const struct adata *list, const struct f_tree *set)
return 0;
}
+static int
+tlvlist_match_set(const struct adata *list, const struct f_tree *set)
+{
+ if (!list)
+ return 0;
+
+ if (!subtlv_set_type(set))
+ return F_CMP_ERROR;
+
+ return 1;
+}
+
const struct adata *
clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos)
{
@@ -483,6 +503,20 @@ lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val
return res;
}
+#if 0
+const struct te_tlvlist *
+tlvlist_filter(struct linpool *pool, const struct te_tlvlist *list, const struct f_val *set, int pos)
+{
+ if (!list)
+ return NULL;
+
+ /* FIXME */
+
+ return NULL;
+}
+#endif
+
+
/**
* val_in_range - implement |~| operator
* @v1: element
@@ -540,6 +574,9 @@ val_in_range(const struct f_val *v1, const struct f_val *v2)
if (v1->type == T_LCLIST)
return lclist_match_set(v1->val.ad, v2->val.t);
+ if (v1->type == T_TLVLIST)
+ return tlvlist_match_set(v1->val.ad, v2->val.t);
+
if (v1->type == T_PATH)
return as_path_match_set(v1->val.ad, v2->val.t);
@@ -573,6 +610,7 @@ val_format(const struct f_val *v, buffer *buf)
case T_CLIST: int_set_format(v->val.ad, 1, -1, buf2, 1000); buffer_print(buf, "(clist %s)", buf2); return;
case T_ECLIST: ec_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(eclist %s)", buf2); return;
case T_LCLIST: lc_set_format(v->val.ad, -1, buf2, 1000); buffer_print(buf, "(lclist %s)", buf2); return;
+ case T_TLVLIST: tlv_set_format(v->val.tl, -1, buf2, 1000); buffer_print(buf, "(tlvlist %s)", buf2); return;
case T_PATH_MASK: pm_format(v->val.path_mask, buf); return;
default: buffer_print(buf, "[unknown type %x]", v->type); return;
}
@@ -598,4 +636,3 @@ val_dump(const struct f_val *v) {
val_format(v, &b);
return val_dump_buffer;
}
-
diff --git a/filter/data.h b/filter/data.h
index a42da941..529c6cb0 100644
--- a/filter/data.h
+++ b/filter/data.h
@@ -59,6 +59,8 @@ enum f_type {
T_RD = 0x2a, /* Route distinguisher for VPN addresses */
T_PATH_MASK_ITEM = 0x2b, /* Path mask item for path mask constructors */
T_BYTESTRING = 0x2c,
+ T_SUBTLV = 0x2d, /* Tunnel encapsulation sub-TLV */
+ T_TLVLIST = 0x2e, /* Tunnel encapsulation TLV list */
T_SET = 0x80,
T_PREFIX_SET = 0x81,
@@ -80,6 +82,9 @@ struct f_val {
const struct f_path_mask *path_mask;
struct f_path_mask_item pmi;
const struct bytestring *bs;
+ struct te_subtlv st;
+ const struct te_tlv *tlv;
+ const struct te_tlvlist *tl;
} val;
};
@@ -205,10 +210,13 @@ static inline int eclist_set_type(const struct f_tree *set)
{ return set->from.type == T_EC; }
static inline int lclist_set_type(const struct f_tree *set)
{ return set->from.type == T_LC; }
+static inline int subtlv_set_type(const struct f_tree *set)
+{ return set->from.type == T_SUBTLV; }
const struct adata *clist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
const struct adata *eclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
const struct adata *lclist_filter(struct linpool *pool, const struct adata *list, const struct f_val *set, int pos);
+const struct te_tlvlist *tlvlist_filter(struct linpool *pool, const struct te_tlvlist *list, const struct f_val *set, int pos);
/* Special undef value for paths and clists */
@@ -220,7 +228,7 @@ undef_value(struct f_val v)
(v.val.ad == &null_adata);
}
-extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist;
+extern const struct f_val f_const_empty_path, f_const_empty_clist, f_const_empty_eclist, f_const_empty_lclist, f_const_empty_tlvlist;
enum filter_return f_eval(const struct f_line *expr, struct linpool *tmp_pool, struct f_val *pres);
diff --git a/filter/f-inst.c b/filter/f-inst.c
index b876a937..4f3b6f61 100644
--- a/filter/f-inst.c
+++ b/filter/f-inst.c
@@ -676,6 +676,13 @@
break;
}
+ /* The same special case for tunnel encap */
+ if (da.type == EAF_TYPE_TUNNEL_ENCAP) {
+ RESULT_(T_TLVLIST, tl, NULL);
+ runtime("Can't get null tunnel encap");
+ break;
+ }
+
/* Undefined value */
RESULT_VOID;
break;
@@ -712,6 +719,15 @@
case EAF_TYPE_UNDEF:
RESULT_VOID;
break;
+ case EAF_TYPE_TUNNEL_ENCAP:
+ {
+ struct te_tlvlist *decoded_tl = tlvlist_decode_tunnel_encap(fpool, e->u.ptr);
+ if (!decoded_tl)
+ runtime( "Tunnel encapsulation decoder error" );
+ RESULT_(T_TLVLIST, tl, decoded_tl);
+ break;
+ }
+
default:
bug("Unknown dynamic attribute type");
}
@@ -772,6 +788,16 @@
}
break;
+ case EAF_TYPE_TUNNEL_ENCAP:
+ {
+ if (v1.type != T_TLVLIST)
+ runtime( "Setting tunnel encap attribute to non-tlvlist value %d", v1.type );
+ l->attrs[0].u.ptr = tlvlist_encode_tunnel_encap(fpool, v1.val.tl);
+ if (!l->attrs[0].u.ptr)
+ runtime( "Tunnel encapsulation encoder error" );
+ break;
+ }
+
default:
bug("Unknown dynamic attribute type");
}
@@ -1102,6 +1128,18 @@
}
+ else if (v1.type == T_TLVLIST)
+ {
+ /* v2.val is either TLV or TLV-set */
+ if ((v2.type == T_SET) && subtlv_set_type(v2.val.t))
+ RESULT_(T_TLVLIST, tl, [[ tlv_set_add(fpool, v1.val.tl, tlv_alloc(fpool, v2.val.t)) ]]);
+ //runtime("Can't add set");
+ else if (v2.type == T_TLVLIST)
+ RESULT_(T_TLVLIST, tl, [[ tlv_set_union(fpool, v1.val.tl, v2.val.tl) ]]);
+ else
+ runtime("Can't add non-tlv");
+ }
+
else
runtime("Can't add to non-[e|l]clist");
}
@@ -1164,6 +1202,17 @@
RESULT_(T_LCLIST, ad, [[ lc_set_del(fpool, v1.val.ad, v2.val.lc) ]]);
}
+#if 0
+ else if (v1.type == T_TLVLIST)
+ {
+ /* v2.val is sub-TLV-set */
+ if ((v2.type == T_SET) && subtlv_set_type(v2.val.t))
+ RESULT_(T_TLVLIST, tl, [[ tlvlist_filter(fpool, v1.val.tl, &v2, 0) ]]);
+ else
+ runtime("Can't delete non-sub-TLV-set");
+ }
+#endif
+
else
runtime("Can't delete in non-[e|l]clist");
}
@@ -1212,6 +1261,17 @@
runtime("Can't filter lc");
}
+#if 0
+ else if (v1.type == T_TLVLIST)
+ {
+ /* v2.val is either TLV or sub-TLV-set */
+ if ((v2.type == T_SET) && subtlv_set_type(v2.val.t) || (v2.type == T_TLVLIST))
+ RESULT_(T_TLVLIST, tl, [[ tlvlist_filter(fpool, v1.val.tl, &v2, 1) ]]);
+ else
+ runtime("Can't filter tlv");
+ }
+#endif
+
else
runtime("Can't filter non-[e|l]clist");
}
diff --git a/lib/Makefile b/lib/Makefile
index 360f21fd..3ccea1cb 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,4 +1,4 @@
-src := base64.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c xmalloc.c
+src := base64.c bitmap.c bitops.c blake2s.c blake2b.c checksum.c event.c flowspec.c idm.c ip.c lists.c mac.c md5.c mempool.c net.c patmatch.c printf.c resource.c sha1.c sha256.c sha512.c slab.c slists.c strtoul.c tbf.c timer.c tunnel_encaps.c xmalloc.c
obj := $(src-o-files)
$(all-daemon)
diff --git a/lib/tunnel_encaps.c b/lib/tunnel_encaps.c
new file mode 100644
index 00000000..e4298943
--- /dev/null
+++ b/lib/tunnel_encaps.c
@@ -0,0 +1,856 @@
+#define LOCAL_DEBUG
+
+#include <stdlib.h>
+#include "lib/tunnel_encaps.h"
+
+struct format_fn {
+ node n;
+ int type;
+ const char *name;
+ format_tunnel_encap_fn cb;
+};
+
+list format_fns;
+
+static format_tunnel_encap_fn lookup_format_tunnel_encap_cb(int type);
+static const char *lookup_format_tunnel_encap_name(int type);
+static int default_format_tunnel_encap(const struct te_encap *encap, byte *buf, uint size);
+
+static
+int walk_encap(const void *p, size_t sub_tlv_len, const struct te_visitor *visitor, struct te_context *ctx)
+{
+ struct te_encap encap;
+ memset(&encap, 0, sizeof(encap));
+
+ encap.type = 0;
+ encap.length = sub_tlv_len;
+ encap.data = (void *)p;
+ return visitor->visit_encap ? visitor->visit_encap(&encap, ctx) : 0;
+}
+
+static
+int walk_color(const void *p, size_t sub_tlv_len, const struct te_visitor *visitor, struct te_context *ctx)
+{
+ if (sub_tlv_len != 8) {
+ DBG(L_TRACE "WG: color len error %d", sub_tlv_len);
+ return -1;
+ }
+
+ if (get_u16(p) != 0x030b) {
+ DBG(L_TRACE "WG: color error %04x", get_u16(p));
+ return -1;
+ }
+
+ return visitor->visit_color ? visitor->visit_color(get_u32(p+4), ctx) : 0;
+}
+
+static
+int walk_udp_dest_port(const void *p, size_t sub_tlv_len, const struct te_visitor *visitor, struct te_context *ctx)
+{
+ if (sub_tlv_len != 2) {
+ DBG(L_TRACE "WG: udp dest port len error %d", sub_tlv_len);
+ return -1;
+ }
+
+ return visitor->visit_udp_dest_port ? visitor->visit_udp_dest_port(get_u16(p), ctx) : 0;
+}
+
+static
+int walk_tunnel_ep(const void *p, size_t sub_tlv_len, const struct te_visitor *visitor, struct te_context *ctx)
+{
+ if (sub_tlv_len < 6) {
+ DBG(L_TRACE "WG: tunnel ep len error");
+ return -1;
+ }
+
+ struct te_endpoint ep;
+ memset(&ep, 0, sizeof(ep));
+
+ ep.reserved = get_u32(p);
+ u16 af = get_u16(p + 4);
+ ep.af = af;
+ switch (af) {
+ case 0:
+ if (sub_tlv_len != 6) {
+ DBG(L_TRACE "WG: Fam 0 len error %d", sub_tlv_len);
+ return -1;
+ }
+ ep.ip = IP6_NONE;
+ break;
+ case AFI_IPV4:
+ if (sub_tlv_len != 10) {
+ DBG(L_TRACE "WG: IPv4 len error %d", sub_tlv_len);
+ return -1;
+ }
+ ep.ip = ipa_from_ip4(get_ip4(p + 6));
+ break;
+ case AFI_IPV6:
+ if (sub_tlv_len != 22) {
+ DBG(L_TRACE "WG: IPv6 len error %d", sub_tlv_len);
+ return -1;
+ }
+ ep.ip = ipa_from_ip6(get_ip6(p + 6));
+ break;
+ default:
+ DBG(L_TRACE "WG: Address family error %d", af);
+ return -1;
+ }
+
+ return visitor->visit_ep ? visitor->visit_ep(&ep, ctx) : 0;
+}
+
+static
+int walk_unknown(const void *p, int type, size_t sub_tlv_len, const struct te_visitor *visitor, struct te_context *ctx)
+{
+ struct te_unknown unknown;
+ memset(&unknown, 0, sizeof(unknown));
+
+ unknown.length = sub_tlv_len;
+ unknown.data = (void *)p;
+ return visitor->visit_unknown ? visitor->visit_unknown(type, &unknown, ctx) : 0;
+}
+
+static
+int walk_sub_tlv(const u8 *p, size_t len, const struct te_visitor *visitor, struct te_context *ctx)
+{
+ if (len < 2)
+ {
+ DBG(L_TRACE "WG: sub_tlv len error %d", len);
+ return -1;
+ }
+
+ const u8 *first = p;
+ const u8 *last = p + len;
+ int type = get_u8(p++);
+ u16 sub_tlv_len = 0;
+
+ if (type >= 0 && type <= 127)
+ {
+ sub_tlv_len = get_u8(p);
+ p++;
+ }
+ else if (type >= 128 && type <= 255)
+ {
+ if (len < 3)
+ {
+ DBG(L_TRACE "WG: sub_tlv len error %d", len);
+ return -1;
+ }
+
+ sub_tlv_len = get_u16(p);
+ p += 2;
+ }
+ else
+ {
+ DBG(L_TRACE "WG: sub_tlv type error %d", type);
+ return -1;
+ }
+
+ if (p + sub_tlv_len > last)
+ {
+ DBG(L_TRACE "WG: sub_tlv value len error %d", sub_tlv_len);
+ return -1;
+ }
+
+ if (visitor->visit_subtlv && visitor->visit_subtlv(type, ctx) < 0)
+ return p - first + sub_tlv_len;
+
+ int res = 0;
+
+ switch (type) {
+ case BGP_TUNNEL_ENCAP_A_SUB_TLV_ENCAP:
+ res = walk_encap(p, sub_tlv_len, visitor, ctx);
+ break;
+ case BGP_TUNNEL_ENCAP_A_SUB_TLV_TUNNEL_EP:
+ res = walk_tunnel_ep(p, sub_tlv_len, visitor, ctx);
+ break;
+ case BGP_TUNNEL_ENCAP_A_SUB_TLV_COLOR:
+ res = walk_color(p, sub_tlv_len, visitor, ctx);
+ break;
+ case BGP_TUNNEL_ENCAP_A_SUB_TLV_UDP_DEST_PORT:
+ res = walk_udp_dest_port(p, sub_tlv_len, visitor, ctx);
+ break;
+ default:
+ res = walk_unknown(p, type, sub_tlv_len, visitor, ctx);
+ break;
+ }
+
+ if (res < 0)
+ {
+ DBG(L_TRACE "WG: Decode error");
+ return res;
+ }
+
+ /* TODO use return value? */
+ if (visitor->visit_subtlv_end)
+ visitor->visit_subtlv_end(type, ctx);
+
+ return p - first + sub_tlv_len;
+}
+
+static
+int walk_sub_tlvs(const u8 *p, size_t size, const struct te_visitor *visitor, struct te_context *ctx)
+{
+ const u8 *pos = p;
+
+ while (size > 0) {
+ int l = walk_sub_tlv(pos, size, visitor, ctx);
+
+ if (l < 0) {
+ DBG(L_TRACE "WG: decode error %d", l);
+ return l;
+ }
+
+ ADVANCE(pos, size, l);
+ }
+
+ return pos - p;
+}
+
+static
+int walk_tlv(const u8 *p, size_t len, const struct te_visitor *visitor, struct te_context *ctx)
+{
+ const u8 *pos = p;
+ int l;
+
+ if (len < 4)
+ {
+ DBG(L_TRACE "WG: tunnel_encap len error %d", len);
+ return -1;
+ }
+
+ u16 type = get_u16(pos);
+ ADVANCE(pos, len, 2);
+
+ u16 value_length = get_u16(pos);
+ ADVANCE(pos, len, 2);
+
+ if (len < value_length)
+ {
+ DBG(L_ERR "WG: tunnel encap len error %d", value_length);
+ return -1;
+ }
+
+ ctx->sub_tlvs_pos = pos;
+ ctx->sub_tlvs_len = value_length;
+
+ if (visitor->visit_tlv && visitor->visit_tlv(type, ctx) < 0)
+ return 0;
+
+ l = walk_sub_tlvs(pos, value_length, visitor, ctx);
+ if (l < 0)
+ {
+ DBG(L_ERR "WG: sub-TLVs decode error");
+ return l;
+ }
+
+ if (l != value_length)
+ {
+ DBG(L_ERR "WG: TLV length error");
+ return -1;
+ }
+
+ ADVANCE(pos, len, l);
+
+ if (visitor->visit_tlv_end && visitor->visit_tlv_end(ctx) < 0)
+ return 0;
+
+ ctx->sub_tlvs_pos = NULL;
+ ctx->sub_tlvs_len = 0;
+
+ return pos - p;
+}
+
+int walk_tunnel_encap(const struct adata *a, const struct te_visitor *visitor, void *opaque)
+{
+ const u8 *p = a->data;
+ const u8 *pos = p;
+ uint size = a->length;
+ struct te_context ctx = { .opaque = opaque, };
+
+ while (size > 0)
+ {
+ int l = walk_tlv(p, size, visitor, &ctx);
+
+ if (l < 0)
+ {
+ DBG(L_ERR "WG: TLV decode error");
+ return l;
+ }
+
+ ADVANCE(pos, size, l);
+ }
+
+ return 0;
+}
+
+struct count_subtlvs {
+ uint count;
+};
+
+static int
+visitor_count_subtlv(int UNUSED type, struct te_context *ctx){
+ struct count_subtlvs *data = ctx->opaque;
+
+ data->count++;
+ return 0;
+}
+
+int te_count_subtlvs(struct te_context *ctx)
+{
+ struct count_subtlvs data = { .count = 0, };
+ struct te_context new_ctx = { .opaque = &data, };
+ struct te_visitor visitor = { .visit_subtlv = visitor_count_subtlv, };
+
+ if (walk_sub_tlvs(ctx->sub_tlvs_pos, ctx->sub_tlvs_len, &visitor, &new_ctx) < 0)
+ return -1;
+
+ return data.count;
+}
+
+struct {
+ int type;
+ const char *name;
+} tunnel_types[] = {
+ {0, "Reserved"},
+ {1, "L2TPv3 over IP"},
+ {2, "GRE"},
+ {3, "Transmit tunnel endpoint (DEPRECATED)"},
+ {4, "IPsec in Tunnel-mode (DEPRECATED)"},
+ {5, "IP in IP tunnel with IPsec Transport Mode (DEPRECATED)"},
+ {6, "MPLS-in-IP tunnel with IPsec Transport Mode (DEPRECATED)"},
+ {7, "IP in IP"},
+ {8, "VXLAN Encapsulation"},
+ {9, "NVGRE Encapsulation"},
+ {10, "MPLS Encapsulation"},
+ {11, "MPLS in GRE Encapsulation"},
+ {12, "VXLAN GPE Encapsulation"},
+ {13, "MPLS in UDP Encapsulation"},
+ {14, "IPv6 Tunnel"},
+ {15, "SR TE Policy Type"},
+ {16, "Bare"},
+ {17, "SR Tunnel (DEPRECATED)"},
+ {18, "Cloud Security"},
+ {19, "Geneve Encapsulation"},
+ {20, "Any-Encapsulation"},
+ {21, "GTP Tunnel Type"},
+};
+
+const uint num_tunnel_types = sizeof(tunnel_types)/sizeof(tunnel_types[0]);
+
+static const char *
+lookup_name(int type)
+{
+ for (uint i = 0; i < num_tunnel_types; i++) {
+ if (tunnel_types[i].type == type)
+ return tunnel_types[i].name;
+ }
+
+ const char *name = lookup_format_tunnel_encap_name(type);
+ return name ? name : "Unassigned";
+}
+
+static int visitor_format_tlv(int type, struct te_context *ctx)
+{
+ struct te_buffer *buf = ctx->opaque;
+ const char *name = lookup_name(type);
+
+ int l = bsnprintf(buf->pos, buf->size, "%s{type: %s(%d)", buf->tlv?"}, ":"", name, type);
+ if (l < 0)
+ {
+ DBG(L_ERR "WG: bsnprintf error");
+ return -1;
+ }
+
+ buf->tlv++;
+ buf->tlv_type = type;
+ buf->subtlv=0;
+ ADVANCE(buf->pos, buf->size, l);
+ return 0;
+}
+
+static int visitor_format_subtlv(int UNUSED type, struct te_context *ctx)
+{
+ struct te_buffer *buf = ctx->opaque;
+
+ int l = bsnprintf(buf->pos, buf->size, ", ");
+ if (l < 0)
+ return -1;
+ ADVANCE(buf->pos, buf->size, l);
+ return 0;
+}
+
+static int visitor_format_encap(const struct te_encap *encap, struct te_context *ctx)
+{
+ struct te_buffer *buf = ctx->opaque;
+ int l = bsnprintf(buf->pos, buf->size, "encap: ");
+ if (l < 0)
+ return -1;
+ ADVANCE(buf->pos, buf->size, l);
+
+ l = lookup_format_tunnel_encap_cb(buf->tlv_type)(encap, buf->pos, buf->size);
+ if (l < 0)
+ l = default_format_tunnel_encap(encap, buf->pos, buf->size);
+
+ if (l < 0)
+ {
+ DBG(L_ERR "WG: Encapsulation format error");
+ return l;
+ }
+
+ ADVANCE(buf->pos, buf->size, l);
+ return 0;
+}
+
+static int visitor_format_ep(const struct te_endpoint *ep, struct te_context *ctx)
+{
+ struct te_buffer *buf = ctx->opaque;
+ int l = bsnprintf(buf->pos, buf->size, "ep: %I", ep->ip);
+ if (l < 0)
+ return -1;
+ ADVANCE(buf->pos, buf->size, l);
+ return 0;
+}
+
+static int visitor_format_color(u32 color, struct te_context *ctx)
+{
+ struct te_buffer *buf = ctx->opaque;
+ int l = bsnprintf(buf->pos, buf->size, "color: %d", color);
+ if (l < 0)
+ return -1;
+ ADVANCE(buf->pos, buf->size, l);
+ return 0;
+}
+
+static int visitor_format_udp_dest_port(u16 udp_dest_port, struct te_context *ctx)
+{
+ struct te_buffer *buf = ctx->opaque;
+ int l = bsnprintf(buf->pos, buf->size, "udp: %d", udp_dest_port);
+ if (l < 0)
+ return -1;
+ ADVANCE(buf->pos, buf->size, l);
+ return 0;
+}
+
+static int visitor_format_unknown(int type, const struct te_unknown *unknown, struct te_context *ctx)
+{
+ struct te_buffer *buf = ctx->opaque;
+ const byte *data = unknown->data;
+ int l = bsnprintf(buf->pos, buf->size, "unknown(%d): ", type);
+ if (l < 0)
+ return -1;
+ ADVANCE(buf->pos, buf->size, l);
+
+ int first = 1;
+ for (uint i = 0; i < unknown->length; i++) {
+ if (buf->size < 4) {
+ DBG(L_ERR "WG: Format unknown sub-TLV error");
+ return -1;
+ }
+
+ int l = bsnprintf(buf->pos, buf->size, "%s%02x", first?"":" ", data[i]);
+ ADVANCE(buf->pos, buf->size, l);
+ first = 0;
+ }
+
+ return 0;
+}
+
+struct te_visitor format_visitor =
+ {
+ .visit_tlv = visitor_format_tlv,
+ .visit_subtlv = visitor_format_subtlv,
+ .visit_encap = visitor_format_encap,
+ .visit_ep = visitor_format_ep,
+ .visit_color = visitor_format_color,
+ .visit_udp_dest_port = visitor_format_udp_dest_port,
+ .visit_unknown = visitor_format_unknown,
+ };
+
+const struct te_visitor *te_get_format_visitor(void)
+{
+ return &format_visitor;
+}
+
+int format_tunnel_encap(const struct adata *a, byte *buf, uint size)
+{
+ struct te_buffer buffer = {.pos=buf, .size=size, .tlv=0, .subtlv=0};
+ int l = bsnprintf(buffer.pos, buffer.size, "[");
+ if (l < 0)
+ return -1;
+ ADVANCE(buffer.pos, buffer.size, l);
+
+ walk_tunnel_encap(a, &format_visitor, &buffer);
+
+ l = bsnprintf(buffer.pos, buffer.size, "%s]", buffer.tlv?"}":"");
+ if (l < 0)
+ return -1;
+ ADVANCE(buffer.pos, buffer.size, l);
+ return buffer.pos - buf;
+}
+
+static int default_format_tunnel_encap(const struct te_encap *encap, byte *buf, uint size)
+{
+ byte *pos = buf;
+ const byte *data = encap->data;
+ int first = 1;
+
+ for (uint i = 0; i < encap->length; i++) {
+ if (size < 4) {
+ DBG(L_ERR "WG: Default format tunnel encap error");
+ return pos - buf;
+ }
+
+ int l = bsnprintf(pos, size, "%s%02x", first?"":" ", data[i]);
+ ADVANCE(pos, size, l);
+
+ first = 0;
+ }
+
+ return pos - buf;
+}
+
+static struct format_fn *lookup_format_tunnel_encap(int type)
+{
+ struct format_fn *n;
+ WALK_LIST(n, format_fns) {
+ if (n->type == type)
+ return n;
+ }
+
+ return NULL;
+}
+
+static const char *lookup_format_tunnel_encap_name(int type)
+{
+ struct format_fn *fn = lookup_format_tunnel_encap(type);
+
+ if (fn && fn->name)
+ return fn->name;
+
+ return NULL;
+}
+
+static format_tunnel_encap_fn lookup_format_tunnel_encap_cb(int type)
+{
+ struct format_fn *fn = lookup_format_tunnel_encap(type);;
+
+ if (fn && fn->cb)
+ return fn->cb;
+
+ DBG("lookup_format_tunnel_encap not found: %d\n", type);
+ return default_format_tunnel_encap;
+}
+
+struct tlv_buffer {
+ uint len;
+ uint size;
+ byte *p;
+ byte *tlv_len_p;
+ byte *subtlv_len_p;
+};
+
+static int calc_tlv(int type, struct te_context *ctx)
+{
+ struct tlv_buffer *buf = ctx->opaque;
+ /*
+ Header:
+ Tunnel Type (2 Octets)
+ Length (2 Octets)
+ Value (Variable)
+ */
+ buf->len += 4;
+
+ if (!buf->size)
+ return 0;
+
+ if (buf->len > buf->size)
+ return -1;
+
+ if (type <= 0 || type > 65535)
+ return -1;
+
+ put_u16(buf->p, type);
+ buf->p += 2;
+
+ buf->tlv_len_p = buf->p;
+
+ put_u16(buf->p, 0xcccc); /* Update in calc_tlv_end */
+ buf->p += 2;
+ return 0;
+}
+
+static int calc_tlv_end(struct te_context *ctx)
+{
+ struct tlv_buffer *buf = ctx->opaque;
+
+ if (!buf->size)
+ return 0;
+
+ if (!buf->tlv_len_p)
+ return -1;
+
+ put_u16(buf->tlv_len_p, buf->p - buf->tlv_len_p - 2);
+ buf->tlv_len_p = NULL;
+ return 0;
+}
+
+static int calc_subtlv(int type, struct te_context *ctx)
+{
+ struct tlv_buffer *buf = ctx->opaque;
+ /*
+ Sub-TLV Type (1 Octet)
+ Sub-TLV Length (1 or 2 Octets)
+ */
+ if (type >= 0 && type <= 127)
+ buf->len += 2;
+ else if (type >= 128 && type <= 255)
+ buf->len += 3;
+ else
+ {
+ DBG(L_ERR "calc_subtlv bad type %d\n", type );
+ return -1;
+ }
+
+ if (!buf->size)
+ return 0;
+
+ if (buf->len > buf->size)
+ return -1;
+
+ put_u8(buf->p, type);
+ buf->p++;
+
+ buf->subtlv_len_p = buf->p;
+
+ if (type >= 0 && type <= 127)
+ {
+ put_u8(buf->p, 0xdd); /* Update in calc_subtlv_end */
+ buf->p++;
+ }
+ else
+ {
+ put_u16(buf->p, 0xeeee); /* Update in calc_subtlv_end */
+ buf->p += 2;
+ }
+
+ return 0;
+}
+
+static int calc_subtlv_end(int type, struct te_context *ctx)
+{
+ struct tlv_buffer *buf = ctx->opaque;
+
+ if (!buf->size)
+ return 0;
+
+ if (!buf->subtlv_len_p)
+ return -1;
+
+ if (type >= 0 && type <= 127)
+ {
+ put_u8(buf->subtlv_len_p, buf->p - buf->subtlv_len_p - 1);
+ buf->subtlv_len_p = NULL;
+ }
+ else if (type >= 128 && type <= 255)
+ {
+ put_u16(buf->subtlv_len_p, buf->p - buf->subtlv_len_p - 2);
+ buf->subtlv_len_p = NULL;
+ }
+ else
+ return -1;
+
+ return 0;
+}
+
+static int calc_encap(const struct te_encap *encap, struct te_context *ctx)
+{
+ struct tlv_buffer *buf = ctx->opaque;
+ /*
+ Sub-TLV Value (Variable)
+ */
+ if (encap->type <= 0)
+ {
+ DBG(L_ERR "calc_encap bad type %d\n", encap->type );
+ return -1;
+ }
+
+ buf->len += encap->length;
+
+ if (!buf->size)
+ return 0;
+
+ if (buf->len > buf->size)
+ return -1;
+
+ memcpy(buf->p, encap->data, encap->length);
+ buf->p += encap->length;
+ return 0;
+}
+
+static int calc_ep(const struct te_endpoint *ep, struct te_context *ctx)
+{
+ struct tlv_buffer *buf = ctx->opaque;
+ /*
+ Sub-TLV Value (10 or 22 Octets)
+ */
+ if (ep->af == AFI_IPV4 && ipa_is_ip4(ep->ip))
+ buf->len += 10;
+ else if (ep->af == AFI_IPV6 && ipa_is_ip6(ep->ip))
+ buf->len += 22;
+ else if (ep->af == 0)
+ buf->len += 6;
+ else
+ {
+ DBG(L_ERR "calc_encap bad af %04x\n", ep->af );
+ return -1;
+ }
+
+ if (!buf->size)
+ return 0;
+
+ if (buf->len > buf->size)
+ return -1;
+
+ put_u32(buf->p, ep->reserved);
+ buf->p += 4;
+
+ put_u16(buf->p, ep->af);
+ buf->p += 2;
+
+ switch (ep->af) {
+ case AFI_IPV4:
+ put_ip4(buf->p, ipa_to_ip4(ep->ip));
+ buf->p += 4;
+ break;
+ case AFI_IPV6:
+ put_ip6(buf->p, ipa_to_ip6(ep->ip));
+ buf->p += 16;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int calc_color(u32 color, struct te_context *ctx)
+{
+ struct tlv_buffer *buf = ctx->opaque;
+ /*
+ Sub-TLV Value (8 Octets)
+ */
+ buf->len += 8;
+
+ if (!buf->size)
+ return 0;
+
+ if (buf->len > buf->size)
+ return -1;
+
+ put_u32(buf->p, 0x030b0000);
+ buf->p += 4;
+
+ put_u32(buf->p, color);
+ buf->p += 4;
+ return 0;
+}
+
+static int calc_udp_dest_port(u16 UNUSED udp_dest_port, struct te_context *ctx)
+{
+ struct tlv_buffer *buf = ctx->opaque;
+ /*
+ Sub-TLV Value (2 Octets)
+ */
+ buf->len += 2;
+
+ if (!buf->size)
+ return 0;
+
+ if (buf->len > buf->size)
+ return -1;
+
+ put_u16(buf->p, udp_dest_port);
+ buf->p += 2;
+ return 0;
+}
+
+static int calc_unknown(int UNUSED type, const struct te_unknown *unknown, struct te_context *ctx)
+{
+ struct tlv_buffer *buf = ctx->opaque;
+ /*
+ Sub-TLV Value (Variable)
+ */
+
+ buf->len += unknown->length;
+
+ if (!buf->size)
+ return 0;
+
+ if (buf->len > buf->size)
+ return -1;
+
+ memcpy(buf->p, unknown->data, unknown->length);
+ buf->p += unknown->length;
+ return 0;
+}
+
+struct te_visitor encode_visitor =
+ {
+ .visit_tlv = calc_tlv,
+ .visit_tlv_end = calc_tlv_end,
+ .visit_subtlv = calc_subtlv,
+ .visit_subtlv_end = calc_subtlv_end,
+ .visit_encap = calc_encap,
+ .visit_ep = calc_ep,
+ .visit_color = calc_color,
+ .visit_udp_dest_port = calc_udp_dest_port,
+ .visit_unknown = calc_unknown,
+ };
+
+const struct te_visitor *te_get_encode_visitor(void)
+{
+ return &encode_visitor;
+}
+
+int te_init_encode_visitor(struct tlv_buffer *buf, byte *p, uint size)
+{
+ if (!buf)
+ return sizeof(struct te_buffer);
+
+ memset(buf, 0, sizeof(struct te_buffer));
+ buf->size = size;
+ buf->p = p;
+
+ return 0;
+}
+
+uint te_get_encode_length(struct tlv_buffer *buf)
+{
+ return buf->len;
+}
+
+void register_format_tunnel_encap(int type, const char *name, format_tunnel_encap_fn cb)
+{
+ struct format_fn *fn = malloc(sizeof(struct format_fn));
+ memset(fn, 0, sizeof(*fn));
+ fn->type = type;
+ fn->name = name;
+ fn->cb = cb;
+ add_head(&format_fns, &fn->n);
+}
+
+void unregister_format_tunnel_encap(int type, format_tunnel_encap_fn cb)
+{
+ struct format_fn *n, *nxt;
+ WALK_LIST_DELSAFE(n, nxt, format_fns) {
+ if (n->type == type && n->cb == cb) {
+ rem_node(&n->n);
+ }
+ }
+}
+
+void tunnel_encap_init()
+{
+ init_list(&format_fns);
+}
diff --git a/lib/tunnel_encaps.h b/lib/tunnel_encaps.h
new file mode 100644
index 00000000..fc7bbda6
--- /dev/null
+++ b/lib/tunnel_encaps.h
@@ -0,0 +1,63 @@
+#ifndef _BIRD_TUNNEL_ENCAPS_
+#define _BIRD_TUNNEL_ENCAPS_
+
+#include "nest/attrs.h"
+#include "nest/route.h"
+
+#define BA_TUNNEL_ENCAP 0x17
+
+#define BGP_TUNNEL_ENCAP_A_SUB_TLV_ENCAP 1
+#define BGP_TUNNEL_ENCAP_A_SUB_TLV_COLOR 4
+#define BGP_TUNNEL_ENCAP_A_SUB_TLV_TUNNEL_EP 6
+#define BGP_TUNNEL_ENCAP_A_SUB_TLV_UDP_DEST_PORT 8
+
+#define FLAG_BGP_TUNNEL_ENCAP_A_SUB_TLV_ENCAP (1<<BGP_TUNNEL_ENCAP_A_SUB_TLV_ENCAP)
+#define FLAG_BGP_TUNNEL_ENCAP_A_SUB_TLV_COLOR (1<<BGP_TUNNEL_ENCAP_A_SUB_TLV_COLOR)
+#define FLAG_BGP_TUNNEL_ENCAP_A_SUB_TLV_TUNNEL_EP (1<<BGP_TUNNEL_ENCAP_A_SUB_TLV_TUNNEL_EP)
+#define FLAG_BGP_TUNNEL_ENCAP_A_SUB_TLV_UDP_DEST_PORT (1<<BGP_TUNNEL_ENCAP_A_SUB_TLV_UDP_DEST_PORT)
+
+struct te_context {
+ void *opaque;
+ const byte *sub_tlvs_pos;
+ uint sub_tlvs_len;
+};
+
+struct te_visitor {
+ int (*visit_tlv)(int type, struct te_context *ctx);
+ int (*visit_tlv_end)(struct te_context *ctx);
+ int (*visit_subtlv)(int type, struct te_context *ctx);
+ int (*visit_subtlv_end)(int type, struct te_context *ctx);
+ int (*visit_encap)(const struct te_encap *encap, struct te_context *ctx);
+ int (*visit_ep)(const struct te_endpoint *ep, struct te_context *ctx);
+ int (*visit_color)(u32 color, struct te_context *ctx);
+ int (*visit_udp_dest_port)(u16 port, struct te_context *ctx);
+ int (*visit_unknown)(int type, const struct te_unknown *unknown, struct te_context *ctx);
+};
+
+struct te_buffer {
+ byte *pos;
+ uint size;
+ int tlv;
+ int tlv_type;
+ int subtlv;
+};
+
+struct tlv_buffer;
+
+typedef int (*format_tunnel_encap_fn)(const struct te_encap *, byte *buf, uint size);
+
+void tunnel_encap_init(void);
+int walk_tunnel_encap(const struct adata *a, const struct te_visitor *visitor, void *opaque);
+int te_count_subtlvs(struct te_context *ctx);
+
+const struct te_visitor *te_get_format_visitor(void);
+int format_tunnel_encap(const struct adata *a, byte *buf, uint size);
+
+const struct te_visitor *te_get_encode_visitor(void);
+int te_init_encode_visitor(struct tlv_buffer *buf, byte *p, uint size);
+uint te_get_encode_length(struct tlv_buffer *buf);
+
+void register_format_tunnel_encap(int type, const char *name, format_tunnel_encap_fn cb);
+void unregister_format_tunnel_encap(int type, format_tunnel_encap_fn cb);
+
+#endif /* _BIRD_TUNNEL_ENCAPS_ */
diff --git a/lib/unaligned.h b/lib/unaligned.h
index dfe0906f..0d3c83f9 100644
--- a/lib/unaligned.h
+++ b/lib/unaligned.h
@@ -17,8 +17,9 @@
* if possible.
*/
+#include <memory.h>
+#include "sysdep/config.h"
#include "sysdep/unix/endian.h"
-#include "lib/string.h"
static inline u8
get_u8(const void *p)
diff --git a/nest/Makefile b/nest/Makefile
index 884d3950..801e52fd 100644
--- a/nest/Makefile
+++ b/nest/Makefile
@@ -1,4 +1,4 @@
-src := a-path.c a-set.c cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c
+src := a-path.c a-set.c a-tlv.c cli.c cmds.c iface.c locks.c neighbor.c password.c proto.c rt-attr.c rt-dev.c rt-fib.c rt-show.c rt-table.c
obj := $(src-o-files)
$(all-daemon)
$(cf-local)
diff --git a/nest/a-tlv.c b/nest/a-tlv.c
new file mode 100644
index 00000000..a26ffd4e
--- /dev/null
+++ b/nest/a-tlv.c
@@ -0,0 +1,418 @@
+#include "lib/tunnel_encaps.h"
+#include "filter/data.h"
+
+static int
+walk_tlvlist(const struct te_tlvlist *set, const struct te_visitor *visitor, void *opaque)
+{
+ if (!set)
+ return 0;
+
+ struct te_tlv *t;
+ struct te_context ctx = { .opaque = opaque, };
+
+ WALK_LIST(t, set->tlv)
+ {
+ if (visitor->visit_tlv && visitor->visit_tlv(t->type, &ctx) < 0)
+ continue;
+
+ for (uint j=0; j < t->len; j++) {
+ const struct te_subtlv *st = &t->st[j];
+
+ if (visitor->visit_subtlv && visitor->visit_subtlv(st->type, &ctx) < 0)
+ continue;
+
+ switch (st->type) {
+ case TLV_ENCAPSULATION:
+ if (visitor->visit_encap && visitor->visit_encap(&st->u.tunnel_encap, &ctx) < 0)
+ return -1;
+ break;
+ case TLV_COLOR:
+ if (visitor->visit_color && visitor->visit_color(st->u.color, &ctx) < 0)
+ return -1;
+ break;
+ case TLV_TUNNEL_ENDPOINT:
+ if (visitor->visit_ep && visitor->visit_ep(&st->u.tunnel_endpoint, &ctx) < 0)
+ return -1;
+ break;
+ case TLV_UDP_DEST_PORT:
+ if (visitor->visit_udp_dest_port && visitor->visit_udp_dest_port(st->u.udp_dest_port, &ctx) < 0)
+ return -1;
+ break;
+ default:
+ if (visitor->visit_unknown && visitor->visit_unknown(st->type, &st->u.unknown, &ctx) < 0)
+ return -1;
+ break;
+ }
+
+ if (visitor->visit_subtlv_end)
+ visitor->visit_subtlv_end(st->type, &ctx); /* TODO use return value? */
+ }
+
+ if (visitor->visit_tlv_end)
+ visitor->visit_tlv_end(&ctx); /* TODO use return value? */
+ }
+
+ return 0;
+}
+
+static void calc_elems(const struct f_tree UNUSED *t, void *ptr)
+{
+ uint *len = ptr;
+ (*len)++;
+}
+
+static void put_elems(const struct f_tree *t, void *ptr)
+{
+ struct te_tlv *tlv = ptr;
+ const struct te_subtlv *st = &t->from.val.st;
+ tlv->len--;
+ tlv->st[tlv->len] = *st;
+ if (st->type == TLV_ENCAPSULATION)
+ tlv->type = st->u.tunnel_encap.type;
+ DBG("put_elems: type %d\n", st->type);
+}
+
+const struct te_tlv *tlv_alloc(struct linpool *pool, const struct f_tree *set)
+{
+ uint len = 0;
+
+ tree_walk(set, calc_elems, &len);
+
+ if (!len)
+ return NULL;
+
+ struct te_tlv *res = lp_alloc(pool, sizeof(struct te_tlv) + len * sizeof(struct te_subtlv));
+
+ res->len = len;
+ tree_walk(set, put_elems, res);
+
+ res->len = len; /* Len was reset during tree_walk. */
+
+ for (uint i=0; i < len; i++) {
+ DBG("tlv_alloc: type[%d]: %d\n", i, res->st[i].type);
+ }
+ return res;
+}
+
+int tlv_set_format(const struct te_tlvlist *set, int UNUSED from, byte *buf, uint size)
+{
+ const struct te_visitor *visitor = te_get_format_visitor();
+ struct te_buffer buffer = {.pos=buf, .size=size, .tlv=0, .subtlv=0};
+
+ if (set)
+ walk_tlvlist(set, visitor, &buffer);
+
+ int l = bsnprintf(buffer.pos, buffer.size, "%s", buffer.tlv?"}":"<empty>");
+ if (l < 0)
+ return -1;
+ ADVANCE(buffer.pos, buffer.size, l);
+ return buffer.pos - buf;
+}
+
+int
+tlv_set_contains(const struct te_tlvlist *list, const struct te_tlv UNUSED *val)
+{
+ if (!list)
+ return 0;
+
+ /* FIXME */
+ return 0;
+}
+
+static void tlv_add_copy(struct linpool *pool, struct te_tlvlist *list, const struct te_tlv *t)
+{
+ uint size = sizeof(struct te_tlv) + t->len * sizeof(struct te_subtlv);
+ struct te_tlv *tlv = lp_alloc(pool, size);
+ memcpy(tlv, t, size);
+ memset(&tlv->n, 0, sizeof(tlv->n));
+ add_tail(&list->tlv, &tlv->n);
+ DBG("tlv_add_copy: src type %d, dst type %d\n", t->st[0].type, tlv->st[0].type);
+}
+
+const struct te_tlvlist *
+tlv_set_add(struct linpool *pool, const struct te_tlvlist *list, const struct te_tlv *val)
+{
+ if (tlv_set_contains(list, val))
+ return list;
+
+ struct te_tlvlist *res = lp_alloc(pool, sizeof(struct te_tlvlist));
+
+ init_list(&res->tlv);
+
+ DBG("tlv_set_add: first type %d, second type %d\n", list?((const struct te_tlv *)HEAD(list->tlv))->st[0].type:-1, val->st[0].type);
+
+ if (list)
+ {
+ struct te_tlv *n;
+ WALK_LIST(n, list->tlv)
+ {
+ tlv_add_copy(pool, res, n);
+ }
+ }
+
+ tlv_add_copy(pool, res, val);
+ return res;
+}
+
+const struct te_tlvlist *
+tlv_set_union(struct linpool UNUSED *pool, const struct te_tlvlist *l1, const struct te_tlvlist *l2)
+{
+ if (!l1)
+ return l2;
+ if (!l2)
+ return l1;
+
+ /* FIXME */
+ void *res = NULL;
+ return res;
+}
+
+static int
+subtlv_same(const struct te_subtlv *st1, const struct te_subtlv *st2)
+{
+ if (st1 == NULL && st2 == NULL)
+ return 1;
+
+ if (st1 == NULL || st2 == NULL)
+ return 0;
+
+ if (st1->type != st2->type)
+ return 0;
+
+ switch(st1->type) {
+ case TLV_ENCAPSULATION:
+ if (st1->u.tunnel_encap.type != st2->u.tunnel_encap.type ||
+ st1->u.tunnel_encap.length != st2->u.tunnel_encap.length ||
+ memcmp(st1->u.tunnel_encap.data, st2->u.tunnel_encap.data, st1->u.tunnel_encap.length))
+ return 0;
+ break;
+ case TLV_COLOR:
+ if (st1->u.color != st2->u.color)
+ return 0;
+ break;
+ case TLV_TUNNEL_ENDPOINT:
+ if (st1->u.tunnel_endpoint.reserved != st2->u.tunnel_endpoint.reserved ||
+ st1->u.tunnel_endpoint.af != st2->u.tunnel_endpoint.af ||
+ st1->u.tunnel_endpoint.af && ipa_compare(st1->u.tunnel_endpoint.ip, st2->u.tunnel_endpoint.ip))
+ return 0;
+ break;
+ case TLV_UDP_DEST_PORT:
+ if (st1->u.udp_dest_port != st2->u.udp_dest_port)
+ return 0;
+ break;
+ default:
+ if (st1->u.unknown.length != st2->u.unknown.length ||
+ memcmp(st1->u.unknown.data, st2->u.unknown.data, st1->u.unknown.length))
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
+static int
+tlv_same(const struct te_tlv *t1, const struct te_tlv *t2)
+{
+ if (t1 == NULL && t2 == NULL)
+ return 1;
+
+ if (t1 == NULL || t2 == NULL)
+ return 0;
+
+ if (t1->type != t2->type ||
+ t1->len != t2->len)
+ return 0;
+
+ for (uint i=0; i < t1->len; i++)
+ {
+ if (!subtlv_same(&t1->st[i], &t2->st[i]))
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+tlvlist_same(const struct te_tlvlist *tl1, const struct te_tlvlist *tl2)
+{
+ if (tl1 == NULL && tl2 == NULL)
+ return 1;
+
+ if (tl1 == NULL || tl2 == NULL)
+ return 0;
+
+ const struct te_tlv *t1 = HEAD(tl1->tlv);
+ const struct te_tlv *t2 = HEAD(tl2->tlv);
+
+ while (NODE_VALID(t1) || NODE_VALID(t2))
+ {
+ if (!NODE_VALID(t1) || !NODE_VALID(t2))
+ return 0;
+
+ /* Both are valid. */
+ if (!tlv_same(t1, t2))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+tlvlist_calc_tunnel_encap_new(const struct te_tlvlist *t, struct adata *ad)
+{
+ if (t == NULL)
+ return 0;
+
+ uint size = ad ? ad->length : 0;
+ byte *p = ad ? ad->data : NULL;
+
+ uint buf_size = te_init_encode_visitor(NULL, NULL, 0);
+ struct tlv_buffer *buf = alloca(buf_size);
+
+ if (te_init_encode_visitor(buf, p, size) < 0)
+ return -1;
+
+ if (walk_tlvlist(t, te_get_encode_visitor(), buf) < 0)
+ return -1;
+
+ return te_get_encode_length(buf);
+}
+
+struct adata *
+tlvlist_encode_tunnel_encap(struct linpool *pool, const struct te_tlvlist *tl)
+{
+ int len = tlvlist_calc_tunnel_encap_new(tl, NULL);
+
+ if (len < 0)
+ return NULL;
+
+ struct adata *ad = lp_alloc(pool, sizeof(struct adata) + len);
+ ad->length = len;
+
+ if (tlvlist_calc_tunnel_encap_new(tl, ad) < 0)
+ return NULL;
+
+ return ad;
+}
+
+struct decoder_buf
+{
+ struct linpool *pool;
+ struct te_tlvlist *tl;
+ struct te_tlv *cur_tlv;
+ struct te_subtlv *cur_subtlv;
+ int cur_subtlv_index;
+};
+
+static int visitor_decode_tlv(int type, struct te_context *ctx)
+{
+ struct decoder_buf *buf = ctx->opaque;
+
+ int count = te_count_subtlvs(ctx);
+
+ if (count < 0)
+ return -1;
+
+ buf->cur_tlv = lp_allocz(buf->pool, sizeof(struct te_tlv) + count * sizeof(struct te_subtlv));
+ buf->cur_tlv->type = type;
+ return 0;
+}
+
+static int visitor_decode_tlv_end(struct te_context *ctx)
+{
+ struct decoder_buf *buf = ctx->opaque;
+
+ add_tail(&buf->tl->tlv, &buf->cur_tlv->n);
+ buf->cur_tlv->len = buf->cur_subtlv_index;
+ buf->cur_tlv = NULL;
+ return 0;
+}
+
+static int
+visitor_decode_subtlv(int type, struct te_context *ctx){
+ struct decoder_buf *buf = ctx->opaque;
+
+ buf->cur_subtlv = &buf->cur_tlv->st[buf->cur_subtlv_index];
+ buf->cur_subtlv->type = type;
+ return 0;
+}
+
+static int
+visitor_decode_subtlv_end(int UNUSED type, struct te_context *ctx){
+ struct decoder_buf *buf = ctx->opaque;
+
+ buf->cur_subtlv_index++;
+ return 0;
+}
+
+static int
+visitor_decode_encap(const struct te_encap *encap, struct te_context *ctx)
+{
+ struct decoder_buf *buf = ctx->opaque;
+
+ buf->cur_subtlv->u.tunnel_encap = *encap;
+ return 0;
+}
+
+static int
+visitor_decode_ep(const struct te_endpoint *ep, struct te_context *ctx)
+{
+ struct decoder_buf *buf = ctx->opaque;
+
+ buf->cur_subtlv->u.tunnel_endpoint = *ep;
+ return 0;
+}
+
+static int
+visitor_decode_color(u32 color, struct te_context *ctx)
+{
+ struct decoder_buf *buf = ctx->opaque;
+
+ buf->cur_subtlv->u.color = color;
+ return 0;
+}
+
+static int
+visitor_decode_udp_dest_port(u16 port, struct te_context *ctx)
+{
+ struct decoder_buf *buf = ctx->opaque;
+
+ buf->cur_subtlv->u.udp_dest_port = port;
+ return 0;
+}
+
+static int
+visitor_decode_unknown(int UNUSED type, const struct te_unknown *unknown, struct te_context *ctx)
+{
+ struct decoder_buf *buf = ctx->opaque;
+
+ buf->cur_subtlv->u.unknown = *unknown;
+ return 0;
+}
+
+struct te_tlvlist *tlvlist_decode_tunnel_encap(struct linpool *pool,
+ const struct adata *ad)
+{
+ struct te_tlvlist *tl = lp_alloc(pool, sizeof(struct te_tlvlist));
+ struct decoder_buf buf = { .pool = pool, .tl = tl, };
+ struct te_visitor visitor =
+ {
+ .visit_tlv = visitor_decode_tlv,
+ .visit_tlv_end = visitor_decode_tlv_end,
+ .visit_subtlv = visitor_decode_subtlv,
+ .visit_subtlv_end = visitor_decode_subtlv_end,
+ .visit_encap = visitor_decode_encap,
+ .visit_ep = visitor_decode_ep,
+ .visit_color = visitor_decode_color,
+ .visit_udp_dest_port = visitor_decode_udp_dest_port,
+ .visit_unknown = visitor_decode_unknown,
+ };
+
+ init_list(&tl->tlv);
+
+ int res = walk_tunnel_encap(ad, &visitor, &buf);
+ if (res < 0)
+ return NULL;
+
+ return tl;
+}
diff --git a/nest/attrs.h b/nest/attrs.h
index 50da817b..f8b3a0c3 100644
--- a/nest/attrs.h
+++ b/nest/attrs.h
@@ -29,6 +29,12 @@
*/
struct f_tree;
+struct te_subtlv;
+struct te_tlv;
+struct te_tlvlist;
+struct te_encap;
+struct te_endpoint;
+struct te_visitor;
int as_path_valid(byte *data, uint len, int bs, int sets, int confed, char *err, uint elen);
int as_path_16to32(byte *dst, const byte *src, uint len);
@@ -221,4 +227,65 @@ struct adata *lc_set_sort(struct linpool *pool, const struct adata *src);
void ec_set_sort_x(struct adata *set); /* Sort in place */
+
+/* a-tlv.c */
+
+
+/* Tunnel Encapsulation TLV types */
+#define TLV_TUNNEL_TYPE 0x00 /* Reserved. Used internally only. */
+#define TLV_ENCAPSULATION 0x01
+#define TLV_COLOR 0x04
+#define TLV_TUNNEL_ENDPOINT 0x06
+#define TLV_UDP_DEST_PORT 0x08
+
+struct te_encap {
+ int type;
+ uint length;
+ const void *data;
+};
+
+struct te_unknown {
+ uint length;
+ const void *data;
+};
+
+struct te_endpoint {
+ u32 reserved;
+ u16 af;
+ ip_addr ip;
+};
+
+/* Tunnel Encapsulation TLV */
+struct te_subtlv {
+ int type;
+ union {
+ struct te_encap tunnel_encap;
+ struct te_endpoint tunnel_endpoint;
+ u32 color;
+ u16 udp_dest_port;
+ struct te_unknown unknown;
+ } u;
+};
+
+struct te_tlv {
+ node n;
+ uint len;
+ int type;
+ struct te_subtlv st[0];
+};
+
+struct te_tlvlist {
+ list tlv;
+};
+
+const struct te_tlv *tlv_alloc(struct linpool *pool, const struct f_tree *set);
+int tlv_set_format(const struct te_tlvlist *set, int from, byte *buf, uint size);
+int tlv_set_contains(const struct te_tlvlist *list, const struct te_tlv *val);
+const struct te_tlvlist *tlv_set_add(struct linpool *pool, const struct te_tlvlist *list, const struct te_tlv *val);
+const struct te_tlvlist *tlv_set_union(struct linpool *pool, const struct te_tlvlist *l1, const struct te_tlvlist *l2);
+int tlvlist_same(const struct te_tlvlist *tl1, const struct te_tlvlist *tl2);
+
+struct adata *tlvlist_encode_tunnel_encap(struct linpool *pool, const struct te_tlvlist *tl);
+struct te_tlvlist *tlvlist_decode_tunnel_encap(struct linpool *pool, const struct adata *ad);
+
#endif
diff --git a/nest/iface.h b/nest/iface.h
index 1189cdd4..9eda1c76 100644
--- a/nest/iface.h
+++ b/nest/iface.h
@@ -11,6 +11,7 @@
#include "lib/lists.h"
#include "lib/ip.h"
+#include "lib/net.h"
extern list iface_list;
diff --git a/nest/route.h b/nest/route.h
index f5fc9e31..3aad59b3 100644
--- a/nest/route.h
+++ b/nest/route.h
@@ -542,6 +542,7 @@ const char *ea_custom_name(uint ea);
#define EAF_TYPE_INT_SET 0x0a /* Set of u32's (e.g., a community list) */
#define EAF_TYPE_EC_SET 0x0e /* Set of pairs of u32's - ext. community list */
#define EAF_TYPE_LC_SET 0x12 /* Set of triplets of u32's - large community list */
+#define EAF_TYPE_TUNNEL_ENCAP 0x16 /* Tunnel Encapsulation (encoding per RFC 9012) */
#define EAF_TYPE_UNDEF 0x1f /* `force undefined' entry */
#define EAF_EMBEDDED 0x01 /* Data stored in eattr.u.data (part of type spec) */
#define EAF_VAR_LENGTH 0x02 /* Attribute length is variable (part of type spec) */
diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c
index 47802007..7816cb26 100644
--- a/sysdep/unix/main.c
+++ b/sysdep/unix/main.c
@@ -30,6 +30,7 @@
#include "lib/event.h"
#include "lib/timer.h"
#include "lib/string.h"
+#include "lib/tunnel_encaps.h"
#include "nest/route.h"
#include "nest/protocol.h"
#include "nest/iface.h"
@@ -880,6 +881,7 @@ main(int argc, char **argv)
if_init();
// roa_init();
config_init();
+ tunnel_encap_init();
uid_t use_uid = get_uid(use_user);
gid_t use_gid = get_gid(use_group);