diff options
Diffstat (limited to 'src/netlink.c')
-rw-r--r-- | src/netlink.c | 129 |
1 files changed, 116 insertions, 13 deletions
diff --git a/src/netlink.c b/src/netlink.c index 46897e43..a9ccebaf 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -27,11 +27,13 @@ #include <libnftnl/udata.h> #include <libnftnl/ruleset.h> #include <libnftnl/common.h> +#include <libnftnl/udata.h> #include <linux/netfilter/nfnetlink.h> #include <linux/netfilter/nf_tables.h> #include <linux/netfilter.h> #include <nftables.h> +#include <parser.h> #include <netlink.h> #include <mnl.h> #include <expression.h> @@ -570,6 +572,31 @@ static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data) if (len != sizeof(uint32_t)) return -1; break; + case NFTNL_UDATA_SET_KEY_TYPEOF: + case NFTNL_UDATA_SET_DATA_TYPEOF: + if (len < 3) + return -1; + break; + default: + return 0; + } + tb[type] = attr; + return 0; +} + +static int set_key_parse_udata(const struct nftnl_udata *attr, void *data) +{ + const struct nftnl_udata **tb = data; + uint8_t type = nftnl_udata_type(attr); + uint8_t len = nftnl_udata_len(attr); + + switch (type) { + case NFTNL_UDATA_SET_TYPEOF_EXPR: + if (len != sizeof(uint32_t)) + return -1; + break; + case NFTNL_UDATA_SET_TYPEOF_DATA: + break; default: return 0; } @@ -577,6 +604,44 @@ static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data) return 0; } +static struct expr *set_make_key(const struct nftnl_udata *attr) +{ + const struct nftnl_udata *ud[NFTNL_UDATA_SET_TYPEOF_MAX + 1] = {}; + const struct expr_ops *ops; + enum expr_types etype; + struct expr *expr; + int err; + + if (!attr) + return NULL; + + err = nftnl_udata_parse(nftnl_udata_get(attr), nftnl_udata_len(attr), + set_key_parse_udata, ud); + if (err < 0) + return NULL; + + if (!ud[NFTNL_UDATA_SET_TYPEOF_EXPR] || + !ud[NFTNL_UDATA_SET_TYPEOF_DATA]) + return NULL; + + etype = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_TYPEOF_EXPR]); + ops = expr_ops_by_type(etype); + + expr = ops->parse_udata(ud[NFTNL_UDATA_SET_TYPEOF_DATA]); + if (!expr) + return NULL; + + return expr; +} + +static bool set_udata_key_valid(const struct expr *e, const struct datatype *d, uint32_t len) +{ + if (!e) + return false; + + return div_round_up(e->len, BITS_PER_BYTE) == len / BITS_PER_BYTE; +} + struct set *netlink_delinearize_set(struct netlink_ctx *ctx, const struct nftnl_set *nls) { @@ -584,11 +649,17 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx, enum byteorder keybyteorder = BYTEORDER_INVALID; enum byteorder databyteorder = BYTEORDER_INVALID; const struct datatype *keytype, *datatype = NULL; + struct expr *typeof_expr_key, *typeof_expr_data; uint32_t flags, key, objtype = 0; + const struct datatype *dtype; bool automerge = false; const char *udata; struct set *set; uint32_t ulen; + uint32_t klen; + + typeof_expr_key = NULL; + typeof_expr_data = NULL; if (nftnl_set_is_set(nls, NFTNL_SET_USERDATA)) { udata = nftnl_set_get_data(nls, NFTNL_SET_USERDATA, &ulen); @@ -606,6 +677,9 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx, GET_U32_UDATA(automerge, NFTNL_UDATA_SET_MERGE_ELEMENTS); #undef GET_U32_UDATA + typeof_expr_key = set_make_key(ud[NFTNL_UDATA_SET_KEY_TYPEOF]); + if (ud[NFTNL_UDATA_SET_DATA_TYPEOF]) + typeof_expr_data = set_make_key(ud[NFTNL_UDATA_SET_DATA_TYPEOF]); } key = nftnl_set_get_u32(nls, NFTNL_SET_KEY_TYPE); @@ -626,12 +700,14 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx, netlink_io_error(ctx, NULL, "Unknown data type in set key %u", data); + datatype_free(keytype); return NULL; } } if (set_is_objmap(flags)) { objtype = nftnl_set_get_u32(nls, NFTNL_SET_OBJ_TYPE); + assert(!datatype); datatype = &string_type; } @@ -641,24 +717,51 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx, set->handle.set.name = xstrdup(nftnl_set_get_str(nls, NFTNL_SET_NAME)); set->automerge = automerge; - set->key = constant_expr_alloc(&netlink_location, - set_datatype_alloc(keytype, keybyteorder), - keybyteorder, - nftnl_set_get_u32(nls, NFTNL_SET_KEY_LEN) * BITS_PER_BYTE, - NULL); + if (datatype) { + dtype = set_datatype_alloc(datatype, databyteorder); + klen = nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE; + + if (set_udata_key_valid(typeof_expr_data, dtype, klen)) { + datatype_free(datatype_get(dtype)); + set->data = typeof_expr_data; + } else { + expr_free(typeof_expr_data); + set->data = constant_expr_alloc(&netlink_location, + dtype, + databyteorder, klen, + NULL); + + /* Can't use 'typeof' keyword, so discard key too */ + expr_free(typeof_expr_key); + typeof_expr_key = NULL; + } + + if (dtype != datatype) + datatype_free(datatype); + } + + dtype = set_datatype_alloc(keytype, keybyteorder); + klen = nftnl_set_get_u32(nls, NFTNL_SET_KEY_LEN) * BITS_PER_BYTE; + + if (set_udata_key_valid(typeof_expr_key, dtype, klen)) { + datatype_free(datatype_get(dtype)); + set->key = typeof_expr_key; + set->key_typeof_valid = true; + } else { + expr_free(typeof_expr_key); + set->key = constant_expr_alloc(&netlink_location, dtype, + keybyteorder, klen, + NULL); + } + + if (dtype != keytype) + datatype_free(keytype); + set->flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS); set->handle.handle.id = nftnl_set_get_u64(nls, NFTNL_SET_HANDLE); set->objtype = objtype; - set->data = NULL; - if (datatype) - set->data = constant_expr_alloc(&netlink_location, - set_datatype_alloc(datatype, databyteorder), - databyteorder, - nftnl_set_get_u32(nls, NFTNL_SET_DATA_LEN) * BITS_PER_BYTE, - NULL); - if (nftnl_set_is_set(nls, NFTNL_SET_TIMEOUT)) set->timeout = nftnl_set_get_u64(nls, NFTNL_SET_TIMEOUT); if (nftnl_set_is_set(nls, NFTNL_SET_GC_INTERVAL)) |