diff options
Diffstat (limited to 'nest')
-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 |
5 files changed, 491 insertions, 1 deletions
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 085c45b9..7b277eed 100644 --- a/nest/route.h +++ b/nest/route.h @@ -552,6 +552,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) */ |