diff options
Diffstat (limited to 'src/netlink.c')
-rw-r--r-- | src/netlink.c | 630 |
1 files changed, 464 insertions, 166 deletions
diff --git a/src/netlink.c b/src/netlink.c index 8cdd6d81..0088b742 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -9,12 +9,12 @@ * Development of this code funded by Astaro AG (http://www.astaro.com/) */ -#include <string.h> +#include <nft.h> + #include <errno.h> #include <libmnl/libmnl.h> #include <netinet/in.h> #include <arpa/inet.h> -#include <stdlib.h> #include <inttypes.h> #include <libnftnl/table.h> @@ -59,7 +59,7 @@ void __noreturn __netlink_abi_error(const char *file, int line, { fprintf(stderr, "E: Contact urgently your Linux kernel vendor. " "Netlink ABI is broken: %s:%d %s\n", file, line, reason); - exit(NFT_EXIT_FAILURE); + abort(); } int netlink_io_error(struct netlink_ctx *ctx, const struct location *loc, @@ -96,9 +96,13 @@ struct nftnl_expr *alloc_nft_expr(const char *name) return nle; } +static void netlink_gen_key(const struct expr *expr, + struct nft_data_linearize *data); +static void __netlink_gen_data(const struct expr *expr, + struct nft_data_linearize *data, bool expand); -static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set, - const struct expr *expr) +struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set, + const struct expr *expr) { const struct expr *elem, *data; struct nftnl_set_elem *nlse; @@ -130,15 +134,23 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set, case EXPR_SET_ELEM_CATCHALL: break; default: - netlink_gen_data(key, &nld); - nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len); - if (set->set_flags & NFT_SET_INTERVAL && key->field_count > 1) { + if (set->set_flags & NFT_SET_INTERVAL && + key->etype == EXPR_CONCAT && key->field_count > 1) { + key->flags |= EXPR_F_INTERVAL; + netlink_gen_key(key, &nld); + key->flags &= ~EXPR_F_INTERVAL; + + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len); + key->flags |= EXPR_F_INTERVAL_END; - netlink_gen_data(key, &nld); + netlink_gen_key(key, &nld); key->flags &= ~EXPR_F_INTERVAL_END; nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY_END, &nld.value, nld.len); + } else { + netlink_gen_key(key, &nld); + nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_KEY, &nld.value, nld.len); } break; } @@ -185,7 +197,7 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set, nftnl_udata_buf_free(udbuf); } if (set_is_datamap(set->set_flags) && data != NULL) { - netlink_gen_data(data, &nld); + __netlink_gen_data(data, &nld, !(data->flags & EXPR_F_SINGLETON)); switch (data->etype) { case EXPR_VERDICT: nftnl_set_elem_set_u32(nlse, NFTNL_SET_ELEM_VERDICT, @@ -242,8 +254,91 @@ static int netlink_export_pad(unsigned char *data, const mpz_t v, return netlink_padded_len(i->len) / BITS_PER_BYTE; } -static int netlink_gen_concat_data_expr(int end, const struct expr *i, - unsigned char *data) +static void byteorder_switch_expr_value(mpz_t v, const struct expr *e) +{ + mpz_switch_byteorder(v, div_round_up(e->len, BITS_PER_BYTE)); +} + +static int __netlink_gen_concat_key(uint32_t flags, const struct expr *i, + unsigned char *data) +{ + struct expr *expr; + + switch (i->etype) { + case EXPR_RANGE: + if (flags & EXPR_F_INTERVAL_END) + expr = i->right; + else + expr = i->left; + + if (expr_basetype(expr)->type == TYPE_INTEGER && + expr->byteorder == BYTEORDER_HOST_ENDIAN) + byteorder_switch_expr_value(expr->value, expr); + + i = expr; + break; + case EXPR_PREFIX: + if (flags & EXPR_F_INTERVAL_END) { + int count; + mpz_t v; + + mpz_init_bitmask(v, i->len - i->prefix_len); + + if (i->byteorder == BYTEORDER_HOST_ENDIAN) + byteorder_switch_expr_value(v, i); + + mpz_add(v, i->prefix->value, v); + count = netlink_export_pad(data, v, i); + mpz_clear(v); + return count; + } + return netlink_export_pad(data, i->prefix->value, i); + case EXPR_VALUE: + /* Switch byteorder only once for singleton values when the set + * contains concatenation of intervals. + */ + if (!(flags & EXPR_F_INTERVAL)) + break; + + expr = (struct expr *)i; + if (expr_basetype(expr)->type == TYPE_INTEGER && + expr->byteorder == BYTEORDER_HOST_ENDIAN) + byteorder_switch_expr_value(expr->value, expr); + break; + default: + BUG("invalid expression type '%s' in set", expr_ops(i)->name); + } + + return netlink_export_pad(data, i->value, i); +} + +static void nft_data_memcpy(struct nft_data_linearize *nld, + const void *src, unsigned int len) +{ + if (len > sizeof(nld->value)) + BUG("nld buffer overflow: want to copy %u, max %u\n", len, (unsigned int)sizeof(nld->value)); + + memcpy(nld->value, src, len); + nld->len = len; +} + +static void netlink_gen_concat_key(const struct expr *expr, + struct nft_data_linearize *nld) +{ + unsigned int len = expr->len / BITS_PER_BYTE, offset = 0; + unsigned char data[len]; + const struct expr *i; + + memset(data, 0, len); + + list_for_each_entry(i, &expr->expressions, list) + offset += __netlink_gen_concat_key(expr->flags, i, data + offset); + + nft_data_memcpy(nld, data, len); +} + +static int __netlink_gen_concat_data(int end, const struct expr *i, + unsigned char *data) { switch (i->etype) { case EXPR_RANGE: @@ -270,21 +365,46 @@ static int netlink_gen_concat_data_expr(int end, const struct expr *i, return netlink_export_pad(data, i->value, i); } -static void netlink_gen_concat_data(const struct expr *expr, - struct nft_data_linearize *nld) +static void __netlink_gen_concat_expand(const struct expr *expr, + struct nft_data_linearize *nld) +{ + unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2, offset = 0; + unsigned char data[len]; + const struct expr *i; + + memset(data, 0, len); + + list_for_each_entry(i, &expr->expressions, list) + offset += __netlink_gen_concat_data(false, i, data + offset); + + list_for_each_entry(i, &expr->expressions, list) + offset += __netlink_gen_concat_data(true, i, data + offset); + + nft_data_memcpy(nld, data, len); +} + +static void __netlink_gen_concat(const struct expr *expr, + struct nft_data_linearize *nld) { unsigned int len = expr->len / BITS_PER_BYTE, offset = 0; - int end = expr->flags & EXPR_F_INTERVAL_END; unsigned char data[len]; const struct expr *i; memset(data, 0, len); list_for_each_entry(i, &expr->expressions, list) - offset += netlink_gen_concat_data_expr(end, i, data + offset); + offset += __netlink_gen_concat_data(expr->flags, i, data + offset); - memcpy(nld->value, data, len); - nld->len = len; + nft_data_memcpy(nld, data, len); +} + +static void netlink_gen_concat_data(const struct expr *expr, + struct nft_data_linearize *nld, bool expand) +{ + if (expand) + __netlink_gen_concat_expand(expr, nld); + else + __netlink_gen_concat(expr, nld); } static void netlink_gen_constant_data(const struct expr *expr, @@ -344,35 +464,54 @@ static void netlink_gen_range(const struct expr *expr, memset(data, 0, len); offset = netlink_export_pad(data, expr->left->value, expr->left); netlink_export_pad(data + offset, expr->right->value, expr->right); - memcpy(nld->value, data, len); - nld->len = len; + nft_data_memcpy(nld, data, len); } static void netlink_gen_prefix(const struct expr *expr, struct nft_data_linearize *nld) { - unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2; - unsigned char data[len]; + unsigned int len = (netlink_padded_len(expr->len) / BITS_PER_BYTE) * 2; + unsigned char data[NFT_MAX_EXPR_LEN_BYTES]; int offset; mpz_t v; + if (len > sizeof(data)) + BUG("Value export of %u bytes would overflow", len); + offset = netlink_export_pad(data, expr->prefix->value, expr); mpz_init_bitmask(v, expr->len - expr->prefix_len); mpz_add(v, expr->prefix->value, v); netlink_export_pad(data + offset, v, expr->prefix); mpz_clear(v); - memcpy(nld->value, data, len); - nld->len = len; + nft_data_memcpy(nld, data, len); } -void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data) +static void netlink_gen_key(const struct expr *expr, + struct nft_data_linearize *data) { switch (expr->etype) { case EXPR_VALUE: return netlink_gen_constant_data(expr, data); case EXPR_CONCAT: - return netlink_gen_concat_data(expr, data); + return netlink_gen_concat_key(expr, data); + case EXPR_RANGE: + return netlink_gen_range(expr, data); + case EXPR_PREFIX: + return netlink_gen_prefix(expr, data); + default: + BUG("invalid data expression type %s\n", expr_name(expr)); + } +} + +static void __netlink_gen_data(const struct expr *expr, + struct nft_data_linearize *data, bool expand) +{ + switch (expr->etype) { + case EXPR_VALUE: + return netlink_gen_constant_data(expr, data); + case EXPR_CONCAT: + return netlink_gen_concat_data(expr, data, expand); case EXPR_VERDICT: return netlink_gen_verdict(expr, data); case EXPR_RANGE: @@ -384,6 +523,11 @@ void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data) } } +void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data) +{ + __netlink_gen_data(expr, data, false); +} + struct expr *netlink_alloc_value(const struct location *loc, const struct nft_data_delinearize *nld) { @@ -445,48 +589,6 @@ void netlink_dump_expr(const struct nftnl_expr *nle, fprintf(fp, "\n"); } -static int list_rule_cb(struct nftnl_rule *nlr, void *arg) -{ - struct netlink_ctx *ctx = arg; - const struct handle *h = ctx->data; - struct rule *rule; - const char *table, *chain; - uint32_t family; - - family = nftnl_rule_get_u32(nlr, NFTNL_RULE_FAMILY); - table = nftnl_rule_get_str(nlr, NFTNL_RULE_TABLE); - chain = nftnl_rule_get_str(nlr, NFTNL_RULE_CHAIN); - - if (h->family != family || - strcmp(table, h->table.name) != 0 || - (h->chain.name && strcmp(chain, h->chain.name) != 0)) - return 0; - - netlink_dump_rule(nlr, ctx); - rule = netlink_delinearize_rule(ctx, nlr); - list_add_tail(&rule->list, &ctx->list); - - return 0; -} - -int netlink_list_rules(struct netlink_ctx *ctx, const struct handle *h) -{ - struct nftnl_rule_list *rule_cache; - - rule_cache = mnl_nft_rule_dump(ctx, h->family); - if (rule_cache == NULL) { - if (errno == EINTR) - return -1; - - return 0; - } - - ctx->data = h; - nftnl_rule_list_foreach(rule_cache, list_rule_cb, ctx); - nftnl_rule_list_free(rule_cache); - return 0; -} - void netlink_dump_chain(const struct nftnl_chain *nlc, struct netlink_ctx *ctx) { FILE *fp = ctx->nft->output.output_fp; @@ -517,21 +619,31 @@ static int chain_parse_udata_cb(const struct nftnl_udata *attr, void *data) return 0; } +static int qsort_device_cmp(const void *a, const void *b) +{ + const char **x = (const char **)a; + const char **y = (const char **)b; + + return strcmp(*x, *y); +} + struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx, const struct nftnl_chain *nlc) { - const struct nftnl_udata *ud[NFTNL_UDATA_OBJ_MAX + 1] = {}; + const struct nftnl_udata *ud[NFTNL_UDATA_CHAIN_MAX + 1] = {}; int priority, policy, len = 0, i; const char * const *dev_array; struct chain *chain; const char *udata; uint32_t ulen; - chain = chain_alloc(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME)); + chain = chain_alloc(); chain->handle.family = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_FAMILY); chain->handle.table.name = xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TABLE)); + chain->handle.chain.name = + xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_NAME)); chain->handle.handle.id = nftnl_chain_get_u64(nlc, NFTNL_CHAIN_HANDLE); if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_FLAGS)) @@ -552,7 +664,7 @@ struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx, BYTEORDER_HOST_ENDIAN, sizeof(int) * BITS_PER_BYTE, &priority); - chain->type = + chain->type.str = xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TYPE)); policy = nftnl_chain_get_u32(nlc, NFTNL_CHAIN_POLICY); chain->policy = constant_expr_alloc(&netlink_location, @@ -580,12 +692,18 @@ struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx, chain->dev_array_len = len; } chain->flags |= CHAIN_F_BASECHAIN; + + if (chain->dev_array_len) { + qsort(chain->dev_array, chain->dev_array_len, + sizeof(char *), qsort_device_cmp); + } } if (nftnl_chain_is_set(nlc, NFTNL_CHAIN_USERDATA)) { udata = nftnl_chain_get_data(nlc, NFTNL_CHAIN_USERDATA, &ulen); if (nftnl_udata_parse(udata, ulen, chain_parse_udata_cb, ud) < 0) { netlink_io_error(ctx, NULL, "Cannot parse userdata"); + chain_free(chain); return NULL; } if (ud[NFTNL_UDATA_CHAIN_COMMENT]) @@ -633,6 +751,7 @@ struct table *netlink_delinearize_table(struct netlink_ctx *ctx, udata = nftnl_table_get_data(nlt, NFTNL_TABLE_USERDATA, &ulen); if (nftnl_udata_parse(udata, ulen, table_parse_udata_cb, ud) < 0) { netlink_io_error(ctx, NULL, "Cannot parse userdata"); + table_free(table); return NULL; } if (ud[NFTNL_UDATA_TABLE_COMMENT]) @@ -653,11 +772,19 @@ static int list_table_cb(struct nftnl_table *nlt, void *arg) return 0; } -int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h) +int netlink_list_tables(struct netlink_ctx *ctx, const struct handle *h, + const struct nft_cache_filter *filter) { struct nftnl_table_list *table_cache; + uint32_t family = h->family; + const char *table = NULL; + + if (filter) { + family = filter->list.family; + table = filter->list.table; + } - table_cache = mnl_nft_table_dump(ctx, h->family); + table_cache = mnl_nft_table_dump(ctx, family, table); if (table_cache == NULL) { if (errno == EINTR) return -1; @@ -683,13 +810,17 @@ enum nft_data_types dtype_map_to_kernel(const struct datatype *dtype) static const struct datatype *dtype_map_from_kernel(enum nft_data_types type) { + /* The function always returns ownership of a reference. But for + * &verdict_Type and datatype_lookup(), those are static instances, + * we can omit the datatype_get() call. + */ switch (type) { case NFT_DATA_VERDICT: return &verdict_type; default: if (type & ~TYPE_MASK) return concat_type_alloc(type); - return datatype_lookup(type); + return datatype_lookup((enum datatypes) type); } } @@ -759,8 +890,8 @@ 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; + uint32_t etype; int err; if (!attr) @@ -776,7 +907,9 @@ static struct expr *set_make_key(const struct nftnl_udata *attr) return NULL; etype = nftnl_udata_get_u32(ud[NFTNL_UDATA_SET_TYPEOF_EXPR]); - ops = expr_ops_by_type(etype); + ops = expr_ops_by_type_u32(etype); + if (!ops) + return NULL; expr = ops->parse_udata(ud[NFTNL_UDATA_SET_TYPEOF_DATA]); if (!expr) @@ -785,7 +918,7 @@ static struct expr *set_make_key(const struct nftnl_udata *attr) return expr; } -static bool set_udata_key_valid(const struct expr *e, const struct datatype *d, uint32_t len) +static bool set_udata_key_valid(const struct expr *e, uint32_t len) { if (!e) return false; @@ -818,21 +951,21 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx, const struct nftnl_udata *ud[NFTNL_UDATA_SET_MAX + 1] = {}; enum byteorder keybyteorder = BYTEORDER_INVALID; enum byteorder databyteorder = BYTEORDER_INVALID; - const struct datatype *keytype, *datatype = NULL; - struct expr *typeof_expr_key, *typeof_expr_data; struct setelem_parse_ctx set_parse_ctx; + const struct datatype *datatype = NULL; + const struct datatype *keytype = NULL; + const struct datatype *dtype2 = NULL; + const struct datatype *dtype = NULL; + struct expr *typeof_expr_data = NULL; + struct expr *typeof_expr_key = NULL; const char *udata, *comment = NULL; uint32_t flags, key, objtype = 0; - const struct datatype *dtype; uint32_t data_interval = 0; bool automerge = false; 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); if (nftnl_udata_parse(udata, ulen, set_parse_udata_cb, ud) < 0) { @@ -854,7 +987,7 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx, if (ud[NFTNL_UDATA_SET_DATA_TYPEOF]) typeof_expr_data = set_make_key(ud[NFTNL_UDATA_SET_DATA_TYPEOF]); if (ud[NFTNL_UDATA_SET_COMMENT]) - comment = xstrdup(nftnl_udata_get(ud[NFTNL_UDATA_SET_COMMENT])); + comment = nftnl_udata_get(ud[NFTNL_UDATA_SET_COMMENT]); } key = nftnl_set_get_u32(nls, NFTNL_SET_KEY_TYPE); @@ -875,8 +1008,8 @@ 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; + set = NULL; + goto out; } } @@ -892,7 +1025,7 @@ 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; if (comment) - set->comment = comment; + set->comment = xstrdup(comment); init_list_head(&set_parse_ctx.stmt_list); @@ -911,17 +1044,28 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx, } list_splice_tail(&set_parse_ctx.stmt_list, &set->stmt_list); + set->flags = nftnl_set_get_u32(nls, NFTNL_SET_FLAGS); + if (datatype) { - dtype = set_datatype_alloc(datatype, databyteorder); + uint32_t dlen; + + dtype2 = 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)); + dlen = data_interval ? klen / 2 : klen; + + if (set_udata_key_valid(typeof_expr_data, dlen)) { + typeof_expr_data->len = klen; set->data = typeof_expr_data; + typeof_expr_data = NULL; + } else if (set->flags & NFT_SET_OBJECT) { + set->data = constant_expr_alloc(&netlink_location, + dtype2, + databyteorder, klen, + NULL); } else { - expr_free(typeof_expr_data); set->data = constant_expr_alloc(&netlink_location, - dtype, + dtype2, databyteorder, klen, NULL); @@ -932,29 +1076,21 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx, if (data_interval) set->data->flags |= EXPR_F_INTERVAL; - - 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)); + if (set_udata_key_valid(typeof_expr_key, klen)) { set->key = typeof_expr_key; + typeof_expr_key = NULL; 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; @@ -981,6 +1117,13 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx, } } +out: + expr_free(typeof_expr_data); + expr_free(typeof_expr_key); + datatype_free(datatype); + datatype_free(keytype); + datatype_free(dtype2); + datatype_free(dtype); return set; } @@ -995,53 +1138,80 @@ void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls) } } -static bool mpz_bitmask_is_prefix(mpz_t bitmask, uint32_t len) +static bool range_expr_is_prefix(const struct expr *range, uint32_t *prefix_len) { + const struct expr *right = range->right; + const struct expr *left = range->left; + uint32_t len = left->len; unsigned long n1, n2; + uint32_t plen; + mpz_t bitmask; - n1 = mpz_scan0(bitmask, 0); - if (n1 == ULONG_MAX) - return false; + mpz_init2(bitmask, left->len); + mpz_xor(bitmask, left->value, right->value); - n2 = mpz_scan1(bitmask, n1 + 1); - if (n2 < len) - return false; + n1 = mpz_scan0(bitmask, 0); + if (n1 == ULONG_MAX) + goto not_a_prefix; - return true; -} + n2 = mpz_scan1(bitmask, n1 + 1); + if (n2 < len) + goto not_a_prefix; -static uint32_t mpz_bitmask_to_prefix(mpz_t bitmask, uint32_t len) -{ - return len - mpz_scan0(bitmask, 0); + plen = len - n1; + + if (mpz_scan1(left->value, 0) < len - plen) + goto not_a_prefix; + + mpz_clear(bitmask); + *prefix_len = plen; + + return true; + +not_a_prefix: + mpz_clear(bitmask); + + return false; } struct expr *range_expr_to_prefix(struct expr *range) { - struct expr *left = range->left, *right = range->right, *prefix; - uint32_t len = left->len, prefix_len; - mpz_t bitmask; - - mpz_init2(bitmask, len); - mpz_xor(bitmask, left->value, right->value); + struct expr *prefix; + uint32_t prefix_len; - if (mpz_bitmask_is_prefix(bitmask, len)) { - prefix_len = mpz_bitmask_to_prefix(bitmask, len); - prefix = prefix_expr_alloc(&range->location, expr_get(left), + if (range_expr_is_prefix(range, &prefix_len)) { + prefix = prefix_expr_alloc(&range->location, + expr_get(range->left), prefix_len); - mpz_clear(bitmask); expr_free(range); - return prefix; } - mpz_clear(bitmask); return range; } -static struct expr *netlink_parse_interval_elem(const struct datatype *dtype, +static struct expr *range_expr_reduce(struct expr *range) +{ + struct expr *expr; + + if (!mpz_cmp(range->left->value, range->right->value)) { + expr = expr_get(range->left); + expr_free(range); + return expr; + } + + if (range->left->dtype->type != TYPE_IPADDR && + range->left->dtype->type != TYPE_IP6ADDR) + return range; + + return range_expr_to_prefix(range); +} + +static struct expr *netlink_parse_interval_elem(const struct set *set, struct expr *expr) { unsigned int len = div_round_up(expr->len, BITS_PER_BYTE); + const struct datatype *dtype = set->data->dtype; struct expr *range, *left, *right; char data[len]; @@ -1058,31 +1228,103 @@ static struct expr *netlink_parse_interval_elem(const struct datatype *dtype, return range_expr_to_prefix(range); } -static struct expr *netlink_parse_concat_elem(const struct datatype *dtype, - struct expr *data) +static struct expr *concat_elem_expr(const struct set *set, struct expr *key, + const struct datatype *dtype, + struct expr *data, int *off) { const struct datatype *subtype; - struct expr *concat, *expr; + unsigned int sub_length; + struct expr *expr; + + if (key) { + (*off)--; + sub_length = round_up(key->len, BITS_PER_BYTE); + + expr = constant_expr_splice(data, sub_length); + expr->dtype = datatype_get(key->dtype); + expr->byteorder = key->byteorder; + expr->len = key->len; + } else { + subtype = concat_subtype_lookup(dtype->type, --(*off)); + sub_length = round_up(subtype->size, BITS_PER_BYTE); + expr = constant_expr_splice(data, sub_length); + expr->dtype = subtype; + expr->byteorder = subtype->byteorder; + } + + if (expr_basetype(expr)->type == TYPE_STRING || + (!(set->flags & NFT_SET_INTERVAL) && + expr->byteorder == BYTEORDER_HOST_ENDIAN)) + mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + + if (expr->dtype->basetype != NULL && + expr->dtype->basetype->type == TYPE_BITMASK) + expr = bitmask_expr_to_binops(expr); + + data->len -= netlink_padding_len(sub_length); + + return expr; +} + +static struct expr *netlink_parse_concat_elem_key(const struct set *set, + struct expr *data) +{ + const struct datatype *dtype = set->key->dtype; + struct expr *concat, *expr, *n = NULL; int off = dtype->subtypes; + if (set->key->etype == EXPR_CONCAT) + n = list_first_entry(&set->key->expressions, struct expr, list); + concat = concat_expr_alloc(&data->location); while (off > 0) { - subtype = concat_subtype_lookup(dtype->type, --off); + expr = concat_elem_expr(set, n, dtype, data, &off); + compound_expr_add(concat, expr); + if (set->key->etype == EXPR_CONCAT) + n = list_next_entry(n, list); + } - expr = constant_expr_splice(data, subtype->size); - expr->dtype = subtype; - expr->byteorder = subtype->byteorder; + expr_free(data); - if (expr->byteorder == BYTEORDER_HOST_ENDIAN) - mpz_switch_byteorder(expr->value, expr->len / BITS_PER_BYTE); + return concat; +} - if (expr->dtype->basetype != NULL && - expr->dtype->basetype->type == TYPE_BITMASK) - expr = bitmask_expr_to_binops(expr); +static struct expr *netlink_parse_concat_elem(const struct set *set, + struct expr *data) +{ + const struct datatype *dtype = set->data->dtype; + struct expr *concat, *expr, *left, *range; + struct list_head expressions; + int off = dtype->subtypes; - compound_expr_add(concat, expr); - data->len -= netlink_padding_len(expr->len); + init_list_head(&expressions); + + concat = concat_expr_alloc(&data->location); + while (off > 0) { + expr = concat_elem_expr(set, NULL, dtype, data, &off); + list_add_tail(&expr->list, &expressions); + } + + if (set->data->flags & EXPR_F_INTERVAL) { + assert(!list_empty(&expressions)); + + off = dtype->subtypes; + + while (off > 0) { + left = list_first_entry(&expressions, struct expr, list); + + expr = concat_elem_expr(set, NULL, dtype, data, &off); + list_del(&left->list); + + range = range_expr_alloc(&data->location, left, expr); + range = range_expr_reduce(range); + compound_expr_add(concat, range); + } + assert(list_empty(&expressions)); + } else { + list_splice_tail(&expressions, &concat->expressions); } + expr_free(data); return concat; @@ -1154,7 +1396,7 @@ key_end: datatype_set(key, set->key->dtype); key->byteorder = set->key->byteorder; if (set->key->dtype->subtypes) - key = netlink_parse_concat_elem(set->key->dtype, key); + key = netlink_parse_concat_elem_key(set, key); if (!(set->flags & NFT_SET_INTERVAL) && key->byteorder == BYTEORDER_HOST_ENDIAN) @@ -1173,6 +1415,7 @@ key_end: } expr = set_elem_expr_alloc(&netlink_location, key); + expr->flags |= EXPR_F_KERNEL; if (nftnl_set_elem_is_set(nlse, NFTNL_SET_ELEM_TIMEOUT)) expr->timeout = nftnl_set_elem_get_u64(nlse, NFTNL_SET_ELEM_TIMEOUT); @@ -1191,7 +1434,7 @@ key_end: nftnl_set_elem_expr_foreach(nlse, set_elem_parse_expressions, &setelem_parse_ctx); } - list_splice_tail(&setelem_parse_ctx.stmt_list, &expr->stmt_list); + list_splice_tail_init(&setelem_parse_ctx.stmt_list, &expr->stmt_list); if (flags & NFT_SET_ELEM_INTERVAL_END) { expr->flags |= EXPR_F_INTERVAL_END; @@ -1217,10 +1460,10 @@ key_end: datatype_set(data, set->data->dtype); data->byteorder = set->data->byteorder; - if (set->data->flags & EXPR_F_INTERVAL) - data = netlink_parse_interval_elem(set->data->dtype, data); - else if (set->data->dtype->subtypes) - data = netlink_parse_concat_elem(set->data->dtype, data); + if (set->data->dtype->subtypes) { + data = netlink_parse_concat_elem(set, data); + } else if (set->data->flags & EXPR_F_INTERVAL) + data = netlink_parse_interval_elem(set, data); if (data->byteorder == BYTEORDER_HOST_ENDIAN) mpz_switch_byteorder(data->value, data->len / BITS_PER_BYTE); @@ -1298,7 +1541,7 @@ static int list_setelements(struct nftnl_set *s, struct netlink_ctx *ctx) } int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h, - struct set *set) + struct set *set, bool reset) { struct nftnl_set *nls; int err; @@ -1313,7 +1556,7 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h, if (h->handle.id) nftnl_set_set_u64(nls, NFTNL_SET_HANDLE, h->handle.id); - err = mnl_nft_setelem_get(ctx, nls); + err = mnl_nft_setelem_get(ctx, nls, reset); if (err < 0) { nftnl_set_free(nls); if (errno == EINTR) @@ -1341,7 +1584,7 @@ int netlink_list_setelems(struct netlink_ctx *ctx, const struct handle *h, int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h, const struct location *loc, struct set *cache_set, - struct set *set, struct expr *init) + struct set *set, struct expr *init, bool reset) { struct nftnl_set *nls, *nls_out = NULL; int err = 0; @@ -1360,7 +1603,7 @@ int netlink_get_setelem(struct netlink_ctx *ctx, const struct handle *h, netlink_dump_set(nls, ctx); - nls_out = mnl_nft_setelem_get_one(ctx, nls); + nls_out = mnl_nft_setelem_get_one(ctx, nls, reset); if (!nls_out) { nftnl_set_free(nls); return -1; @@ -1435,6 +1678,7 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, udata = nftnl_obj_get_data(nlo, NFTNL_OBJ_USERDATA, &ulen); if (nftnl_udata_parse(udata, ulen, obj_parse_udata_cb, ud) < 0) { netlink_io_error(ctx, NULL, "Cannot parse userdata"); + obj_free(obj); return NULL; } if (ud[NFTNL_UDATA_OBJ_COMMENT]) @@ -1468,6 +1712,7 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, obj->ct_helper.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_HELPER_L4PROTO); break; case NFT_OBJECT_CT_TIMEOUT: + init_list_head(&obj->ct_timeout.timeout_list); obj->ct_timeout.l3proto = nftnl_obj_get_u16(nlo, NFTNL_OBJ_CT_TIMEOUT_L3PROTO); obj->ct_timeout.l4proto = nftnl_obj_get_u8(nlo, NFTNL_OBJ_CT_TIMEOUT_L4PROTO); memcpy(obj->ct_timeout.timeout, @@ -1553,6 +1798,55 @@ int netlink_reset_objs(struct netlink_ctx *ctx, const struct cmd *cmd, return err; } +int netlink_reset_rules(struct netlink_ctx *ctx, const struct cmd *cmd, + bool dump) +{ + const struct handle *h = &cmd->handle; + struct nft_cache_filter f = { + .list.table = h->table.name, + .list.chain = h->chain.name, + .list.rule_handle = h->handle.id, + }; + struct rule *rule, *next, *crule, *cnext; + struct table *table; + struct chain *chain; + int ret; + + ret = rule_cache_dump(ctx, h, &f, dump, true); + + list_for_each_entry_safe(rule, next, &ctx->list, list) { + table = table_cache_find(&ctx->nft->cache.table_cache, + rule->handle.table.name, + rule->handle.family); + if (!table) + continue; + + chain = chain_cache_find(table, rule->handle.chain.name); + if (!chain) + continue; + + list_del(&rule->list); + list_for_each_entry_safe(crule, cnext, &chain->rules, list) { + if (crule->handle.handle.id != rule->handle.handle.id) + continue; + + list_replace(&crule->list, &rule->list); + rule_free(crule); + rule = NULL; + break; + } + if (rule) { + list_add_tail(&rule->list, &chain->rules); + } + } + list_for_each_entry_safe(rule, next, &ctx->list, list) { + list_del(&rule->list); + rule_free(rule); + } + + return ret; +} + struct flowtable * netlink_delinearize_flowtable(struct netlink_ctx *ctx, struct nftnl_flowtable *nlo) @@ -1576,12 +1870,18 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx, while (dev_array[len]) len++; - flowtable->dev_array = calloc(1, len * sizeof(char *)); + if (len) + flowtable->dev_array = xmalloc(len * sizeof(char *)); for (i = 0; i < len; i++) flowtable->dev_array[i] = xstrdup(dev_array[i]); flowtable->dev_array_len = len; + if (flowtable->dev_array_len) { + qsort(flowtable->dev_array, flowtable->dev_array_len, + sizeof(char *), qsort_device_cmp); + } + priority = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO); flowtable->priority.expr = constant_expr_alloc(&netlink_location, @@ -1615,7 +1915,8 @@ int netlink_list_flowtables(struct netlink_ctx *ctx, const struct handle *h) struct nftnl_flowtable_list *flowtable_cache; int err; - flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family, h->table.name); + flowtable_cache = mnl_nft_flowtable_dump(ctx, h->family, + h->table.name, NULL); if (flowtable_cache == NULL) { if (errno == EINTR) return -1; @@ -1761,7 +2062,6 @@ static void trace_gen_stmts(struct list_head *stmts, const void *hdr; uint32_t hlen; unsigned int n; - bool stacked; if (!nftnl_trace_is_set(nlt, attr)) return; @@ -1816,6 +2116,8 @@ restart: n = 0; next: list_for_each_entry(stmt, &unordered, list) { + enum proto_bases b = base; + rel = stmt->expr; lhs = rel->left; @@ -1828,18 +2130,14 @@ next: list_move_tail(&stmt->list, stmts); n++; - stacked = payload_is_stacked(desc, rel); + if (payload_is_stacked(desc, rel)) + b--; - if (lhs->flags & EXPR_F_PROTOCOL && - pctx->pbase == PROTO_BASE_INVALID) { - payload_dependency_store(pctx, stmt, base - stacked); - } else { - /* Don't strip 'icmp type' from payload dump. */ - if (pctx->icmp_type == 0) - payload_dependency_kill(pctx, lhs, ctx->family); - if (lhs->flags & EXPR_F_PROTOCOL) - payload_dependency_store(pctx, stmt, base - stacked); - } + /* Don't strip 'icmp type' from payload dump. */ + if (pctx->icmp_type == 0) + payload_dependency_kill(pctx, lhs, ctx->family); + if (lhs->flags & EXPR_F_PROTOCOL) + payload_dependency_store(pctx, stmt, b); goto next; } @@ -1868,7 +2166,7 @@ static void trace_print_packet(const struct nftnl_trace *nlt, meta_expr_alloc(&netlink_location, NFT_META_OIF), octx); - proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0); + proto_ctx_init(&ctx, nftnl_trace_get_u32(nlt, NFTNL_TRACE_FAMILY), 0, false); ll_desc = ctx.protocol[PROTO_BASE_LL_HDR].desc; if ((ll_desc == &proto_inet || ll_desc == &proto_netdev) && nftnl_trace_is_set(nlt, NFTNL_TRACE_NFPROTO)) { |