diff options
-rw-r--r-- | filter/config.Y | 149 | ||||
-rw-r--r-- | filter/data.c | 38 | ||||
-rw-r--r-- | filter/data.h | 11 | ||||
-rw-r--r-- | filter/decl.m4 | 3 | ||||
-rw-r--r-- | filter/f-inst.c | 78 | ||||
-rw-r--r-- | lib/Makefile | 2 | ||||
-rw-r--r-- | lib/tunnel_encaps.c | 876 | ||||
-rw-r--r-- | lib/tunnel_encaps.h | 63 | ||||
-rw-r--r-- | lib/unaligned.h | 3 | ||||
-rw-r--r-- | nest/Makefile | 2 | ||||
-rw-r--r-- | nest/a-tlv.c | 421 | ||||
-rw-r--r-- | nest/attrs.h | 67 | ||||
-rw-r--r-- | nest/iface.h | 1 | ||||
-rw-r--r-- | nest/route.h | 1 | ||||
-rw-r--r-- | proto/aggregator/aggregator.c | 2 | ||||
-rw-r--r-- | sysdep/unix/main.c | 2 |
16 files changed, 1712 insertions, 7 deletions
diff --git a/filter/config.Y b/filter/config.Y index 9345cc21..4f857350 100644 --- a/filter/config.Y +++ b/filter/config.Y @@ -197,6 +197,138 @@ 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, 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 = 0; + 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_encap(u32 st_type, u32 type, const struct adata *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 adata *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, 1), 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_tunnel_ep(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, 1), cfg_mem, &val); + + if (ret > F_RETURN) + cf_error("Runtime error while evaluating expression; see log for details"); + + switch(val.type) + { + 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; @@ -358,7 +490,7 @@ CF_KEYWORDS_EXCLUSIVE(IN) CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, ACCEPT, REJECT, ERROR, INT, BOOL, IP, PREFIX, RD, PAIR, QUAD, EC, LC, - SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST, + SET, STRING, BYTESTRING, BGPMASK, BGPPATH, CLIST, ECLIST, LCLIST, TLVLIST, IF, THEN, ELSE, CASE, FOR, DO, TRUE, FALSE, RT, RO, UNKNOWN, GENERIC, @@ -371,7 +503,7 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN, EMPTY, FILTER, WHERE, EVAL, ATTRIBUTE, FROM_HEX, - 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 @@ -388,7 +520,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 ec_items set_items switch_items switch_body +%type <e> pair_item ec_item lc_item set_item switch_item ec_items set_items switch_items switch_body sub_tlv_item %type <trie> fprefix_set %type <v> set_atom switch_atom fipa %type <px> fprefix @@ -468,6 +600,8 @@ type: | ECLIST { $$ = T_ECLIST; } | LCLIST { $$ = T_LCLIST; } | ROUTE { $$ = T_ROUTE; } + | SUBTLV { $$ = T_SUBTLV; } + | TLVLIST { $$ = T_TLVLIST; } | type SET { switch ($1) { case T_INT: @@ -477,6 +611,7 @@ type: case T_LC: case T_RD: case T_IP: + case T_SUBTLV: $$ = T_SET; break; @@ -701,10 +836,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); } ; @@ -713,6 +854,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); } ; @@ -938,6 +1080,7 @@ term: | '-' EMPTY '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_CLIST)); } | '-' '-' EMPTY '-' '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_ECLIST)); } | '-' '-' '-' EMPTY '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_LCLIST)); } + | '-' '-' '-' '-' EMPTY '-' '-' '-' '-' { $$ = f_new_inst(FI_CONSTANT, val_empty(T_TLVLIST)); } | PREPEND '(' term ',' term ')' { $$ = f_dispatch_method_x("prepend", $3->type, $3, $5); } | ADD '(' term ',' term ')' { $$ = f_dispatch_method_x("add", $3->type, $3, $5); } diff --git a/filter/data.c b/filter/data.c index e268a8ec..d2347684 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" @@ -55,6 +57,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", [T_ROUTE] = "route", @@ -89,7 +93,7 @@ f_type_element_type(enum f_type t) const struct f_trie f_const_empty_trie = { .ipv4 = -1, }; const struct f_val f_const_empty_prefix_set = { .type = T_PREFIX_SET, - .val.ti = &f_const_empty_trie, + .val.ti = &f_const_empty_trie }; static struct adata * @@ -309,6 +313,8 @@ val_same(const struct f_val *v1, const struct f_val *v2) return v1->val.rte == v2->val.rte; case T_ROUTES_BLOCK: return v1->val.ad == v2->val.ad; + case T_TLVLIST: + return tlvlist_same(v1->val.tl, v2->val.tl); default: bug("Invalid type in val_same(): %x", v1->type); } @@ -415,6 +421,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) { @@ -516,6 +534,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 @@ -576,6 +608,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); @@ -640,6 +675,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; case T_ROUTE: rte_format(v->val.rte, buf); return; case T_ROUTES_BLOCK: rte_block_format(v->val.rte, buf); return; diff --git a/filter/data.h b/filter/data.h index 5dff7e85..a6923eb6 100644 --- a/filter/data.h +++ b/filter/data.h @@ -64,6 +64,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_ROUTE = 0x78, T_ROUTES_BLOCK = 0x79, @@ -96,6 +98,9 @@ struct f_val { const struct f_path_mask *path_mask; struct f_path_mask_item pmi; struct rte *rte; + struct te_subtlv st; + const struct te_tlv *tlv; + const struct te_tlvlist *tl; } val; }; @@ -315,6 +320,8 @@ static inline int lclist_set_type(const struct f_tree *set) { return !set || set->from.type == T_LC; } static inline int path_set_type(const struct f_tree *set) { return !set || set->from.type == T_INT; } +static inline int subtlv_set_type(const struct f_tree *set) +{ return !set || set->from.type == T_SUBTLV; } int clist_match_set(const struct adata *clist, const struct f_tree *set); int eclist_match_set(const struct adata *list, const struct f_tree *set); @@ -323,6 +330,7 @@ int lclist_match_set(const struct adata *list, const struct f_tree *set); 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 */ @@ -347,6 +355,9 @@ val_empty(enum f_type t) case T_EMPTY_LIST: return (struct f_val) { .type = t, .val.ad = &null_adata }; + case T_TLVLIST: + return (struct f_val) { .type = T_TLVLIST, .val.tl = NULL }; + default: return (struct f_val) { }; } diff --git a/filter/decl.m4 b/filter/decl.m4 index a378eadf..397a6183 100644 --- a/filter/decl.m4 +++ b/filter/decl.m4 @@ -603,6 +603,9 @@ f_const_promotion_(struct f_inst *arg, enum f_type want, int update) } else if ((c->type == T_EMPTY_LIST) && (want == T_LCLIST)) { *c = val_empty(T_LCLIST); return 1; + } else if ((c->type == T_EMPTY_LIST) && (want == T_TLVLIST)) { + *c = val_empty(T_TLVLIST); + return 1; } return 0; diff --git a/filter/f-inst.c b/filter/f-inst.c index 9cc46aa0..b26facb0 100644 --- a/filter/f-inst.c +++ b/filter/f-inst.c @@ -861,6 +861,14 @@ case EAF_TYPE_LC_SET: RESULT_(T_LCLIST, ad, e->u.ptr); 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"); } @@ -927,6 +935,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"); } @@ -1309,6 +1327,24 @@ RESULT(T_LCLIST, ad, [[ lc_set_union(fpool, v1.val.ad, v2.val.ad) ]]); } + INST(FI_TLVLIST_ADD_SET, 2, 1) { + ARG(1, T_TLVLIST); + ARG(2, T_SET); + METHOD_CONSTRUCTOR("add"); + + if (!subtlv_set_type(v2.val.t)) + runtime("Mismatched set type"); + + RESULT(T_TLVLIST, tl, [[ tlv_set_add(fpool, v1.val.tl, tlv_alloc(fpool, v2.val.t)) ]]); + } + + INST(FI_TLVLIST_ADD_TLVLIST, 2, 1) { + ARG(1, T_TLVLIST); + ARG(2, T_TLVLIST); + METHOD_CONSTRUCTOR("add"); + RESULT(T_TLVLIST, tl, [[ tlv_set_union(fpool, v1.val.tl, v2.val.tl) ]]); + } + INST(FI_PATH_DELETE_INT, 2, 1) { ARG(1, T_PATH); ARG(2, T_INT); @@ -1424,6 +1460,21 @@ RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 0) ]]); } + INST(FI_TLVLIST_DELETE_SET, 2, 1) { + ARG(1, T_TLVLIST); + ARG(2, T_SET); + METHOD_CONSTRUCTOR("delete"); + + if (!subtlv_set_type(v2.val.t)) + runtime("Mismatched set type"); + +#if 0 + RESULT(T_TLVLIST, tl, [[ tlvlist_filter(fpool, v1.val.tl, &v2, 0) ]]); +#else + runtime("FI_TLVLIST_DELETE_SET disabled"); +#endif + } + INST(FI_PATH_FILTER_SET, 2, 1) { ARG(1, T_PATH); ARG(2, T_SET); @@ -1489,6 +1540,33 @@ RESULT(T_LCLIST, ad, [[ lclist_filter(fpool, v1.val.ad, &v2, 1) ]]); } + INST(FI_TLVLIST_FILTER_SET, 2, 1) { + ARG(1, T_TLVLIST); + ARG(2, T_SET); + METHOD_CONSTRUCTOR("filter"); + + if (!subtlv_set_type(v2.val.t)) + runtime("Mismatched set type"); + +#if 0 + RESULT(T_TLVLIST, tl, [[ tlvlist_filter(fpool, v1.val.tl, &v2, 1) ]]); +#else + runtime("FI_TLVLIST_FILTER_SET disabled"); +#endif + } + + INST(FI_TLVLIST_FILTER_TLVLIST, 2, 1) { + ARG(1, T_TLVLIST); + ARG(2, T_TLVLIST); + METHOD_CONSTRUCTOR("filter"); + +#if 0 + RESULT(T_TLVLIST, tl, [[ tlvlist_filter(fpool, v1.val.tl, &v2, 1) ]]); +#else + runtime("FI_TLVLIST_FILTER_TLVLIST disabled"); +#endif + } + INST(FI_ROA_CHECK_IMPLICIT, 0, 1) { /* ROA Check */ NEVER_CONSTANT; RTC(1); diff --git a/lib/Makefile b/lib/Makefile index 0310eca2..1534b794 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..d549694a --- /dev/null +++ b/lib/tunnel_encaps.c @@ -0,0 +1,876 @@ +#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"}, + {22, "Dynamic Path Selection (DPS) Tunnel Encapsulation"}, + {23, "Originating PE (OPE)"}, + {24, "Dynamic Path Selection (DPS) Policy"}, +}; + +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 ef0fdf67..aa75fed7 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 mpls.c neighbor.c password.c proto.c proto-build.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 mpls.c neighbor.c password.c proto.c proto-build.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..13e05f43 --- /dev/null +++ b/nest/a-tlv.c @@ -0,0 +1,421 @@ +#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 aee3468b..99174b2b 100644 --- a/nest/attrs.h +++ b/nest/attrs.h @@ -30,6 +30,12 @@ struct f_val; 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); @@ -243,4 +249,65 @@ int rte_set_walk(const struct adata *list, u32 *pos, struct rte **val); 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 f8e92850..daf84a7c 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 d26a4b8c..e797fa8d 100644 --- a/nest/route.h +++ b/nest/route.h @@ -555,6 +555,7 @@ const char *ea_custom_name(uint ea); #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_IFACE 0x16 /* Interface pointer stored in adata */ +#define EAF_TYPE_TUNNEL_ENCAP 0x1a /* Tunnel Encapsulation (encoding per RFC 9012) */ #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/proto/aggregator/aggregator.c b/proto/aggregator/aggregator.c index e5c2a176..950da103 100644 --- a/proto/aggregator/aggregator.c +++ b/proto/aggregator/aggregator.c @@ -560,6 +560,8 @@ aggregator_rt_notify(struct proto *P, struct channel *src_ch, net *net, rte *new case T_PATH_MASK_ITEM: case T_ROUTE: case T_ROUTES_BLOCK: + case T_SUBTLV: + case T_TLVLIST: bug("Invalid type %s in hashing", f_type_name(tmp_bucket->aggr_data[i].type)); case T_SET: MX(t); diff --git a/sysdep/unix/main.c b/sysdep/unix/main.c index 0d7788bb..e4d7f925 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" @@ -899,6 +900,7 @@ main(int argc, char **argv) mpls_init(); // roa_init(); config_init(); + tunnel_encap_init(); uid_t use_uid = get_uid(use_user); gid_t use_gid = get_gid(use_group); |