diff options
-rw-r--r-- | include/datatype.h | 4 | ||||
-rw-r--r-- | include/expression.h | 11 | ||||
-rw-r--r-- | include/linux/netfilter/nf_tables.h | 39 | ||||
-rw-r--r-- | include/rule.h | 5 | ||||
-rw-r--r-- | include/statement.h | 11 | ||||
-rw-r--r-- | src/datatype.c | 82 | ||||
-rw-r--r-- | src/evaluate.c | 47 | ||||
-rw-r--r-- | src/expression.c | 42 | ||||
-rw-r--r-- | src/netlink.c | 83 | ||||
-rw-r--r-- | src/netlink_delinearize.c | 41 | ||||
-rw-r--r-- | src/netlink_linearize.c | 26 | ||||
-rw-r--r-- | src/parser_bison.y | 105 | ||||
-rw-r--r-- | src/rule.c | 23 | ||||
-rw-r--r-- | src/scanner.l | 10 | ||||
-rw-r--r-- | src/segtree.c | 8 | ||||
-rw-r--r-- | src/statement.c | 31 |
16 files changed, 486 insertions, 82 deletions
diff --git a/include/datatype.h b/include/datatype.h index 3c3f42f3..2a6a4fca 100644 --- a/include/datatype.h +++ b/include/datatype.h @@ -231,4 +231,8 @@ concat_subtype_lookup(uint32_t type, unsigned int n) return datatype_lookup(concat_subtype_id(type, n)); } +extern void time_print(uint64_t seconds); +extern struct error_record *time_parse(const struct location *loc, + const char *c, uint64_t *res); + #endif /* NFTABLES_DATATYPE_H */ diff --git a/include/expression.h b/include/expression.h index 7477c3e6..010cb954 100644 --- a/include/expression.h +++ b/include/expression.h @@ -27,6 +27,7 @@ * @EXPR_LIST: list of expressions * @EXPR_SET: literal set * @EXPR_SET_REF: set reference + * @EXPR_SET_ELEM: set element * @EXPR_MAPPING: a single mapping (key : value) * @EXPR_MAP: map operation (expr map { EXPR_MAPPING, ... }) * @EXPR_UNARY: byteorder conversion, generated during evaluation @@ -48,6 +49,7 @@ enum expr_types { EXPR_LIST, EXPR_SET, EXPR_SET_REF, + EXPR_SET_ELEM, EXPR_MAPPING, EXPR_MAP, EXPR_UNARY, @@ -230,6 +232,13 @@ struct expr { struct set *set; }; struct { + /* EXPR_SET_ELEM */ + struct expr *key; + uint64_t timeout; + uint64_t expiration; + const char *comment; + }; + struct { /* EXPR_UNARY */ struct expr *arg; }; @@ -363,6 +372,8 @@ extern struct expr *map_expr_alloc(const struct location *loc, extern struct expr *set_ref_expr_alloc(const struct location *loc, struct set *set); +extern struct expr *set_elem_expr_alloc(const struct location *loc, + struct expr *key); extern void range_expr_value_low(mpz_t rop, const struct expr *expr); extern void range_expr_value_high(mpz_t rop, const struct expr *expr); diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 832bc46d..0e964439 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -207,12 +207,14 @@ enum nft_rule_compat_attributes { * @NFT_SET_CONSTANT: set contents may not change while bound * @NFT_SET_INTERVAL: set contains intervals * @NFT_SET_MAP: set is used as a dictionary + * @NFT_SET_TIMEOUT: set uses timeouts */ enum nft_set_flags { NFT_SET_ANONYMOUS = 0x1, NFT_SET_CONSTANT = 0x2, NFT_SET_INTERVAL = 0x4, NFT_SET_MAP = 0x8, + NFT_SET_TIMEOUT = 0x10, }; /** @@ -251,6 +253,8 @@ enum nft_set_desc_attributes { * @NFTA_SET_POLICY: selection policy (NLA_U32) * @NFTA_SET_DESC: set description (NLA_NESTED) * @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32) + * @NFTA_SET_TIMEOUT: default timeout value (NLA_U64) + * @NFTA_SET_GC_INTERVAL: garbage collection interval (NLA_U32) */ enum nft_set_attributes { NFTA_SET_UNSPEC, @@ -264,6 +268,8 @@ enum nft_set_attributes { NFTA_SET_POLICY, NFTA_SET_DESC, NFTA_SET_ID, + NFTA_SET_TIMEOUT, + NFTA_SET_GC_INTERVAL, __NFTA_SET_MAX }; #define NFTA_SET_MAX (__NFTA_SET_MAX - 1) @@ -283,12 +289,18 @@ enum nft_set_elem_flags { * @NFTA_SET_ELEM_KEY: key value (NLA_NESTED: nft_data) * @NFTA_SET_ELEM_DATA: data value of mapping (NLA_NESTED: nft_data_attributes) * @NFTA_SET_ELEM_FLAGS: bitmask of nft_set_elem_flags (NLA_U32) + * @NFTA_SET_ELEM_TIMEOUT: timeout value (NLA_U64) + * @NFTA_SET_ELEM_EXPIRATION: expiration time (NLA_U64) + * @NFTA_SET_ELEM_USERDATA: user data (NLA_BINARY) */ enum nft_set_elem_attributes { NFTA_SET_ELEM_UNSPEC, NFTA_SET_ELEM_KEY, NFTA_SET_ELEM_DATA, NFTA_SET_ELEM_FLAGS, + NFTA_SET_ELEM_TIMEOUT, + NFTA_SET_ELEM_EXPIRATION, + NFTA_SET_ELEM_USERDATA, __NFTA_SET_ELEM_MAX }; #define NFTA_SET_ELEM_MAX (__NFTA_SET_ELEM_MAX - 1) @@ -504,6 +516,33 @@ enum nft_lookup_attributes { }; #define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1) +enum nft_dynset_ops { + NFT_DYNSET_OP_ADD, + NFT_DYNSET_OP_UPDATE, +}; + +/** + * enum nft_dynset_attributes - dynset expression attributes + * + * @NFTA_DYNSET_SET_NAME: name of set the to add data to (NLA_STRING) + * @NFTA_DYNSET_SET_ID: uniquely identifier of the set in the transaction (NLA_U32) + * @NFTA_DYNSET_OP: operation (NLA_U32) + * @NFTA_DYNSET_SREG_KEY: source register of the key (NLA_U32) + * @NFTA_DYNSET_SREG_DATA: source register of the data (NLA_U32) + * @NFTA_DYNSET_TIMEOUT: timeout value for the new element (NLA_U64) + */ +enum nft_dynset_attributes { + NFTA_DYNSET_UNSPEC, + NFTA_DYNSET_SET_NAME, + NFTA_DYNSET_SET_ID, + NFTA_DYNSET_OP, + NFTA_DYNSET_SREG_KEY, + NFTA_DYNSET_SREG_DATA, + NFTA_DYNSET_TIMEOUT, + __NFTA_DYNSET_MAX, +}; +#define NFTA_DYNSET_MAX (__NFTA_DYNSET_MAX - 1) + /** * enum nft_payload_bases - nf_tables payload expression offset bases * diff --git a/include/rule.h b/include/rule.h index 97959f7b..5d445993 100644 --- a/include/rule.h +++ b/include/rule.h @@ -173,6 +173,7 @@ enum set_flags { SET_F_CONSTANT = 0x2, SET_F_INTERVAL = 0x4, SET_F_MAP = 0x8, + SET_F_TIMEOUT = 0x10, }; /** @@ -183,6 +184,8 @@ enum set_flags { * @location: location the set was defined/declared at * @refcnt: reference count * @flags: bitmask of set flags + * @gc_int: garbage collection interval + * @timeout: default timeout value * @keytype: key data type * @keylen: key length * @datatype: mapping data type @@ -197,6 +200,8 @@ struct set { struct location location; unsigned int refcnt; uint32_t flags; + uint32_t gc_int; + uint64_t timeout; const struct datatype *keytype; unsigned int keylen; const struct datatype *datatype; diff --git a/include/statement.h b/include/statement.h index d1431215..48e61307 100644 --- a/include/statement.h +++ b/include/statement.h @@ -104,6 +104,14 @@ extern struct stmt *ct_stmt_alloc(const struct location *loc, enum nft_ct_keys key, struct expr *expr); +struct set_stmt { + struct expr *set; + struct expr *key; + enum nft_dynset_ops op; +}; + +extern struct stmt *set_stmt_alloc(const struct location *loc); + /** * enum stmt_types - statement types * @@ -120,6 +128,7 @@ extern struct stmt *ct_stmt_alloc(const struct location *loc, * @STMT_REDIR: redirect statement * @STMT_QUEUE: QUEUE statement * @STMT_CT: conntrack statement + * @STMT_SET: set statement */ enum stmt_types { STMT_INVALID, @@ -135,6 +144,7 @@ enum stmt_types { STMT_REDIR, STMT_QUEUE, STMT_CT, + STMT_SET, }; /** @@ -184,6 +194,7 @@ struct stmt { struct redir_stmt redir; struct queue_stmt queue; struct ct_stmt ct; + struct set_stmt set; }; }; diff --git a/src/datatype.c b/src/datatype.c index c93f76a3..f93337b1 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -760,11 +760,9 @@ const struct datatype icmpx_code_type = { .sym_tbl = &icmpx_code_tbl, }; -static void time_type_print(const struct expr *expr) +void time_print(uint64_t seconds) { - uint64_t days, hours, minutes, seconds; - - seconds = mpz_get_uint64(expr->value); + uint64_t days, hours, minutes; days = seconds / 86400; seconds %= 86400; @@ -775,8 +773,6 @@ static void time_type_print(const struct expr *expr) minutes = seconds / 60; seconds %= 60; - printf("\""); - if (days > 0) printf("%"PRIu64"d", days); if (hours > 0) @@ -785,8 +781,6 @@ static void time_type_print(const struct expr *expr) printf("%"PRIu64"m", minutes); if (seconds > 0) printf("%"PRIu64"s", seconds); - - printf("\""); } enum { @@ -805,8 +799,8 @@ static uint32_t str2int(char *tmp, const char *c, int k) return atoi(tmp); } -static struct error_record *time_type_parse(const struct expr *sym, - struct expr **res) +struct error_record *time_parse(const struct location *loc, const char *str, + uint64_t *res) { int i, len; unsigned int k = 0; @@ -815,76 +809,82 @@ static struct error_record *time_type_parse(const struct expr *sym, uint64_t d = 0, h = 0, m = 0, s = 0; uint32_t mask = 0; - c = sym->identifier; + c = str; len = strlen(c); for (i = 0; i < len; i++, c++) { switch (*c) { case 'd': - if (mask & DAY) { - return error(&sym->location, + if (mask & DAY) + return error(loc, "Day has been specified twice"); - } + d = str2int(tmp, c, k); k = 0; mask |= DAY; break; case 'h': - if (mask & HOUR) { - return error(&sym->location, + if (mask & HOUR) + return error(loc, "Hour has been specified twice"); - } + h = str2int(tmp, c, k); k = 0; - if (h > 23) { - return error(&sym->location, - "Hour needs to be 0-23"); - } mask |= HOUR; break; case 'm': - if (mask & MIN) { - return error(&sym->location, + if (mask & MIN) + return error(loc, "Minute has been specified twice"); - } + m = str2int(tmp, c, k); k = 0; - if (m > 59) { - return error(&sym->location, - "Minute needs to be 0-59"); - } mask |= MIN; break; case 's': - if (mask & SECS) { - return error(&sym->location, + if (mask & SECS) + return error(loc, "Second has been specified twice"); - } + s = str2int(tmp, c, k); k = 0; - if (s > 59) { - return error(&sym->location, - "second needs to be 0-59"); - } mask |= SECS; break; default: if (!isdigit(*c)) - return error(&sym->location, "wrong format"); + return error(loc, "wrong time format"); - if (k++ >= array_size(tmp)) { - return error(&sym->location, - "value too large"); - } + if (k++ >= array_size(tmp)) + return error(loc, "value too large"); break; } } /* default to seconds if no unit was specified */ if (!mask) - s = atoi(sym->identifier); + s = atoi(str); else s = 24*60*60*d+60*60*h+60*m+s; + *res = s; + return NULL; +} + + +static void time_type_print(const struct expr *expr) +{ + time_print(mpz_get_uint64(expr->value)); +} + +static struct error_record *time_type_parse(const struct expr *sym, + struct expr **res) +{ + struct error_record *erec; + uint64_t s; + + erec = time_parse(&sym->location, sym->identifier, &s); + if (erec != NULL) + return erec; + if (s > UINT32_MAX) return error(&sym->location, "value too large"); diff --git a/src/evaluate.c b/src/evaluate.c index 7ecb7939..e260a803 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -614,7 +614,7 @@ static int expr_evaluate_concat(struct eval_ctx *ctx, struct expr **expr) struct expr *i, *next; list_for_each_entry_safe(i, next, &(*expr)->expressions, list) { - if (dtype && off == 0) + if (expr_is_constant(*expr) && dtype && off == 0) return expr_binary_error(ctx->msgs, i, *expr, "unexpected concat component, " "expecting %s", @@ -675,6 +675,19 @@ static int expr_evaluate_list(struct eval_ctx *ctx, struct expr **expr) return 0; } +static int expr_evaluate_set_elem(struct eval_ctx *ctx, struct expr **expr) +{ + struct expr *elem = *expr; + + if (expr_evaluate(ctx, &elem->key) < 0) + return -1; + + elem->dtype = elem->key->dtype; + elem->len = elem->key->len; + elem->flags = elem->key->flags; + return 0; +} + static int expr_evaluate_set(struct eval_ctx *ctx, struct expr **expr) { struct expr *set = *expr, *i, *next; @@ -1100,6 +1113,8 @@ static int expr_evaluate(struct eval_ctx *ctx, struct expr **expr) return expr_evaluate_list(ctx, expr); case EXPR_SET: return expr_evaluate_set(ctx, expr); + case EXPR_SET_ELEM: + return expr_evaluate_set_elem(ctx, expr); case EXPR_MAP: return expr_evaluate_map(ctx, expr); case EXPR_MAPPING: @@ -1646,6 +1661,30 @@ static int stmt_evaluate_log(struct eval_ctx *ctx, struct stmt *stmt) return 0; } +static int stmt_evaluate_set(struct eval_ctx *ctx, struct stmt *stmt) +{ + expr_set_context(&ctx->ectx, NULL, 0); + if (expr_evaluate(ctx, &stmt->set.set) < 0) + return -1; + if (stmt->set.set->ops->type != EXPR_SET_REF) + return expr_error(ctx->msgs, stmt->set.set, + "Expression does not refer to a set"); + + if (stmt_evaluate_arg(ctx, stmt, + stmt->set.set->set->keytype, + stmt->set.set->set->keylen, + &stmt->set.key) < 0) + return -1; + if (expr_is_constant(stmt->set.key)) + return expr_error(ctx->msgs, stmt->set.key, + "Key expression can not be constant"); + if (stmt->set.key->comment != NULL) + return expr_error(ctx->msgs, stmt->set.key, + "Key expression comments are not supported"); + + return 0; +} + int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt) { #ifdef DEBUG @@ -1680,6 +1719,8 @@ int stmt_evaluate(struct eval_ctx *ctx, struct stmt *stmt) return stmt_evaluate_redir(ctx, stmt); case STMT_QUEUE: return stmt_evaluate_queue(ctx, stmt); + case STMT_SET: + return stmt_evaluate_set(ctx, stmt); default: BUG("unknown statement type %s\n", stmt->ops->name); } @@ -1722,6 +1763,10 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set) return -1; } + /* Default timeout value implies timeout support */ + if (set->timeout) + set->flags |= SET_F_TIMEOUT; + if (!(set->flags & SET_F_MAP)) return 0; diff --git a/src/expression.c b/src/expression.c index 5b848da7..3edc5501 100644 --- a/src/expression.c +++ b/src/expression.c @@ -886,6 +886,44 @@ struct expr *set_ref_expr_alloc(const struct location *loc, struct set *set) return expr; } +static void set_elem_expr_print(const struct expr *expr) +{ + expr_print(expr->key); + if (expr->timeout) { + printf(" timeout "); + time_print(expr->timeout / 1000); + } + if (expr->expiration) { + printf(" expires "); + time_print(expr->expiration / 1000); + } + if (expr->comment) + printf(" comment \"%s\"", expr->comment); +} + +static void set_elem_expr_destroy(struct expr *expr) +{ + xfree(expr->comment); + expr_free(expr->key); +} + +static const struct expr_ops set_elem_expr_ops = { + .type = EXPR_SET_ELEM, + .name = "set element", + .print = set_elem_expr_print, + .destroy = set_elem_expr_destroy, +}; + +struct expr *set_elem_expr_alloc(const struct location *loc, struct expr *key) +{ + struct expr *expr; + + expr = expr_alloc(loc, &set_elem_expr_ops, key->dtype, + key->byteorder, key->len); + expr->key = key; + return expr; +} + void range_expr_value_low(mpz_t rop, const struct expr *expr) { switch (expr->ops->type) { @@ -897,6 +935,8 @@ void range_expr_value_low(mpz_t rop, const struct expr *expr) return range_expr_value_low(rop, expr->left); case EXPR_MAPPING: return range_expr_value_low(rop, expr->left); + case EXPR_SET_ELEM: + return range_expr_value_low(rop, expr->key); default: BUG("invalid range expression type %s\n", expr->ops->name); } @@ -919,6 +959,8 @@ void range_expr_value_high(mpz_t rop, const struct expr *expr) return range_expr_value_high(rop, expr->right); case EXPR_MAPPING: return range_expr_value_high(rop, expr->left); + case EXPR_SET_ELEM: + return range_expr_value_high(rop, expr->key); default: BUG("invalid range expression type %s\n", expr->ops->name); } diff --git a/src/netlink.c b/src/netlink.c index 343d8bea..d31387f8 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -205,6 +205,7 @@ struct nft_set *alloc_nft_set(const struct handle *h) static struct nft_set_elem *alloc_nft_setelem(const struct expr *expr) { + const struct expr *elem, *key, *data; struct nft_set_elem *nlse; struct nft_data_linearize nld; @@ -212,24 +213,34 @@ static struct nft_set_elem *alloc_nft_setelem(const struct expr *expr) if (nlse == NULL) memory_allocation_error(); - if (expr->ops->type == EXPR_VALUE || - expr->flags & EXPR_F_INTERVAL_END) { - netlink_gen_data(expr, &nld); - nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_KEY, - &nld.value, nld.len); + data = NULL; + if (expr->ops->type == EXPR_MAPPING) { + elem = expr->left; + if (!(expr->flags & EXPR_F_INTERVAL_END)) + data = expr->right; } else { - assert(expr->ops->type == EXPR_MAPPING); - netlink_gen_data(expr->left, &nld); - nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_KEY, - &nld.value, nld.len); - netlink_gen_data(expr->right, &nld); - switch (expr->right->ops->type) { + elem = expr; + } + key = elem->key; + + netlink_gen_data(key, &nld); + nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_KEY, &nld.value, nld.len); + if (elem->timeout) + nft_set_elem_attr_set_u64(nlse, NFT_SET_ELEM_ATTR_TIMEOUT, + elem->timeout); + if (elem->comment) + nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_USERDATA, + elem->comment, strlen(elem->comment) + 1); + + if (data != NULL) { + netlink_gen_data(data, &nld); + switch (data->ops->type) { case EXPR_VERDICT: nft_set_elem_attr_set_u32(nlse, NFT_SET_ELEM_ATTR_VERDICT, - expr->right->verdict); - if (expr->chain != NULL) + data->verdict); + if (data->chain != NULL) nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_CHAIN, - nld.chain, strlen(nld.chain)); + nld.chain, strlen(nld.chain)); break; case EXPR_VALUE: nft_set_elem_attr_set(nlse, NFT_SET_ELEM_ATTR_DATA, @@ -1063,6 +1074,11 @@ static struct set *netlink_delinearize_set(struct netlink_ctx *ctx, set->datalen = data_len * BITS_PER_BYTE; } + if (nft_set_attr_is_set(nls, NFT_SET_ATTR_TIMEOUT)) + set->timeout = nft_set_attr_get_u64(nls, NFT_SET_ATTR_TIMEOUT); + if (nft_set_attr_is_set(nls, NFT_SET_ATTR_GC_INTERVAL)) + set->gc_int = nft_set_attr_get_u32(nls, NFT_SET_ATTR_GC_INTERVAL); + if (nft_set_attr_is_set(nls, NFT_SET_ATTR_POLICY)) set->policy = nft_set_attr_get_u32(nls, NFT_SET_ATTR_POLICY); @@ -1126,6 +1142,11 @@ static int netlink_add_set_batch(struct netlink_ctx *ctx, nft_set_attr_set_u32(nls, NFT_SET_ATTR_DATA_LEN, set->datalen / BITS_PER_BYTE); } + if (set->timeout) + nft_set_attr_set_u64(nls, NFT_SET_ATTR_TIMEOUT, set->timeout); + if (set->gc_int) + nft_set_attr_set_u32(nls, NFT_SET_ATTR_GC_INTERVAL, set->gc_int); + set->handle.set_id = ++set_id; nft_set_attr_set_u32(nls, NFT_SET_ATTR_ID, set->handle.set_id); @@ -1368,7 +1389,7 @@ static int netlink_delinearize_setelem(struct nft_set_elem *nlse, struct set *set) { struct nft_data_delinearize nld; - struct expr *expr, *data; + struct expr *expr, *key, *data; uint32_t flags = 0; nld.value = @@ -1376,17 +1397,31 @@ static int netlink_delinearize_setelem(struct nft_set_elem *nlse, if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_FLAGS)) flags = nft_set_elem_attr_get_u32(nlse, NFT_SET_ELEM_ATTR_FLAGS); - expr = netlink_alloc_value(&netlink_location, &nld); - expr->dtype = set->keytype; - expr->byteorder = set->keytype->byteorder; + key = netlink_alloc_value(&netlink_location, &nld); + key->dtype = set->keytype; + key->byteorder = set->keytype->byteorder; if (!(set->flags & SET_F_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); + key->byteorder == BYTEORDER_HOST_ENDIAN) + mpz_switch_byteorder(key->value, key->len / BITS_PER_BYTE); + + if (key->dtype->basetype != NULL && + key->dtype->basetype->type == TYPE_BITMASK) + key = bitmask_expr_to_binops(key); + + expr = set_elem_expr_alloc(&netlink_location, key); + if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_TIMEOUT)) + expr->timeout = nft_set_elem_attr_get_u64(nlse, NFT_SET_ELEM_ATTR_TIMEOUT); + if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_EXPIRATION)) + expr->expiration = nft_set_elem_attr_get_u64(nlse, NFT_SET_ELEM_ATTR_EXPIRATION); + if (nft_set_elem_attr_is_set(nlse, NFT_SET_ELEM_ATTR_USERDATA)) { + const void *data; + uint32_t len; + + data = nft_set_elem_attr_get(nlse, NFT_SET_ELEM_ATTR_USERDATA, &len); + expr->comment = xmalloc(len); + memcpy((char *)expr->comment, data, len); + } if (flags & NFT_SET_ELEM_INTERVAL_END) { expr->flags |= EXPR_F_INTERVAL_END; diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index b23d5875..e9ce76e2 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -692,6 +692,40 @@ static void netlink_parse_queue(struct netlink_parse_ctx *ctx, list_add_tail(&stmt->list, &ctx->rule->stmts); } +static void netlink_parse_dynset(struct netlink_parse_ctx *ctx, + const struct location *loc, + const struct nft_rule_expr *nle) +{ + struct expr *expr; + struct stmt *stmt; + struct set *set; + enum nft_registers sreg; + const char *name; + + name = nft_rule_expr_get_str(nle, NFT_EXPR_DYNSET_SET_NAME); + set = set_lookup(ctx->table, name); + if (set == NULL) + return netlink_error(ctx, loc, + "Unknown set '%s' in dynset statement", + name); + + sreg = netlink_parse_register(nle, NFT_EXPR_DYNSET_SREG_KEY); + expr = netlink_get_register(ctx, loc, sreg); + if (expr == NULL) + return netlink_error(ctx, loc, + "Dynset statement has no key expression"); + + expr = set_elem_expr_alloc(&expr->location, expr); + expr->timeout = nft_rule_expr_get_u64(nle, NFT_EXPR_DYNSET_TIMEOUT); + + stmt = set_stmt_alloc(loc); + stmt->set.set = set_ref_expr_alloc(loc, set); + stmt->set.op = nft_rule_expr_get_u32(nle, NFT_EXPR_DYNSET_OP); + stmt->set.key = expr; + + list_add_tail(&stmt->list, &ctx->rule->stmts); +} + static const struct { const char *name; void (*parse)(struct netlink_parse_ctx *ctx, @@ -715,6 +749,7 @@ static const struct { { .name = "masq", .parse = netlink_parse_masq }, { .name = "redir", .parse = netlink_parse_redir }, { .name = "queue", .parse = netlink_parse_queue }, + { .name = "dynset", .parse = netlink_parse_dynset }, }; static int netlink_parse_expr(struct nft_rule_expr *nle, void *arg) @@ -1031,6 +1066,9 @@ static void expr_postprocess(struct rule_pp_ctx *ctx, expr_postprocess(ctx, stmt, &expr->left); expr_postprocess(ctx, stmt, &expr->right); break; + case EXPR_SET_ELEM: + expr_postprocess(ctx, stmt, &expr->key); + break; case EXPR_SET_REF: case EXPR_EXTHDR: case EXPR_META: @@ -1135,6 +1173,9 @@ static void rule_parse_postprocess(struct netlink_parse_ctx *ctx, struct rule *r case STMT_REJECT: stmt_reject_postprocess(rctx, stmt); break; + case STMT_SET: + expr_postprocess(&rctx, stmt, &stmt->set.key); + break; default: break; } diff --git a/src/netlink_linearize.c b/src/netlink_linearize.c index 0a0b6864..fbc6ae12 100644 --- a/src/netlink_linearize.c +++ b/src/netlink_linearize.c @@ -529,6 +529,8 @@ static void netlink_gen_expr(struct netlink_linearize_ctx *ctx, return netlink_gen_meta(ctx, expr, dreg); case EXPR_CT: return netlink_gen_ct(ctx, expr, dreg); + case EXPR_SET_ELEM: + return netlink_gen_expr(ctx, expr->key, dreg); default: BUG("unknown expression type %s\n", expr->ops->name); } @@ -799,6 +801,28 @@ static void netlink_gen_ct_stmt(struct netlink_linearize_ctx *ctx, nft_rule_add_expr(ctx->nlr, nle); } +static void netlink_gen_set_stmt(struct netlink_linearize_ctx *ctx, + const struct stmt *stmt) +{ + struct nft_rule_expr *nle; + enum nft_registers sreg_key; + + sreg_key = get_register(ctx); + netlink_gen_expr(ctx, stmt->set.key, sreg_key); + release_register(ctx); + + nle = alloc_nft_expr("dynset"); + netlink_put_register(nle, NFT_EXPR_DYNSET_SREG_KEY, sreg_key); + nft_rule_expr_set_u64(nle, NFT_EXPR_DYNSET_TIMEOUT, + stmt->set.key->timeout); + nft_rule_expr_set_u32(nle, NFT_EXPR_DYNSET_OP, stmt->set.op); + nft_rule_expr_set_str(nle, NFT_EXPR_DYNSET_SET_NAME, + stmt->set.set->set->handle.set); + nft_rule_expr_set_u32(nle, NFT_EXPR_DYNSET_SET_ID, + stmt->set.set->set->handle.set_id); + nft_rule_add_expr(ctx->nlr, nle); +} + static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, const struct stmt *stmt) { @@ -827,6 +851,8 @@ static void netlink_gen_stmt(struct netlink_linearize_ctx *ctx, return netlink_gen_queue_stmt(ctx, stmt); case STMT_CT: return netlink_gen_ct_stmt(ctx, stmt); + case STMT_SET: + return netlink_gen_set_stmt(ctx, stmt); default: BUG("unknown statement type %s\n", stmt->ops->name); } diff --git a/src/parser_bison.y b/src/parser_bison.y index b86381d9..eac3fcbe 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -181,6 +181,7 @@ static void location_update(struct location *loc, struct location *rhs, int n) %token INET "inet" %token ADD "add" +%token UPDATE "update" %token CREATE "create" %token INSERT "insert" %token DELETE "delete" @@ -201,6 +202,8 @@ static void location_update(struct location *loc, struct location *rhs, int n) %token CONSTANT "constant" %token INTERVAL "interval" +%token TIMEOUT "timeout" +%token GC_INTERVAL "gc-interval" %token ELEMENTS "elements" %token POLICY "policy" @@ -396,6 +399,8 @@ static void location_update(struct location *loc, struct location *rhs, int n) %type <string> identifier string comment_spec %destructor { xfree($$); } identifier string comment_spec +%type <val> time_spec + %type <val> type_identifier %type <datatype> data_type @@ -452,6 +457,9 @@ static void location_update(struct location *loc, struct location *rhs, int n) %type <stmt> queue_stmt queue_stmt_alloc %destructor { stmt_free($$); } queue_stmt queue_stmt_alloc %type <val> queue_stmt_flags queue_stmt_flag +%type <stmt> set_stmt +%destructor { stmt_free($$); } set_stmt +%type <val> set_stmt_op %type <expr> symbol_expr verdict_expr integer_expr %destructor { expr_free($$); } symbol_expr verdict_expr integer_expr @@ -468,8 +476,8 @@ static void location_update(struct location *loc, struct location *rhs, int n) %destructor { expr_free($$); } prefix_expr range_expr wildcard_expr %type <expr> list_expr %destructor { expr_free($$); } list_expr -%type <expr> concat_expr map_lhs_expr -%destructor { expr_free($$); } concat_expr map_lhs_expr +%type <expr> concat_expr +%destructor { expr_free($$); } concat_expr %type <expr> map_expr %destructor { expr_free($$); } map_expr @@ -482,6 +490,8 @@ static void location_update(struct location *loc, struct location *rhs, int n) %type <expr> set_expr set_list_expr set_list_member_expr %destructor { expr_free($$); } set_expr set_list_expr set_list_member_expr +%type <expr> set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr +%destructor { expr_free($$); } set_elem_expr set_elem_expr_alloc set_lhs_expr set_rhs_expr %type <expr> expr initializer_expr %destructor { expr_free($$); } expr initializer_expr @@ -940,6 +950,16 @@ set_block : /* empty */ { $$ = $<set>-1; } $1->flags = $3; $$ = $1; } + | set_block TIMEOUT time_spec stmt_seperator + { + $1->timeout = $3 * 1000; + $$ = $1; + } + | set_block GC_INTERVAL time_spec stmt_seperator + { + $1->gc_int = $3 * 1000; + $$ = $1; + } | set_block ELEMENTS '=' set_expr { $1->init = $4; @@ -957,6 +977,7 @@ set_flag_list : set_flag_list COMMA set_flag set_flag : CONSTANT { $$ = SET_F_CONSTANT; } | INTERVAL { $$ = SET_F_INTERVAL; } + | TIMEOUT { $$ = SET_F_TIMEOUT; } ; map_block_alloc : /* empty */ @@ -1093,6 +1114,20 @@ string : STRING | QUOTED_STRING ; +time_spec : STRING + { + struct error_record *erec; + uint64_t res; + + erec = time_parse(&@1, $1, &res); + if (erec != NULL) { + erec_queue(erec, state->msgs); + YYERROR; + } + $$ = res; + } + ; + family_spec : /* empty */ { $$ = NFPROTO_IPV4; } | family_spec_explicit ; @@ -1236,6 +1271,7 @@ stmt : verdict_stmt | ct_stmt | masq_stmt | redir_stmt + | set_stmt ; verdict_stmt : verdict_expr @@ -1281,13 +1317,12 @@ verdict_map_list_expr : verdict_map_list_member_expr | verdict_map_list_expr COMMA opt_newline ; -verdict_map_list_member_expr: opt_newline map_lhs_expr COLON verdict_expr opt_newline +verdict_map_list_member_expr: opt_newline set_elem_expr COLON verdict_expr opt_newline { $$ = mapping_expr_alloc(&@$, $2, $4); } ; - counter_stmt : counter_stmt_alloc | counter_stmt_alloc counter_args @@ -1549,6 +1584,19 @@ queue_stmt_flag : BYPASS { $$ = NFT_QUEUE_FLAG_BYPASS; } | FANOUT { $$ = NFT_QUEUE_FLAG_CPU_FANOUT; } ; +set_stmt : SET set_stmt_op set_elem_expr symbol_expr + { + $$ = set_stmt_alloc(&@$); + $$->set.op = $2; + $$->set.key = $3; + $$->set.set = $4; + } + ; + +set_stmt_op : ADD { $$ = NFT_DYNSET_OP_ADD; } + | UPDATE { $$ = NFT_DYNSET_OP_UPDATE; } + ; + match_stmt : relational_expr { $$ = expr_stmt_alloc(&@$, $1); @@ -1702,10 +1750,6 @@ multiton_expr : prefix_expr | wildcard_expr ; -map_lhs_expr : multiton_expr - | concat_expr - ; - map_expr : concat_expr MAP expr { $$ = map_expr_alloc(&@$, $1, $3); @@ -1713,9 +1757,9 @@ map_expr : concat_expr MAP expr ; expr : concat_expr + | multiton_expr | set_expr | map_expr - | multiton_expr ; set_expr : '{' set_list_expr '}' @@ -1738,20 +1782,55 @@ set_list_expr : set_list_member_expr | set_list_expr COMMA opt_newline ; -set_list_member_expr : opt_newline expr opt_newline +set_list_member_expr : opt_newline set_expr opt_newline { $$ = $2; } - | opt_newline map_lhs_expr COLON concat_expr opt_newline + | opt_newline set_elem_expr opt_newline { - $$ = mapping_expr_alloc(&@$, $2, $4); + $$ = $2; } - | opt_newline map_lhs_expr COLON verdict_expr opt_newline + | opt_newline set_elem_expr COLON set_rhs_expr opt_newline { $$ = mapping_expr_alloc(&@$, $2, $4); } ; +set_elem_expr : set_elem_expr_alloc + | set_elem_expr_alloc set_elem_options + ; + +set_elem_expr_alloc : set_lhs_expr + { + $$ = set_elem_expr_alloc(&@1, $1); + } + ; + +set_elem_options : set_elem_option + { + $<expr>$ = $<expr>0; + } + | set_elem_options set_elem_option + ; + +set_elem_option : TIMEOUT time_spec + { + $<expr>0->timeout = $2 * 1000; + } + | COMMENT string + { + $<expr>0->comment = $2; + } + ; + +set_lhs_expr : concat_expr + | multiton_expr + ; + +set_rhs_expr : concat_expr + | verdict_expr + ; + initializer_expr : expr | list_expr ; @@ -136,6 +136,7 @@ static void do_set_print(const struct set *set, struct print_fmt_options *opts) { const char *delim = ""; const char *type; + uint32_t flags; type = set->flags & SET_F_MAP ? "map" : "set"; printf("%s%s", opts->tab, type); @@ -167,7 +168,12 @@ static void do_set_print(const struct set *set, struct print_fmt_options *opts) } } - if (set->flags & (SET_F_CONSTANT | SET_F_INTERVAL)) { + flags = set->flags; + /* "timeout" flag is redundant if a default timeout exists */ + if (set->timeout) + flags &= ~SET_F_TIMEOUT; + + if (flags & (SET_F_CONSTANT | SET_F_INTERVAL | SET_F_TIMEOUT)) { printf("%s%sflags ", opts->tab, opts->tab); if (set->flags & SET_F_CONSTANT) { printf("%sconstant", delim); @@ -177,6 +183,21 @@ static void do_set_print(const struct set *set, struct print_fmt_options *opts) printf("%sinterval", delim); delim = ","; } + if (set->flags & SET_F_TIMEOUT) { + printf("%stimeout", delim); + delim = ","; + } + printf("%s", opts->nl); + } + + if (set->timeout) { + printf("%s%stimeout ", opts->tab, opts->tab); + time_print(set->timeout / 1000); + printf("%s", opts->nl); + } + if (set->gc_int) { + printf("%s%sgc-interval ", opts->tab, opts->tab); + time_print(set->gc_int / 1000); printf("%s", opts->nl); } diff --git a/src/scanner.l b/src/scanner.l index 73c4f8b1..985ea2a3 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -117,6 +117,8 @@ quotedstring \"[^"]*\" comment #.*$ slash \/ +timestring ([0-9]+d)?([0-9]+h)?([0-9]+m)?([0-9]+s)? + hex4 ([[:xdigit:]]{1,4}) v680 (({hex4}:){7}{hex4}) v670 ((:)((:{hex4}){7})) @@ -255,6 +257,7 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "inet" { return INET; } "add" { return ADD; } +"update" { return UPDATE; } "create" { return CREATE; } "insert" { return INSERT; } "delete" { return DELETE; } @@ -269,6 +272,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "constant" { return CONSTANT; } "interval" { return INTERVAL; } +"timeout" { return TIMEOUT; } +"gc-interval" { return GC_INTERVAL; } "elements" { return ELEMENTS; } "policy" { return POLICY; } @@ -457,6 +462,11 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) return STRING; } +{timestring} { + yylval->string = xstrdup(yytext); + return STRING; + } + {decstring} { errno = 0; yylval->val = strtoull(yytext, NULL, 0); diff --git a/src/segtree.c b/src/segtree.c index 65221e9d..060951c0 100644 --- a/src/segtree.c +++ b/src/segtree.c @@ -419,6 +419,7 @@ static void set_insert_interval(struct expr *set, struct seg_tree *tree, expr = constant_expr_alloc(&internal_location, tree->keytype, tree->byteorder, tree->keylen, NULL); mpz_set(expr->value, ei->left); + expr = set_elem_expr_alloc(&internal_location, expr); if (ei->expr != NULL && ei->expr->ops->type == EXPR_MAPPING) expr = mapping_expr_alloc(&ei->expr->location, expr, @@ -473,9 +474,9 @@ extern void interval_map_decompose(struct expr *set); static struct expr *expr_value(struct expr *expr) { if (expr->ops->type == EXPR_MAPPING) - return expr->left; + return expr->left->key; else - return expr; + return expr->key; } static int expr_value_cmp(const void *p1, const void *p2) @@ -565,6 +566,7 @@ void interval_map_decompose(struct expr *set) mpz_set(tmp->value, range); tmp = range_expr_alloc(&low->location, expr_value(low), tmp); + tmp = set_elem_expr_alloc(&low->location, tmp); if (low->ops->type == EXPR_MAPPING) tmp = mapping_expr_alloc(&tmp->location, tmp, low->right); @@ -576,6 +578,7 @@ void interval_map_decompose(struct expr *set) prefix_len = expr_value(i)->len - mpz_scan0(range, 0); prefix = prefix_expr_alloc(&low->location, expr_value(low), prefix_len); + prefix = set_elem_expr_alloc(&low->location, prefix); if (low->ops->type == EXPR_MAPPING) prefix = mapping_expr_alloc(&low->location, prefix, low->right); @@ -598,6 +601,7 @@ void interval_map_decompose(struct expr *set) mpz_init_bitmask(i->value, i->len); i = range_expr_alloc(&low->location, expr_value(low), i); + i = set_elem_expr_alloc(&low->location, i); if (low->ops->type == EXPR_MAPPING) i = mapping_expr_alloc(&i->location, i, low->right); diff --git a/src/statement.c b/src/statement.c index d72c6e9b..9ebc5938 100644 --- a/src/statement.c +++ b/src/statement.c @@ -377,3 +377,34 @@ struct stmt *redir_stmt_alloc(const struct location *loc) { return stmt_alloc(loc, &redir_stmt_ops); } + +static const char * const set_stmt_op_names[] = { + [NFT_DYNSET_OP_ADD] = "add", + [NFT_DYNSET_OP_UPDATE] = "update", +}; + +static void set_stmt_print(const struct stmt *stmt) +{ + printf("set %s ", set_stmt_op_names[stmt->set.op]); + expr_print(stmt->set.key); + printf(" "); + expr_print(stmt->set.set); +} + +static void set_stmt_destroy(struct stmt *stmt) +{ + expr_free(stmt->set.key); + expr_free(stmt->set.set); +} + +static const struct stmt_ops set_stmt_ops = { + .type = STMT_SET, + .name = "set", + .print = set_stmt_print, + .destroy = set_stmt_destroy, +}; + +struct stmt *set_stmt_alloc(const struct location *loc) +{ + return stmt_alloc(loc, &set_stmt_ops); +} |