summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/evaluate.c27
-rw-r--r--src/json.c4
-rw-r--r--src/netlink.c8
-rw-r--r--src/parser_bison.y36
-rw-r--r--src/parser_json.c2
-rw-r--r--src/rule.c178
-rw-r--r--src/scanner.l2
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) {
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 <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) {
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 <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; }