diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/evaluate.c | 27 | ||||
-rw-r--r-- | src/json.c | 4 | ||||
-rw-r--r-- | src/netlink.c | 8 | ||||
-rw-r--r-- | src/parser_bison.y | 36 | ||||
-rw-r--r-- | src/parser_json.c | 2 | ||||
-rw-r--r-- | src/rule.c | 178 | ||||
-rw-r--r-- | src/scanner.l | 2 |
7 files changed, 242 insertions, 15 deletions
diff --git a/src/evaluate.c b/src/evaluate.c index 1fc861f6..3f57ef60 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -17,6 +17,7 @@ #include <linux/netfilter.h> #include <linux/netfilter_arp.h> #include <linux/netfilter/nf_tables.h> +#include <linux/netfilter_ipv4.h> #include <netinet/ip_icmp.h> #include <netinet/icmp6.h> #include <net/ethernet.h> @@ -2962,6 +2963,22 @@ static int set_evaluate(struct eval_ctx *ctx, struct set *set) return 0; } +static bool evaluate_priority(struct prio_spec *prio, int family, int hook) +{ + int priority; + + /* A numeric value has been used to specify priority. */ + if (prio->str == NULL) + return true; + + priority = std_prio_lookup(prio->str, family, hook); + if (priority == NF_IP_PRI_LAST) + return false; + prio->num += priority; + + return true; +} + static uint32_t str2hooknum(uint32_t family, const char *hook); static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft) @@ -2978,6 +2995,10 @@ static int flowtable_evaluate(struct eval_ctx *ctx, struct flowtable *ft) if (ft->hooknum == NF_INET_NUMHOOKS) return chain_error(ctx, ft, "invalid hook %s", ft->hookstr); + if (!evaluate_priority(&ft->priority, NFPROTO_NETDEV, ft->hooknum)) + return chain_error(ctx, ft, "'%s' is invalid priority.", + ft->priority.str); + if (!ft->dev_expr) return chain_error(ctx, ft, "Unbound flowtable not allowed (must specify devices)"); @@ -3130,6 +3151,12 @@ static int chain_evaluate(struct eval_ctx *ctx, struct chain *chain) if (chain->hooknum == NF_INET_NUMHOOKS) return chain_error(ctx, chain, "invalid hook %s", chain->hookstr); + + if (!evaluate_priority(&chain->priority, chain->handle.family, + chain->hooknum)) + return chain_error(ctx, chain, + "'%s' is invalid priority in this context.", + chain->priority.str); } list_for_each_entry(rule, &chain->rules, list) { @@ -214,7 +214,7 @@ static json_t *chain_print_json(const struct output_ctx *octx, "type", chain->type, "hook", hooknum2str(chain->handle.family, chain->hooknum), - "prio", chain->priority, + "prio", chain->priority.num, "policy", chain_policy2str(chain->policy)); if (chain->dev) json_object_set_new(tmp, "dev", json_string(chain->dev)); @@ -315,7 +315,7 @@ static json_t *flowtable_print_json(const struct flowtable *ftable) "name", ftable->handle.flowtable, "table", ftable->handle.table.name, "hook", hooknum2str(NFPROTO_NETDEV, ftable->hooknum), - "prio", ftable->priority); + "prio", ftable->priority.num); for (i = 0; i < ftable->dev_array_len; i++) { const char *dev = ftable->dev_array[i]; diff --git a/src/netlink.c b/src/netlink.c index 394af2f0..bd472799 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -597,7 +597,7 @@ int netlink_add_chain_batch(struct netlink_ctx *ctx, const struct cmd *cmd, nftnl_chain_set_u32(nlc, NFTNL_CHAIN_HOOKNUM, chain->hooknum); nftnl_chain_set_s32(nlc, NFTNL_CHAIN_PRIO, - chain->priority); + chain->priority.num); nftnl_chain_set_str(nlc, NFTNL_CHAIN_TYPE, chain->type); } @@ -666,7 +666,7 @@ struct chain *netlink_delinearize_chain(struct netlink_ctx *ctx, nftnl_chain_get_u32(nlc, NFTNL_CHAIN_HOOKNUM); chain->hookstr = hooknum2str(chain->handle.family, chain->hooknum); - chain->priority = + chain->priority.num = nftnl_chain_get_s32(nlc, NFTNL_CHAIN_PRIO); chain->type = xstrdup(nftnl_chain_get_str(nlc, NFTNL_CHAIN_TYPE)); @@ -1495,7 +1495,7 @@ int netlink_add_flowtable(struct netlink_ctx *ctx, const struct cmd *cmd, flo = alloc_nftnl_flowtable(&cmd->handle, ft); nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_HOOKNUM, ft->hooknum); - nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, ft->priority); + nftnl_flowtable_set_u32(flo, NFTNL_FLOWTABLE_PRIO, ft->priority.num); list_for_each_entry(expr, &ft->dev_expr->expressions, list) dev_array[i++] = expr->identifier; @@ -1598,7 +1598,7 @@ netlink_delinearize_flowtable(struct netlink_ctx *ctx, flowtable->dev_array_len = len; - flowtable->priority = + flowtable->priority.num = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_PRIO); flowtable->hooknum = nftnl_flowtable_get_u32(nlo, NFTNL_FLOWTABLE_HOOKNUM); diff --git a/src/parser_bison.y b/src/parser_bison.y index f3c88299..bc6f7277 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -153,6 +153,7 @@ int nft_lex(void *, void *, void *); const struct datatype *datatype; struct handle_spec handle_spec; struct position_spec position_spec; + struct prio_spec prio_spec; const struct exthdr_desc *exthdr_desc; } @@ -182,6 +183,8 @@ int nft_lex(void *, void *, void *); %token AT "@" %token VMAP "vmap" +%token PLUS "+" + %token INCLUDE "include" %token DEFINE "define" %token REDEFINE "redefine" @@ -526,6 +529,7 @@ int nft_lex(void *, void *, void *); %type <handle> set_spec setid_spec set_identifier flowtable_identifier obj_spec objid_spec obj_identifier %destructor { handle_free(&$$); } set_spec setid_spec set_identifier obj_spec objid_spec obj_identifier %type <val> family_spec family_spec_explicit chain_policy prio_spec +%type <prio_spec> extended_prio_spec %type <string> dev_spec quota_unit %destructor { xfree($$); } dev_spec quota_unit @@ -1643,7 +1647,7 @@ flowtable_block_alloc : /* empty */ flowtable_block : /* empty */ { $$ = $<flowtable>-1; } | flowtable_block common_block | flowtable_block stmt_separator - | flowtable_block HOOK STRING PRIORITY prio_spec stmt_separator + | flowtable_block HOOK STRING PRIORITY extended_prio_spec stmt_separator { $$->hookstr = chain_hookname_lookup($3); if ($$->hookstr == NULL) { @@ -1776,7 +1780,7 @@ type_identifier : STRING { $$ = $1; } | CLASSID { $$ = xstrdup("classid"); } ; -hook_spec : TYPE STRING HOOK STRING dev_spec PRIORITY prio_spec +hook_spec : TYPE STRING HOOK STRING dev_spec PRIORITY extended_prio_spec { const char *chain_type = chain_type_name_lookup($2); @@ -1804,6 +1808,34 @@ hook_spec : TYPE STRING HOOK STRING dev_spec PRIORITY prio_spec } ; +extended_prio_spec : prio_spec + { + struct prio_spec spec = {0}; + spec.num = $1; + $$ = spec; + } + | STRING + { + struct prio_spec spec = {0}; + spec.str = $1; + $$ = spec; + } + | STRING PLUS NUM + { + struct prio_spec spec = {0}; + spec.num = $3; + spec.str = $1; + $$ = spec; + } + | STRING DASH NUM + { + struct prio_spec spec = {0}; + spec.num = -$3; + spec.str = $1; + $$ = spec; + } + ; + prio_spec : NUM { $$ = $1; } | DASH NUM { $$ = -$2; } ; diff --git a/src/parser_json.c b/src/parser_json.c index 80364d97..b51f95e1 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -2595,7 +2595,7 @@ static struct cmd *json_parse_cmd_add_flowtable(struct json_ctx *ctx, flowtable = flowtable_alloc(int_loc); flowtable->hookstr = hookstr; - flowtable->priority = prio; + flowtable->priority.num = prio; flowtable->dev_expr = json_parse_flowtable_devs(ctx, devs); if (!flowtable->dev_expr) { @@ -28,6 +28,8 @@ #include <netinet/ip.h> #include <linux/netfilter.h> #include <linux/netfilter_arp.h> +#include <linux/netfilter_ipv4.h> +#include <linux/netfilter_bridge.h> void handle_free(struct handle *h) { @@ -675,6 +677,7 @@ void chain_free(struct chain *chain) xfree(chain->type); if (chain->dev != NULL) xfree(chain->dev); + xfree(chain->priority.str); xfree(chain); } @@ -773,9 +776,159 @@ const char *chain_policy2str(uint32_t policy) return "unknown"; } +struct prio_tag { + int val; + const char *str; +}; + +const static struct prio_tag std_prios[] = { + { NF_IP_PRI_RAW, "raw" }, + { NF_IP_PRI_MANGLE, "mangle" }, + { NF_IP_PRI_NAT_DST, "dstnat" }, + { NF_IP_PRI_FILTER, "filter" }, + { NF_IP_PRI_SECURITY, "security" }, + { NF_IP_PRI_NAT_SRC, "srcnat" }, +}; + +const static struct prio_tag bridge_std_prios[] = { + { NF_BR_PRI_NAT_DST_BRIDGED, "dstnat" }, + { NF_BR_PRI_FILTER_BRIDGED, "filter" }, + { NF_BR_PRI_NAT_DST_OTHER, "out" }, + { NF_BR_PRI_NAT_SRC, "srcnat" }, +}; + +static bool std_prio_family_hook_compat(int prio, int family, int hook) +{ + /* bridge family has different values */ + if (family == NFPROTO_BRIDGE) { + switch (prio) { + case NF_BR_PRI_NAT_DST_BRIDGED: + if (hook == NF_BR_PRE_ROUTING) + return true; + break; + case NF_BR_PRI_FILTER_BRIDGED: + return true; + case NF_BR_PRI_NAT_DST_OTHER: + if (hook == NF_BR_LOCAL_OUT) + return true; + break; + case NF_BR_PRI_NAT_SRC: + if (hook == NF_BR_POST_ROUTING) + return true; + } + return false; + } + switch(prio) { + case NF_IP_PRI_FILTER: + switch (family) { + case NFPROTO_INET: + case NFPROTO_IPV4: + case NFPROTO_IPV6: + case NFPROTO_ARP: + case NFPROTO_NETDEV: + return true; + } + break; + case NF_IP_PRI_RAW: + case NF_IP_PRI_MANGLE: + case NF_IP_PRI_SECURITY: + switch (family) { + case NFPROTO_INET: + case NFPROTO_IPV4: + case NFPROTO_IPV6: + return true; + } + break; + case NF_IP_PRI_NAT_DST: + switch(family) { + case NFPROTO_INET: + case NFPROTO_IPV4: + case NFPROTO_IPV6: + if (hook == NF_INET_PRE_ROUTING) + return true; + } + break; + case NF_IP_PRI_NAT_SRC: + switch(family) { + case NFPROTO_INET: + case NFPROTO_IPV4: + case NFPROTO_IPV6: + if (hook == NF_INET_POST_ROUTING) + return true; + } + } + return false; +} + +int std_prio_lookup(const char *std_prio_name, int family, int hook) +{ + const struct prio_tag *prio_arr; + size_t i, arr_size; + + if (family == NFPROTO_BRIDGE) { + prio_arr = bridge_std_prios; + arr_size = array_size(bridge_std_prios); + } else { + prio_arr = std_prios; + arr_size = array_size(std_prios); + } + + for (i = 0; i < arr_size; ++i) { + if (strcmp(prio_arr[i].str, std_prio_name) == 0 && + std_prio_family_hook_compat(prio_arr[i].val, family, hook)) + return prio_arr[i].val; + } + return NF_IP_PRI_LAST; +} + +static const char *prio2str(char *buf, size_t bufsize, int family, int hook, + int prio, int numeric) +{ + const struct prio_tag *prio_arr; + const char *std_prio_str; + const int reach = 10; + int std_prio, offset; + size_t i, arr_size; + + if (family == NFPROTO_BRIDGE) { + prio_arr = bridge_std_prios; + arr_size = array_size(bridge_std_prios); + } else { + prio_arr = std_prios; + arr_size = array_size(std_prios); + } + + if (numeric != NFT_NUMERIC_ALL) { + for (i = 0; i < arr_size; ++i) { + std_prio = prio_arr[i].val; + std_prio_str = prio_arr[i].str; + if (abs(prio - std_prio) <= reach) { + if (!std_prio_family_hook_compat(std_prio, + family, hook)) + break; + offset = prio - std_prio; + strncpy(buf, std_prio_str, bufsize); + if (offset > 0) + snprintf(buf + strlen(buf), + bufsize - strlen(buf), " + %d", + offset); + else if (offset < 0) + snprintf(buf + strlen(buf), + bufsize - strlen(buf), " - %d", + -offset); + return buf; + } + } + } + snprintf(buf, bufsize, "%d", prio); + return buf; +} + static void chain_print_declaration(const struct chain *chain, struct output_ctx *octx) { + char priobuf[STD_PRIO_BUFSIZE]; + nft_print(octx, "\tchain %s {", chain->handle.chain.name); if (octx->handle > 0) nft_print(octx, " # handle %" PRIu64, chain->handle.handle.id); @@ -785,8 +938,11 @@ static void chain_print_declaration(const struct chain *chain, hooknum2str(chain->handle.family, chain->hooknum)); if (chain->dev != NULL) nft_print(octx, " device %s", chain->dev); - nft_print(octx, " priority %d; policy %s;\n", - chain->priority, chain_policy2str(chain->policy)); + nft_print(octx, " priority %s; policy %s;\n", + prio2str(priobuf, sizeof(priobuf), + chain->handle.family, chain->hooknum, + chain->priority.num, octx->numeric), + chain_policy2str(chain->policy)); } } @@ -806,13 +962,18 @@ static void chain_print(const struct chain *chain, struct output_ctx *octx) void chain_print_plain(const struct chain *chain, struct output_ctx *octx) { + char priobuf[STD_PRIO_BUFSIZE]; + nft_print(octx, "chain %s %s %s", family2str(chain->handle.family), chain->handle.table.name, chain->handle.chain.name); if (chain->flags & CHAIN_F_BASECHAIN) { - nft_print(octx, " { type %s hook %s priority %d; policy %s; }", + nft_print(octx, " { type %s hook %s priority %s; policy %s; }", chain->type, chain->hookstr, - chain->priority, chain_policy2str(chain->policy)); + prio2str(priobuf, sizeof(priobuf), + chain->handle.family, chain->hooknum, + chain->priority.num, octx->numeric), + chain_policy2str(chain->policy)); } if (octx->handle > 0) nft_print(octx, " # handle %" PRIu64, chain->handle.handle.id); @@ -1638,6 +1799,7 @@ void flowtable_free(struct flowtable *flowtable) if (--flowtable->refcnt > 0) return; handle_free(&flowtable->handle); + xfree(flowtable->priority.str); xfree(flowtable); } @@ -1650,6 +1812,7 @@ static void flowtable_print_declaration(const struct flowtable *flowtable, struct print_fmt_options *opts, struct output_ctx *octx) { + char priobuf[STD_PRIO_BUFSIZE]; int i; nft_print(octx, "%sflowtable", opts->tab); @@ -1662,10 +1825,13 @@ static void flowtable_print_declaration(const struct flowtable *flowtable, nft_print(octx, " %s {%s", flowtable->handle.flowtable, opts->nl); - nft_print(octx, "%s%shook %s priority %d%s", + nft_print(octx, "%s%shook %s priority %s%s", opts->tab, opts->tab, hooknum2str(NFPROTO_NETDEV, flowtable->hooknum), - flowtable->priority, opts->stmt_separator); + prio2str(priobuf, sizeof(priobuf), NFPROTO_NETDEV, + flowtable->hooknum, flowtable->priority.num, + octx->numeric), + opts->stmt_separator); nft_print(octx, "%s%sdevices = { ", opts->tab, opts->tab); for (i = 0; i < flowtable->dev_array_len; i++) { diff --git a/src/scanner.l b/src/scanner.l index bce69152..2f45e05b 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -232,6 +232,8 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "=" { return '='; } "vmap" { return VMAP; } +"+" { return PLUS; } + "include" { return INCLUDE; } "define" { return DEFINE; } "redefine" { return REDEFINE; } |