diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2016-09-23 09:23:33 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2016-09-25 17:16:42 -0400 |
commit | 0f3cd9b3697708c86a825ae3cedabf7be6fd3e72 (patch) | |
tree | 14b59bebf1a6dc16a961864a93febfbacc4d3715 /net | |
parent | 7a682575ad4829b4de3e672a6ad5f73a05826b82 (diff) |
netfilter: nf_tables: add range expression
Inverse ranges != [a,b] are not currently possible because rules are
composites of && operations, and we need to express this:
data < a || data > b
This patch adds a new range expression. Positive ranges can be already
through two cmp expressions:
cmp(sreg, data, >=)
cmp(sreg, data, <=)
This new range expression provides an alternative way to express this.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'net')
-rw-r--r-- | net/netfilter/Makefile | 3 | ||||
-rw-r--r-- | net/netfilter/nf_tables_core.c | 7 | ||||
-rw-r--r-- | net/netfilter/nft_range.c | 138 |
3 files changed, 146 insertions, 2 deletions
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 0c8581100ac6..c23c3c84416f 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -71,8 +71,9 @@ obj-$(CONFIG_NF_DUP_NETDEV) += nf_dup_netdev.o | |||
71 | 71 | ||
72 | # nf_tables | 72 | # nf_tables |
73 | nf_tables-objs += nf_tables_core.o nf_tables_api.o nf_tables_trace.o | 73 | nf_tables-objs += nf_tables_core.o nf_tables_api.o nf_tables_trace.o |
74 | nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o nft_dynset.o | 74 | nf_tables-objs += nft_immediate.o nft_cmp.o nft_range.o |
75 | nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o | 75 | nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o |
76 | nf_tables-objs += nft_lookup.o nft_dynset.o | ||
76 | 77 | ||
77 | obj-$(CONFIG_NF_TABLES) += nf_tables.o | 78 | obj-$(CONFIG_NF_TABLES) += nf_tables.o |
78 | obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o | 79 | obj-$(CONFIG_NF_TABLES_INET) += nf_tables_inet.o |
diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 67259cefef06..7c94ce0080d5 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c | |||
@@ -263,8 +263,13 @@ int __init nf_tables_core_module_init(void) | |||
263 | if (err < 0) | 263 | if (err < 0) |
264 | goto err7; | 264 | goto err7; |
265 | 265 | ||
266 | return 0; | 266 | err = nft_range_module_init(); |
267 | if (err < 0) | ||
268 | goto err8; | ||
267 | 269 | ||
270 | return 0; | ||
271 | err8: | ||
272 | nft_dynset_module_exit(); | ||
268 | err7: | 273 | err7: |
269 | nft_payload_module_exit(); | 274 | nft_payload_module_exit(); |
270 | err6: | 275 | err6: |
diff --git a/net/netfilter/nft_range.c b/net/netfilter/nft_range.c new file mode 100644 index 000000000000..c6d5358482d1 --- /dev/null +++ b/net/netfilter/nft_range.c | |||
@@ -0,0 +1,138 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org> | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/module.h> | ||
12 | #include <linux/netlink.h> | ||
13 | #include <linux/netfilter.h> | ||
14 | #include <linux/netfilter/nf_tables.h> | ||
15 | #include <net/netfilter/nf_tables_core.h> | ||
16 | #include <net/netfilter/nf_tables.h> | ||
17 | |||
18 | struct nft_range_expr { | ||
19 | struct nft_data data_from; | ||
20 | struct nft_data data_to; | ||
21 | enum nft_registers sreg:8; | ||
22 | u8 len; | ||
23 | enum nft_range_ops op:8; | ||
24 | }; | ||
25 | |||
26 | static void nft_range_eval(const struct nft_expr *expr, | ||
27 | struct nft_regs *regs, | ||
28 | const struct nft_pktinfo *pkt) | ||
29 | { | ||
30 | const struct nft_range_expr *priv = nft_expr_priv(expr); | ||
31 | bool mismatch; | ||
32 | int d1, d2; | ||
33 | |||
34 | d1 = memcmp(®s->data[priv->sreg], &priv->data_from, priv->len); | ||
35 | d2 = memcmp(®s->data[priv->sreg], &priv->data_to, priv->len); | ||
36 | switch (priv->op) { | ||
37 | case NFT_RANGE_EQ: | ||
38 | mismatch = (d1 < 0 || d2 > 0); | ||
39 | break; | ||
40 | case NFT_RANGE_NEQ: | ||
41 | mismatch = (d1 >= 0 && d2 <= 0); | ||
42 | break; | ||
43 | } | ||
44 | |||
45 | if (mismatch) | ||
46 | regs->verdict.code = NFT_BREAK; | ||
47 | } | ||
48 | |||
49 | static const struct nla_policy nft_range_policy[NFTA_RANGE_MAX + 1] = { | ||
50 | [NFTA_RANGE_SREG] = { .type = NLA_U32 }, | ||
51 | [NFTA_RANGE_OP] = { .type = NLA_U32 }, | ||
52 | [NFTA_RANGE_FROM_DATA] = { .type = NLA_NESTED }, | ||
53 | [NFTA_RANGE_TO_DATA] = { .type = NLA_NESTED }, | ||
54 | }; | ||
55 | |||
56 | static int nft_range_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | ||
57 | const struct nlattr * const tb[]) | ||
58 | { | ||
59 | struct nft_range_expr *priv = nft_expr_priv(expr); | ||
60 | struct nft_data_desc desc_from, desc_to; | ||
61 | int err; | ||
62 | |||
63 | err = nft_data_init(NULL, &priv->data_from, sizeof(priv->data_from), | ||
64 | &desc_from, tb[NFTA_RANGE_FROM_DATA]); | ||
65 | if (err < 0) | ||
66 | return err; | ||
67 | |||
68 | err = nft_data_init(NULL, &priv->data_to, sizeof(priv->data_to), | ||
69 | &desc_to, tb[NFTA_RANGE_TO_DATA]); | ||
70 | if (err < 0) | ||
71 | goto err1; | ||
72 | |||
73 | if (desc_from.len != desc_to.len) { | ||
74 | err = -EINVAL; | ||
75 | goto err2; | ||
76 | } | ||
77 | |||
78 | priv->sreg = nft_parse_register(tb[NFTA_RANGE_SREG]); | ||
79 | err = nft_validate_register_load(priv->sreg, desc_from.len); | ||
80 | if (err < 0) | ||
81 | goto err2; | ||
82 | |||
83 | priv->op = ntohl(nla_get_be32(tb[NFTA_RANGE_OP])); | ||
84 | priv->len = desc_from.len; | ||
85 | return 0; | ||
86 | err2: | ||
87 | nft_data_uninit(&priv->data_to, desc_to.type); | ||
88 | err1: | ||
89 | nft_data_uninit(&priv->data_from, desc_from.type); | ||
90 | return err; | ||
91 | } | ||
92 | |||
93 | static int nft_range_dump(struct sk_buff *skb, const struct nft_expr *expr) | ||
94 | { | ||
95 | const struct nft_range_expr *priv = nft_expr_priv(expr); | ||
96 | |||
97 | if (nft_dump_register(skb, NFTA_RANGE_SREG, priv->sreg)) | ||
98 | goto nla_put_failure; | ||
99 | if (nla_put_be32(skb, NFTA_RANGE_OP, htonl(priv->op))) | ||
100 | goto nla_put_failure; | ||
101 | |||
102 | if (nft_data_dump(skb, NFTA_RANGE_FROM_DATA, &priv->data_from, | ||
103 | NFT_DATA_VALUE, priv->len) < 0 || | ||
104 | nft_data_dump(skb, NFTA_RANGE_TO_DATA, &priv->data_to, | ||
105 | NFT_DATA_VALUE, priv->len) < 0) | ||
106 | goto nla_put_failure; | ||
107 | return 0; | ||
108 | |||
109 | nla_put_failure: | ||
110 | return -1; | ||
111 | } | ||
112 | |||
113 | static struct nft_expr_type nft_range_type; | ||
114 | static const struct nft_expr_ops nft_range_ops = { | ||
115 | .type = &nft_range_type, | ||
116 | .size = NFT_EXPR_SIZE(sizeof(struct nft_range_expr)), | ||
117 | .eval = nft_range_eval, | ||
118 | .init = nft_range_init, | ||
119 | .dump = nft_range_dump, | ||
120 | }; | ||
121 | |||
122 | static struct nft_expr_type nft_range_type __read_mostly = { | ||
123 | .name = "range", | ||
124 | .ops = &nft_range_ops, | ||
125 | .policy = nft_range_policy, | ||
126 | .maxattr = NFTA_RANGE_MAX, | ||
127 | .owner = THIS_MODULE, | ||
128 | }; | ||
129 | |||
130 | int __init nft_range_module_init(void) | ||
131 | { | ||
132 | return nft_register_expr(&nft_range_type); | ||
133 | } | ||
134 | |||
135 | void nft_range_module_exit(void) | ||
136 | { | ||
137 | nft_unregister_expr(&nft_range_type); | ||
138 | } | ||