From c8a0e8c90e2d1188e6fcdd8951b295722e56d542 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Eckl?= Date: Fri, 3 Aug 2018 10:55:33 +0200 Subject: src: Set/print standard chain prios with textual names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the possibility to use textual names to set the chain priority to standard values so that numeric values do not need to be learnt any more for basic usage. Basic arithmetic can also be done with them to ease the addition of relatively higher/lower priority chains. Addition and substraction is possible. Values are also printed with their friendly name within the range of +- 10. Also numeric printing is supported in case of -nnn option (numeric == NFT_NUMERIC_ALL) The supported name-value pairs and where they are valid is based on how x_tables use these values when registering their base chains. (See iptables/nft.c in the iptables repository). Also see the compatibility matrices extracted from the man page: Standard priority names, family and hook compatibility matrix ┌─────────┬───────┬────────────────┬─────────────┐ │Name │ Value │ Families │ Hooks │ ├─────────┼───────┼────────────────┼─────────────┤ │ │ │ │ │ │raw │ -300 │ ip, ip6, inet │ all │ ├─────────┼───────┼────────────────┼─────────────┤ │ │ │ │ │ │mangle │ -150 │ ip, ip6, inet │ all │ ├─────────┼───────┼────────────────┼─────────────┤ │ │ │ │ │ │dstnat │ -100 │ ip, ip6, inet │ prerouting │ ├─────────┼───────┼────────────────┼─────────────┤ │ │ │ │ │ │filter │ 0 │ ip, ip6, inet, │ all │ │ │ │ arp, netdev │ │ ├─────────┼───────┼────────────────┼─────────────┤ │ │ │ │ │ │security │ 50 │ ip, ip6, inet │ all │ ├─────────┼───────┼────────────────┼─────────────┤ │ │ │ │ │ │srcnat │ 100 │ ip, ip6, inet │ postrouting │ └─────────┴───────┴────────────────┴─────────────┘ Standard priority names and hook compatibility for the bridge family ┌───────┬───────┬─────────────┐ │ │ │ │ │Name │ Value │ Hooks │ ├───────┼───────┼─────────────┤ │ │ │ │ │dstnat │ -300 │ prerouting │ ├───────┼───────┼─────────────┤ │ │ │ │ │filter │ -200 │ all │ ├───────┼───────┼─────────────┤ │ │ │ │ │out │ 100 │ output │ ├───────┼───────┼─────────────┤ │ │ │ │ │srcnat │ 300 │ postrouting │ └───────┴───────┴─────────────┘ This can be also applied for flowtables wher it works as a netdev family chain. Example: nft> add table ip x nft> add chain ip x y { type filter hook prerouting priority raw; } nft> add chain ip x z { type filter hook prerouting priority mangle + 1; } nft> add chain ip x w { type filter hook prerouting priority dstnat - 5; } nft> add chain ip x r { type filter hook prerouting priority filter + 10; } nft> add chain ip x t { type filter hook prerouting priority security; } nft> add chain ip x q { type filter hook postrouting priority srcnat + 11; } nft> add chain ip x h { type filter hook prerouting priority 15; } nft> nft> add flowtable ip x y { hook ingress priority filter + 5 ; devices = {enp0s31f6}; } nft> nft> add table arp x nft> add chain arp x y { type filter hook input priority filter + 5; } nft> nft> add table bridge x nft> add chain bridge x y { type filter hook input priority filter + 9; } nft> add chain bridge x z { type filter hook prerouting priority dstnat; } nft> add chain bridge x q { type filter hook postrouting priority srcnat; } nft> add chain bridge x k { type filter hook output priority out; } nft> nft> list ruleset table ip x { flowtable y { hook ingress priority filter + 5 devices = { enp0s31f6 } } chain y { type filter hook prerouting priority raw; policy accept; } chain z { type filter hook prerouting priority mangle + 1; policy accept; } chain w { type filter hook prerouting priority dstnat - 5; policy accept; } chain r { type filter hook prerouting priority filter + 10; policy accept; } chain t { type filter hook prerouting priority security; policy accept; } chain q { type filter hook postrouting priority 111; policy accept; } chain h { type filter hook prerouting priority 15; policy accept; } } table arp x { chain y { type filter hook input priority filter + 5; policy accept; } } table bridge x { chain y { type filter hook input priority filter + 9; policy accept; } chain z { type filter hook prerouting priority dstnat; policy accept; } chain q { type filter hook postrouting priority srcnat; policy accept; } chain k { type filter hook output priority out; policy accept; } } nft> # Everything should fail after this nft> add chain ip x h { type filter hook prerouting priority first; } Error: 'first' is invalid priority in this context. add chain ip x h { type filter hook prerouting priority first; } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ nft> add chain ip x q { type filter hook prerouting priority srcnat + 11; } Error: 'srcnat' is invalid priority in this context. add chain ip x q { type filter hook prerouting priority srcnat + 11; } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ nft> add chain arp x y { type filter hook input priority raw; } Error: 'raw' is invalid priority in this context. add chain arp x y { type filter hook input priority raw; } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ nft> add flowtable ip x y { hook ingress priority magle; devices = {enp0s31f6}; } Error: 'magle' is invalid priority. add flowtable ip x y { hook ingress priority magle; devices = {enp0s31f6}; } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ nft> add chain bridge x r { type filter hook postrouting priority dstnat; } Error: 'dstnat' is invalid priority in this context. add chain bridge x r { type filter hook postrouting priority dstnat; } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ nft> add chain bridge x t { type filter hook prerouting priority srcnat; } Error: 'srcnat' is invalid priority in this context. add chain bridge x t { type filter hook prerouting priority srcnat; } ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Signed-off-by: Máté Eckl Signed-off-by: Pablo Neira Ayuso --- src/evaluate.c | 27 ++++++++ src/json.c | 4 +- src/netlink.c | 8 +-- src/parser_bison.y | 36 ++++++++++- src/parser_json.c | 2 +- src/rule.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/scanner.l | 2 + 7 files changed, 242 insertions(+), 15 deletions(-) (limited to 'src') 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 #include #include +#include #include #include #include @@ -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) { diff --git a/src/json.c b/src/json.c index c1cd0fbf..8a3e15e6 100644 --- a/src/json.c +++ b/src/json.c @@ -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 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 family_spec family_spec_explicit chain_policy prio_spec +%type extended_prio_spec %type dev_spec quota_unit %destructor { xfree($$); } dev_spec quota_unit @@ -1643,7 +1647,7 @@ flowtable_block_alloc : /* empty */ flowtable_block : /* empty */ { $$ = $-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) { diff --git a/src/rule.c b/src/rule.c index 7a7ac73b..d11b1d29 100644 --- a/src/rule.c +++ b/src/rule.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include 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; } -- cgit v1.2.3