diff options
| author | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-10-13 18:06:06 -0400 |
|---|---|---|
| committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2013-10-14 12:00:04 -0400 |
| commit | 0ca743a5599199152a31a7146b83213c786c2eb2 (patch) | |
| tree | fd5cd87e1e1cf3f43d5868304c7c45f9bf11cef5 | |
| parent | 9370761c56b66aa5c65e069a7b010111a025018d (diff) | |
netfilter: nf_tables: add compatibility layer for x_tables
This patch adds the x_tables compatibility layer. This allows you
to use existing x_tables matches and targets from nf_tables.
This compatibility later allows us to use existing matches/targets
for features that are still missing in nf_tables. We can progressively
replace them with native nf_tables extensions. It also provides the
userspace compatibility software that allows you to express the
rule-set using the iptables syntax but using the nf_tables kernel
components.
In order to get this compatibility layer working, I've done the
following things:
* add NFNL_SUBSYS_NFT_COMPAT: this new nfnetlink subsystem is used
to query the x_tables match/target revision, so we don't need to
use the native x_table getsockopt interface.
* emulate xt structures: this required extending the struct nft_pktinfo
to include the fragment offset, which is already obtained from
ip[6]_tables and that is used by some matches/targets.
* add support for default policy to base chains, required to emulate
x_tables.
* add NFTA_CHAIN_USE attribute to obtain the number of references to
chains, required by x_tables emulation.
* add chain packet/byte counters using per-cpu.
* support 32-64 bits compat.
For historical reasons, this patch includes the following patches
that were posted in the netfilter-devel mailing list.
From Pablo Neira Ayuso:
* nf_tables: add default policy to base chains
* netfilter: nf_tables: add NFTA_CHAIN_USE attribute
* nf_tables: nft_compat: private data of target and matches in contiguous area
* nf_tables: validate hooks for compat match/target
* nf_tables: nft_compat: release cached matches/targets
* nf_tables: x_tables support as a compile time option
* nf_tables: fix alias for xtables over nftables module
* nf_tables: add packet and byte counters per chain
* nf_tables: fix per-chain counter stats if no counters are passed
* nf_tables: don't bump chain stats
* nf_tables: add protocol and flags for xtables over nf_tables
* nf_tables: add ip[6]t_entry emulation
* nf_tables: move specific layer 3 compat code to nf_tables_ipv[4|6]
* nf_tables: support 32bits-64bits x_tables compat
* nf_tables: fix compilation if CONFIG_COMPAT is disabled
From Patrick McHardy:
* nf_tables: move policy to struct nft_base_chain
* nf_tables: send notifications for base chain policy changes
From Alexander Primak:
* nf_tables: remove the duplicate NF_INET_LOCAL_OUT
From Nicolas Dichtel:
* nf_tables: fix compilation when nf-netlink is a module
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
| -rw-r--r-- | include/net/netfilter/nf_tables.h | 44 | ||||
| -rw-r--r-- | include/net/netfilter/nf_tables_ipv4.h | 23 | ||||
| -rw-r--r-- | include/net/netfilter/nf_tables_ipv6.h | 30 | ||||
| -rw-r--r-- | include/uapi/linux/netfilter/Kbuild | 1 | ||||
| -rw-r--r-- | include/uapi/linux/netfilter/nf_tables.h | 32 | ||||
| -rw-r--r-- | include/uapi/linux/netfilter/nf_tables_compat.h | 38 | ||||
| -rw-r--r-- | include/uapi/linux/netfilter/nfnetlink.h | 3 | ||||
| -rw-r--r-- | net/ipv4/netfilter/nf_tables_ipv4.c | 32 | ||||
| -rw-r--r-- | net/ipv4/netfilter/nft_chain_nat_ipv4.c | 6 | ||||
| -rw-r--r-- | net/ipv4/netfilter/nft_chain_route_ipv4.c | 6 | ||||
| -rw-r--r-- | net/ipv6/netfilter/nf_tables_ipv6.c | 33 | ||||
| -rw-r--r-- | net/ipv6/netfilter/nft_chain_route_ipv6.c | 8 | ||||
| -rw-r--r-- | net/netfilter/Kconfig | 9 | ||||
| -rw-r--r-- | net/netfilter/Makefile | 1 | ||||
| -rw-r--r-- | net/netfilter/nf_tables_api.c | 220 | ||||
| -rw-r--r-- | net/netfilter/nf_tables_core.c | 46 | ||||
| -rw-r--r-- | net/netfilter/nft_cmp.c | 3 | ||||
| -rw-r--r-- | net/netfilter/nft_compat.c | 768 | ||||
| -rw-r--r-- | net/netfilter/nft_immediate.c | 12 | ||||
| -rw-r--r-- | net/netfilter/nft_payload.c | 4 |
20 files changed, 1241 insertions, 78 deletions
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 8403f7f52e81..a68f45f0fe2e 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
| 5 | #include <linux/netfilter.h> | 5 | #include <linux/netfilter.h> |
| 6 | #include <linux/netfilter/x_tables.h> | ||
| 6 | #include <linux/netfilter/nf_tables.h> | 7 | #include <linux/netfilter/nf_tables.h> |
| 7 | #include <net/netlink.h> | 8 | #include <net/netlink.h> |
| 8 | 9 | ||
| @@ -15,8 +16,23 @@ struct nft_pktinfo { | |||
| 15 | u8 hooknum; | 16 | u8 hooknum; |
| 16 | u8 nhoff; | 17 | u8 nhoff; |
| 17 | u8 thoff; | 18 | u8 thoff; |
| 19 | /* for x_tables compatibility */ | ||
| 20 | struct xt_action_param xt; | ||
| 18 | }; | 21 | }; |
| 19 | 22 | ||
| 23 | static inline void nft_set_pktinfo(struct nft_pktinfo *pkt, | ||
| 24 | const struct nf_hook_ops *ops, | ||
| 25 | struct sk_buff *skb, | ||
| 26 | const struct net_device *in, | ||
| 27 | const struct net_device *out) | ||
| 28 | { | ||
| 29 | pkt->skb = skb; | ||
| 30 | pkt->in = pkt->xt.in = in; | ||
| 31 | pkt->out = pkt->xt.out = out; | ||
| 32 | pkt->hooknum = pkt->xt.hooknum = ops->hooknum; | ||
| 33 | pkt->xt.family = ops->pf; | ||
| 34 | } | ||
| 35 | |||
| 20 | struct nft_data { | 36 | struct nft_data { |
| 21 | union { | 37 | union { |
| 22 | u32 data[4]; | 38 | u32 data[4]; |
| @@ -57,6 +73,7 @@ static inline void nft_data_debug(const struct nft_data *data) | |||
| 57 | * @afi: address family info | 73 | * @afi: address family info |
| 58 | * @table: the table the chain is contained in | 74 | * @table: the table the chain is contained in |
| 59 | * @chain: the chain the rule is contained in | 75 | * @chain: the chain the rule is contained in |
| 76 | * @nla: netlink attributes | ||
| 60 | */ | 77 | */ |
| 61 | struct nft_ctx { | 78 | struct nft_ctx { |
| 62 | const struct sk_buff *skb; | 79 | const struct sk_buff *skb; |
| @@ -64,6 +81,7 @@ struct nft_ctx { | |||
| 64 | const struct nft_af_info *afi; | 81 | const struct nft_af_info *afi; |
| 65 | const struct nft_table *table; | 82 | const struct nft_table *table; |
| 66 | const struct nft_chain *chain; | 83 | const struct nft_chain *chain; |
| 84 | const struct nlattr * const *nla; | ||
| 67 | }; | 85 | }; |
| 68 | 86 | ||
| 69 | struct nft_data_desc { | 87 | struct nft_data_desc { |
| @@ -235,7 +253,8 @@ extern void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, | |||
| 235 | * @maxattr: highest netlink attribute number | 253 | * @maxattr: highest netlink attribute number |
| 236 | */ | 254 | */ |
| 237 | struct nft_expr_type { | 255 | struct nft_expr_type { |
| 238 | const struct nft_expr_ops *(*select_ops)(const struct nlattr * const tb[]); | 256 | const struct nft_expr_ops *(*select_ops)(const struct nft_ctx *, |
| 257 | const struct nlattr * const tb[]); | ||
| 239 | const struct nft_expr_ops *ops; | 258 | const struct nft_expr_ops *ops; |
| 240 | struct list_head list; | 259 | struct list_head list; |
| 241 | const char *name; | 260 | const char *name; |
| @@ -253,6 +272,8 @@ struct nft_expr_type { | |||
| 253 | * @destroy: destruction function | 272 | * @destroy: destruction function |
| 254 | * @dump: function to dump parameters | 273 | * @dump: function to dump parameters |
| 255 | * @type: expression type | 274 | * @type: expression type |
| 275 | * @validate: validate expression, called during loop detection | ||
| 276 | * @data: extra data to attach to this expression operation | ||
| 256 | */ | 277 | */ |
| 257 | struct nft_expr; | 278 | struct nft_expr; |
| 258 | struct nft_expr_ops { | 279 | struct nft_expr_ops { |
| @@ -267,8 +288,11 @@ struct nft_expr_ops { | |||
| 267 | void (*destroy)(const struct nft_expr *expr); | 288 | void (*destroy)(const struct nft_expr *expr); |
| 268 | int (*dump)(struct sk_buff *skb, | 289 | int (*dump)(struct sk_buff *skb, |
| 269 | const struct nft_expr *expr); | 290 | const struct nft_expr *expr); |
| 270 | const struct nft_data * (*get_verdict)(const struct nft_expr *expr); | 291 | int (*validate)(const struct nft_ctx *ctx, |
| 292 | const struct nft_expr *expr, | ||
| 293 | const struct nft_data **data); | ||
| 271 | const struct nft_expr_type *type; | 294 | const struct nft_expr_type *type; |
| 295 | void *data; | ||
| 272 | }; | 296 | }; |
| 273 | 297 | ||
| 274 | #define NFT_EXPR_MAXATTR 16 | 298 | #define NFT_EXPR_MAXATTR 16 |
| @@ -368,16 +392,25 @@ enum nft_chain_type { | |||
| 368 | NFT_CHAIN_T_MAX | 392 | NFT_CHAIN_T_MAX |
| 369 | }; | 393 | }; |
| 370 | 394 | ||
| 395 | struct nft_stats { | ||
| 396 | u64 bytes; | ||
| 397 | u64 pkts; | ||
| 398 | }; | ||
| 399 | |||
| 371 | /** | 400 | /** |
| 372 | * struct nft_base_chain - nf_tables base chain | 401 | * struct nft_base_chain - nf_tables base chain |
| 373 | * | 402 | * |
| 374 | * @ops: netfilter hook ops | 403 | * @ops: netfilter hook ops |
| 375 | * @type: chain type | 404 | * @type: chain type |
| 405 | * @policy: default policy | ||
| 406 | * @stats: per-cpu chain stats | ||
| 376 | * @chain: the chain | 407 | * @chain: the chain |
| 377 | */ | 408 | */ |
| 378 | struct nft_base_chain { | 409 | struct nft_base_chain { |
| 379 | struct nf_hook_ops ops; | 410 | struct nf_hook_ops ops; |
| 380 | enum nft_chain_type type; | 411 | enum nft_chain_type type; |
| 412 | u8 policy; | ||
| 413 | struct nft_stats __percpu *stats; | ||
| 381 | struct nft_chain chain; | 414 | struct nft_chain chain; |
| 382 | }; | 415 | }; |
| 383 | 416 | ||
| @@ -386,11 +419,8 @@ static inline struct nft_base_chain *nft_base_chain(const struct nft_chain *chai | |||
| 386 | return container_of(chain, struct nft_base_chain, chain); | 419 | return container_of(chain, struct nft_base_chain, chain); |
| 387 | } | 420 | } |
| 388 | 421 | ||
| 389 | extern unsigned int nft_do_chain(const struct nf_hook_ops *ops, | 422 | extern unsigned int nft_do_chain_pktinfo(struct nft_pktinfo *pkt, |
| 390 | struct sk_buff *skb, | 423 | const struct nf_hook_ops *ops); |
| 391 | const struct net_device *in, | ||
| 392 | const struct net_device *out, | ||
| 393 | int (*okfn)(struct sk_buff *)); | ||
| 394 | 424 | ||
| 395 | /** | 425 | /** |
| 396 | * struct nft_table - nf_tables table | 426 | * struct nft_table - nf_tables table |
diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h new file mode 100644 index 000000000000..1be1c2c197ee --- /dev/null +++ b/include/net/netfilter/nf_tables_ipv4.h | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #ifndef _NF_TABLES_IPV4_H_ | ||
| 2 | #define _NF_TABLES_IPV4_H_ | ||
| 3 | |||
| 4 | #include <net/netfilter/nf_tables.h> | ||
| 5 | #include <net/ip.h> | ||
| 6 | |||
| 7 | static inline void | ||
| 8 | nft_set_pktinfo_ipv4(struct nft_pktinfo *pkt, | ||
| 9 | const struct nf_hook_ops *ops, | ||
| 10 | struct sk_buff *skb, | ||
| 11 | const struct net_device *in, | ||
| 12 | const struct net_device *out) | ||
| 13 | { | ||
| 14 | struct iphdr *ip; | ||
| 15 | |||
| 16 | nft_set_pktinfo(pkt, ops, skb, in, out); | ||
| 17 | |||
| 18 | pkt->xt.thoff = ip_hdrlen(pkt->skb); | ||
| 19 | ip = ip_hdr(pkt->skb); | ||
| 20 | pkt->xt.fragoff = ntohs(ip->frag_off) & IP_OFFSET; | ||
| 21 | } | ||
| 22 | |||
| 23 | #endif | ||
diff --git a/include/net/netfilter/nf_tables_ipv6.h b/include/net/netfilter/nf_tables_ipv6.h new file mode 100644 index 000000000000..4a9b88a65963 --- /dev/null +++ b/include/net/netfilter/nf_tables_ipv6.h | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #ifndef _NF_TABLES_IPV6_H_ | ||
| 2 | #define _NF_TABLES_IPV6_H_ | ||
| 3 | |||
| 4 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
| 5 | #include <net/ipv6.h> | ||
| 6 | |||
| 7 | static inline int | ||
| 8 | nft_set_pktinfo_ipv6(struct nft_pktinfo *pkt, | ||
| 9 | const struct nf_hook_ops *ops, | ||
| 10 | struct sk_buff *skb, | ||
| 11 | const struct net_device *in, | ||
| 12 | const struct net_device *out) | ||
| 13 | { | ||
| 14 | int protohdr, thoff = 0; | ||
| 15 | unsigned short frag_off; | ||
| 16 | |||
| 17 | nft_set_pktinfo(pkt, ops, skb, in, out); | ||
| 18 | |||
| 19 | protohdr = ipv6_find_hdr(pkt->skb, &thoff, -1, &frag_off, NULL); | ||
| 20 | /* If malformed, drop it */ | ||
| 21 | if (protohdr < 0) | ||
| 22 | return -1; | ||
| 23 | |||
| 24 | pkt->xt.thoff = thoff; | ||
| 25 | pkt->xt.fragoff = frag_off; | ||
| 26 | |||
| 27 | return 0; | ||
| 28 | } | ||
| 29 | |||
| 30 | #endif | ||
diff --git a/include/uapi/linux/netfilter/Kbuild b/include/uapi/linux/netfilter/Kbuild index 6ce0b7f566a7..17c3af2c4bb9 100644 --- a/include/uapi/linux/netfilter/Kbuild +++ b/include/uapi/linux/netfilter/Kbuild | |||
| @@ -6,6 +6,7 @@ header-y += nf_conntrack_sctp.h | |||
| 6 | header-y += nf_conntrack_tcp.h | 6 | header-y += nf_conntrack_tcp.h |
| 7 | header-y += nf_conntrack_tuple_common.h | 7 | header-y += nf_conntrack_tuple_common.h |
| 8 | header-y += nf_tables.h | 8 | header-y += nf_tables.h |
| 9 | header-y += nf_tables_compat.h | ||
| 9 | header-y += nf_nat.h | 10 | header-y += nf_nat.h |
| 10 | header-y += nfnetlink.h | 11 | header-y += nfnetlink.h |
| 11 | header-y += nfnetlink_acct.h | 12 | header-y += nfnetlink_acct.h |
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 779cf951c8de..1563875e6942 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h | |||
| @@ -115,7 +115,10 @@ enum nft_table_attributes { | |||
| 115 | * @NFTA_CHAIN_HANDLE: numeric handle of the chain (NLA_U64) | 115 | * @NFTA_CHAIN_HANDLE: numeric handle of the chain (NLA_U64) |
| 116 | * @NFTA_CHAIN_NAME: name of the chain (NLA_STRING) | 116 | * @NFTA_CHAIN_NAME: name of the chain (NLA_STRING) |
| 117 | * @NFTA_CHAIN_HOOK: hook specification for basechains (NLA_NESTED: nft_hook_attributes) | 117 | * @NFTA_CHAIN_HOOK: hook specification for basechains (NLA_NESTED: nft_hook_attributes) |
| 118 | * @NFTA_CHAIN_POLICY: numeric policy of the chain (NLA_U32) | ||
| 119 | * @NFTA_CHAIN_USE: number of references to this chain (NLA_U32) | ||
| 118 | * @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING) | 120 | * @NFTA_CHAIN_TYPE: type name of the string (NLA_NUL_STRING) |
| 121 | * @NFTA_CHAIN_COUNTERS: counter specification of the chain (NLA_NESTED: nft_counter_attributes) | ||
| 119 | */ | 122 | */ |
| 120 | enum nft_chain_attributes { | 123 | enum nft_chain_attributes { |
| 121 | NFTA_CHAIN_UNSPEC, | 124 | NFTA_CHAIN_UNSPEC, |
| @@ -123,7 +126,10 @@ enum nft_chain_attributes { | |||
| 123 | NFTA_CHAIN_HANDLE, | 126 | NFTA_CHAIN_HANDLE, |
| 124 | NFTA_CHAIN_NAME, | 127 | NFTA_CHAIN_NAME, |
| 125 | NFTA_CHAIN_HOOK, | 128 | NFTA_CHAIN_HOOK, |
| 129 | NFTA_CHAIN_POLICY, | ||
| 130 | NFTA_CHAIN_USE, | ||
| 126 | NFTA_CHAIN_TYPE, | 131 | NFTA_CHAIN_TYPE, |
| 132 | NFTA_CHAIN_COUNTERS, | ||
| 127 | __NFTA_CHAIN_MAX | 133 | __NFTA_CHAIN_MAX |
| 128 | }; | 134 | }; |
| 129 | #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) | 135 | #define NFTA_CHAIN_MAX (__NFTA_CHAIN_MAX - 1) |
| @@ -135,6 +141,7 @@ enum nft_chain_attributes { | |||
| 135 | * @NFTA_RULE_CHAIN: name of the chain containing the rule (NLA_STRING) | 141 | * @NFTA_RULE_CHAIN: name of the chain containing the rule (NLA_STRING) |
| 136 | * @NFTA_RULE_HANDLE: numeric handle of the rule (NLA_U64) | 142 | * @NFTA_RULE_HANDLE: numeric handle of the rule (NLA_U64) |
| 137 | * @NFTA_RULE_EXPRESSIONS: list of expressions (NLA_NESTED: nft_expr_attributes) | 143 | * @NFTA_RULE_EXPRESSIONS: list of expressions (NLA_NESTED: nft_expr_attributes) |
| 144 | * @NFTA_RULE_COMPAT: compatibility specifications of the rule (NLA_NESTED: nft_rule_compat_attributes) | ||
| 138 | */ | 145 | */ |
| 139 | enum nft_rule_attributes { | 146 | enum nft_rule_attributes { |
| 140 | NFTA_RULE_UNSPEC, | 147 | NFTA_RULE_UNSPEC, |
| @@ -142,11 +149,36 @@ enum nft_rule_attributes { | |||
| 142 | NFTA_RULE_CHAIN, | 149 | NFTA_RULE_CHAIN, |
| 143 | NFTA_RULE_HANDLE, | 150 | NFTA_RULE_HANDLE, |
| 144 | NFTA_RULE_EXPRESSIONS, | 151 | NFTA_RULE_EXPRESSIONS, |
| 152 | NFTA_RULE_COMPAT, | ||
| 145 | __NFTA_RULE_MAX | 153 | __NFTA_RULE_MAX |
| 146 | }; | 154 | }; |
| 147 | #define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1) | 155 | #define NFTA_RULE_MAX (__NFTA_RULE_MAX - 1) |
| 148 | 156 | ||
| 149 | /** | 157 | /** |
| 158 | * enum nft_rule_compat_flags - nf_tables rule compat flags | ||
| 159 | * | ||
| 160 | * @NFT_RULE_COMPAT_F_INV: invert the check result | ||
| 161 | */ | ||
| 162 | enum nft_rule_compat_flags { | ||
| 163 | NFT_RULE_COMPAT_F_INV = (1 << 1), | ||
| 164 | NFT_RULE_COMPAT_F_MASK = NFT_RULE_COMPAT_F_INV, | ||
| 165 | }; | ||
| 166 | |||
| 167 | /** | ||
| 168 | * enum nft_rule_compat_attributes - nf_tables rule compat attributes | ||
| 169 | * | ||
| 170 | * @NFTA_RULE_COMPAT_PROTO: numerice value of handled protocol (NLA_U32) | ||
| 171 | * @NFTA_RULE_COMPAT_FLAGS: bitmask of enum nft_rule_compat_flags (NLA_U32) | ||
| 172 | */ | ||
| 173 | enum nft_rule_compat_attributes { | ||
| 174 | NFTA_RULE_COMPAT_UNSPEC, | ||
| 175 | NFTA_RULE_COMPAT_PROTO, | ||
| 176 | NFTA_RULE_COMPAT_FLAGS, | ||
| 177 | __NFTA_RULE_COMPAT_MAX | ||
| 178 | }; | ||
| 179 | #define NFTA_RULE_COMPAT_MAX (__NFTA_RULE_COMPAT_MAX - 1) | ||
| 180 | |||
| 181 | /** | ||
| 150 | * enum nft_set_flags - nf_tables set flags | 182 | * enum nft_set_flags - nf_tables set flags |
| 151 | * | 183 | * |
| 152 | * @NFT_SET_ANONYMOUS: name allocation, automatic cleanup on unlink | 184 | * @NFT_SET_ANONYMOUS: name allocation, automatic cleanup on unlink |
diff --git a/include/uapi/linux/netfilter/nf_tables_compat.h b/include/uapi/linux/netfilter/nf_tables_compat.h new file mode 100644 index 000000000000..8310f5f76551 --- /dev/null +++ b/include/uapi/linux/netfilter/nf_tables_compat.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | #ifndef _NFT_COMPAT_NFNETLINK_H_ | ||
| 2 | #define _NFT_COMPAT_NFNETLINK_H_ | ||
| 3 | |||
| 4 | enum nft_target_attributes { | ||
| 5 | NFTA_TARGET_UNSPEC, | ||
| 6 | NFTA_TARGET_NAME, | ||
| 7 | NFTA_TARGET_REV, | ||
| 8 | NFTA_TARGET_INFO, | ||
| 9 | __NFTA_TARGET_MAX | ||
| 10 | }; | ||
| 11 | #define NFTA_TARGET_MAX (__NFTA_TARGET_MAX - 1) | ||
| 12 | |||
| 13 | enum nft_match_attributes { | ||
| 14 | NFTA_MATCH_UNSPEC, | ||
| 15 | NFTA_MATCH_NAME, | ||
| 16 | NFTA_MATCH_REV, | ||
| 17 | NFTA_MATCH_INFO, | ||
| 18 | __NFTA_MATCH_MAX | ||
| 19 | }; | ||
| 20 | #define NFTA_MATCH_MAX (__NFTA_MATCH_MAX - 1) | ||
| 21 | |||
| 22 | #define NFT_COMPAT_NAME_MAX 32 | ||
| 23 | |||
| 24 | enum { | ||
| 25 | NFNL_MSG_COMPAT_GET, | ||
| 26 | NFNL_MSG_COMPAT_MAX | ||
| 27 | }; | ||
| 28 | |||
| 29 | enum { | ||
| 30 | NFTA_COMPAT_UNSPEC = 0, | ||
| 31 | NFTA_COMPAT_NAME, | ||
| 32 | NFTA_COMPAT_REV, | ||
| 33 | NFTA_COMPAT_TYPE, | ||
| 34 | __NFTA_COMPAT_MAX, | ||
| 35 | }; | ||
| 36 | #define NFTA_COMPAT_MAX (__NFTA_COMPAT_MAX - 1) | ||
| 37 | |||
| 38 | #endif | ||
diff --git a/include/uapi/linux/netfilter/nfnetlink.h b/include/uapi/linux/netfilter/nfnetlink.h index d276c3bd55b8..288959404d54 100644 --- a/include/uapi/linux/netfilter/nfnetlink.h +++ b/include/uapi/linux/netfilter/nfnetlink.h | |||
| @@ -54,6 +54,7 @@ struct nfgenmsg { | |||
| 54 | #define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 | 54 | #define NFNL_SUBSYS_CTNETLINK_TIMEOUT 8 |
| 55 | #define NFNL_SUBSYS_CTHELPER 9 | 55 | #define NFNL_SUBSYS_CTHELPER 9 |
| 56 | #define NFNL_SUBSYS_NFTABLES 10 | 56 | #define NFNL_SUBSYS_NFTABLES 10 |
| 57 | #define NFNL_SUBSYS_COUNT 11 | 57 | #define NFNL_SUBSYS_NFT_COMPAT 11 |
| 58 | #define NFNL_SUBSYS_COUNT 12 | ||
| 58 | 59 | ||
| 59 | #endif /* _UAPI_NFNETLINK_H */ | 60 | #endif /* _UAPI_NFNETLINK_H */ |
diff --git a/net/ipv4/netfilter/nf_tables_ipv4.c b/net/ipv4/netfilter/nf_tables_ipv4.c index 23525c4c0192..c61cffb9b760 100644 --- a/net/ipv4/netfilter/nf_tables_ipv4.c +++ b/net/ipv4/netfilter/nf_tables_ipv4.c | |||
| @@ -15,6 +15,8 @@ | |||
| 15 | #include <linux/netfilter_ipv4.h> | 15 | #include <linux/netfilter_ipv4.h> |
| 16 | #include <net/netfilter/nf_tables.h> | 16 | #include <net/netfilter/nf_tables.h> |
| 17 | #include <net/ip.h> | 17 | #include <net/ip.h> |
| 18 | #include <net/net_namespace.h> | ||
| 19 | #include <net/netfilter/nf_tables_ipv4.h> | ||
| 18 | 20 | ||
| 19 | static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops, | 21 | static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops, |
| 20 | struct sk_buff *skb, | 22 | struct sk_buff *skb, |
| @@ -22,6 +24,8 @@ static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops, | |||
| 22 | const struct net_device *out, | 24 | const struct net_device *out, |
| 23 | int (*okfn)(struct sk_buff *)) | 25 | int (*okfn)(struct sk_buff *)) |
| 24 | { | 26 | { |
| 27 | struct nft_pktinfo pkt; | ||
| 28 | |||
| 25 | if (unlikely(skb->len < sizeof(struct iphdr) || | 29 | if (unlikely(skb->len < sizeof(struct iphdr) || |
| 26 | ip_hdr(skb)->ihl < sizeof(struct iphdr) / 4)) { | 30 | ip_hdr(skb)->ihl < sizeof(struct iphdr) / 4)) { |
| 27 | if (net_ratelimit()) | 31 | if (net_ratelimit()) |
| @@ -29,8 +33,9 @@ static unsigned int nft_ipv4_output(const struct nf_hook_ops *ops, | |||
| 29 | "packet\n"); | 33 | "packet\n"); |
| 30 | return NF_ACCEPT; | 34 | return NF_ACCEPT; |
| 31 | } | 35 | } |
| 36 | nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); | ||
| 32 | 37 | ||
| 33 | return nft_do_chain(ops, skb, in, out, okfn); | 38 | return nft_do_chain_pktinfo(&pkt, ops); |
| 34 | } | 39 | } |
| 35 | 40 | ||
| 36 | static struct nft_af_info nft_af_ipv4 __read_mostly = { | 41 | static struct nft_af_info nft_af_ipv4 __read_mostly = { |
| @@ -42,6 +47,21 @@ static struct nft_af_info nft_af_ipv4 __read_mostly = { | |||
| 42 | }, | 47 | }, |
| 43 | }; | 48 | }; |
| 44 | 49 | ||
| 50 | |||
| 51 | static unsigned int | ||
| 52 | nft_do_chain_ipv4(const struct nf_hook_ops *ops, | ||
| 53 | struct sk_buff *skb, | ||
| 54 | const struct net_device *in, | ||
| 55 | const struct net_device *out, | ||
| 56 | int (*okfn)(struct sk_buff *)) | ||
| 57 | { | ||
| 58 | struct nft_pktinfo pkt; | ||
| 59 | |||
| 60 | nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); | ||
| 61 | |||
| 62 | return nft_do_chain_pktinfo(&pkt, ops); | ||
| 63 | } | ||
| 64 | |||
| 45 | static struct nf_chain_type filter_ipv4 = { | 65 | static struct nf_chain_type filter_ipv4 = { |
| 46 | .family = NFPROTO_IPV4, | 66 | .family = NFPROTO_IPV4, |
| 47 | .name = "filter", | 67 | .name = "filter", |
| @@ -52,11 +72,11 @@ static struct nf_chain_type filter_ipv4 = { | |||
| 52 | (1 << NF_INET_PRE_ROUTING) | | 72 | (1 << NF_INET_PRE_ROUTING) | |
| 53 | (1 << NF_INET_POST_ROUTING), | 73 | (1 << NF_INET_POST_ROUTING), |
| 54 | .fn = { | 74 | .fn = { |
| 55 | [NF_INET_LOCAL_IN] = nft_do_chain, | 75 | [NF_INET_LOCAL_IN] = nft_do_chain_ipv4, |
| 56 | [NF_INET_LOCAL_OUT] = nft_do_chain, | 76 | [NF_INET_LOCAL_OUT] = nft_ipv4_output, |
| 57 | [NF_INET_FORWARD] = nft_do_chain, | 77 | [NF_INET_FORWARD] = nft_do_chain_ipv4, |
| 58 | [NF_INET_PRE_ROUTING] = nft_do_chain, | 78 | [NF_INET_PRE_ROUTING] = nft_do_chain_ipv4, |
| 59 | [NF_INET_POST_ROUTING] = nft_do_chain, | 79 | [NF_INET_POST_ROUTING] = nft_do_chain_ipv4, |
| 60 | }, | 80 | }, |
| 61 | }; | 81 | }; |
| 62 | 82 | ||
diff --git a/net/ipv4/netfilter/nft_chain_nat_ipv4.c b/net/ipv4/netfilter/nft_chain_nat_ipv4.c index cd286306be85..e09c201adf84 100644 --- a/net/ipv4/netfilter/nft_chain_nat_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_nat_ipv4.c | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include <net/netfilter/nf_nat.h> | 23 | #include <net/netfilter/nf_nat.h> |
| 24 | #include <net/netfilter/nf_nat_core.h> | 24 | #include <net/netfilter/nf_nat_core.h> |
| 25 | #include <net/netfilter/nf_tables.h> | 25 | #include <net/netfilter/nf_tables.h> |
| 26 | #include <net/netfilter/nf_tables_ipv4.h> | ||
| 26 | #include <net/netfilter/nf_nat_l3proto.h> | 27 | #include <net/netfilter/nf_nat_l3proto.h> |
| 27 | #include <net/ip.h> | 28 | #include <net/ip.h> |
| 28 | 29 | ||
| @@ -181,6 +182,7 @@ static unsigned int nf_nat_fn(const struct nf_hook_ops *ops, | |||
| 181 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); | 182 | struct nf_conn *ct = nf_ct_get(skb, &ctinfo); |
| 182 | struct nf_conn_nat *nat; | 183 | struct nf_conn_nat *nat; |
| 183 | enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); | 184 | enum nf_nat_manip_type maniptype = HOOK2MANIP(ops->hooknum); |
| 185 | struct nft_pktinfo pkt; | ||
| 184 | unsigned int ret; | 186 | unsigned int ret; |
| 185 | 187 | ||
| 186 | if (ct == NULL || nf_ct_is_untracked(ct)) | 188 | if (ct == NULL || nf_ct_is_untracked(ct)) |
| @@ -213,7 +215,9 @@ static unsigned int nf_nat_fn(const struct nf_hook_ops *ops, | |||
| 213 | if (nf_nat_initialized(ct, maniptype)) | 215 | if (nf_nat_initialized(ct, maniptype)) |
| 214 | break; | 216 | break; |
| 215 | 217 | ||
| 216 | ret = nft_do_chain(ops, skb, in, out, okfn); | 218 | nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); |
| 219 | |||
| 220 | ret = nft_do_chain_pktinfo(&pkt, ops); | ||
| 217 | if (ret != NF_ACCEPT) | 221 | if (ret != NF_ACCEPT) |
| 218 | return ret; | 222 | return ret; |
| 219 | if (!nf_nat_initialized(ct, maniptype)) { | 223 | if (!nf_nat_initialized(ct, maniptype)) { |
diff --git a/net/ipv4/netfilter/nft_chain_route_ipv4.c b/net/ipv4/netfilter/nft_chain_route_ipv4.c index 6b84e097b8fc..4e6bf9a3d7aa 100644 --- a/net/ipv4/netfilter/nft_chain_route_ipv4.c +++ b/net/ipv4/netfilter/nft_chain_route_ipv4.c | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include <linux/netfilter/nfnetlink.h> | 17 | #include <linux/netfilter/nfnetlink.h> |
| 18 | #include <linux/netfilter/nf_tables.h> | 18 | #include <linux/netfilter/nf_tables.h> |
| 19 | #include <net/netfilter/nf_tables.h> | 19 | #include <net/netfilter/nf_tables.h> |
| 20 | #include <net/netfilter/nf_tables_ipv4.h> | ||
| 20 | #include <net/route.h> | 21 | #include <net/route.h> |
| 21 | #include <net/ip.h> | 22 | #include <net/ip.h> |
| 22 | 23 | ||
| @@ -27,6 +28,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, | |||
| 27 | int (*okfn)(struct sk_buff *)) | 28 | int (*okfn)(struct sk_buff *)) |
| 28 | { | 29 | { |
| 29 | unsigned int ret; | 30 | unsigned int ret; |
| 31 | struct nft_pktinfo pkt; | ||
| 30 | u32 mark; | 32 | u32 mark; |
| 31 | __be32 saddr, daddr; | 33 | __be32 saddr, daddr; |
| 32 | u_int8_t tos; | 34 | u_int8_t tos; |
| @@ -37,13 +39,15 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, | |||
| 37 | ip_hdrlen(skb) < sizeof(struct iphdr)) | 39 | ip_hdrlen(skb) < sizeof(struct iphdr)) |
| 38 | return NF_ACCEPT; | 40 | return NF_ACCEPT; |
| 39 | 41 | ||
| 42 | nft_set_pktinfo_ipv4(&pkt, ops, skb, in, out); | ||
| 43 | |||
| 40 | mark = skb->mark; | 44 | mark = skb->mark; |
| 41 | iph = ip_hdr(skb); | 45 | iph = ip_hdr(skb); |
| 42 | saddr = iph->saddr; | 46 | saddr = iph->saddr; |
| 43 | daddr = iph->daddr; | 47 | daddr = iph->daddr; |
| 44 | tos = iph->tos; | 48 | tos = iph->tos; |
| 45 | 49 | ||
| 46 | ret = nft_do_chain(ops, skb, in, out, okfn); | 50 | ret = nft_do_chain_pktinfo(&pkt, ops); |
| 47 | if (ret != NF_DROP && ret != NF_QUEUE) { | 51 | if (ret != NF_DROP && ret != NF_QUEUE) { |
| 48 | iph = ip_hdr(skb); | 52 | iph = ip_hdr(skb); |
| 49 | 53 | ||
diff --git a/net/ipv6/netfilter/nf_tables_ipv6.c b/net/ipv6/netfilter/nf_tables_ipv6.c index 3631d6238e6f..42f905a808a3 100644 --- a/net/ipv6/netfilter/nf_tables_ipv6.c +++ b/net/ipv6/netfilter/nf_tables_ipv6.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/ipv6.h> | 14 | #include <linux/ipv6.h> |
| 15 | #include <linux/netfilter_ipv6.h> | 15 | #include <linux/netfilter_ipv6.h> |
| 16 | #include <net/netfilter/nf_tables.h> | 16 | #include <net/netfilter/nf_tables.h> |
| 17 | #include <net/netfilter/nf_tables_ipv6.h> | ||
| 17 | 18 | ||
| 18 | static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops, | 19 | static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops, |
| 19 | struct sk_buff *skb, | 20 | struct sk_buff *skb, |
| @@ -21,14 +22,18 @@ static unsigned int nft_ipv6_output(const struct nf_hook_ops *ops, | |||
| 21 | const struct net_device *out, | 22 | const struct net_device *out, |
| 22 | int (*okfn)(struct sk_buff *)) | 23 | int (*okfn)(struct sk_buff *)) |
| 23 | { | 24 | { |
| 25 | struct nft_pktinfo pkt; | ||
| 26 | |||
| 24 | if (unlikely(skb->len < sizeof(struct ipv6hdr))) { | 27 | if (unlikely(skb->len < sizeof(struct ipv6hdr))) { |
| 25 | if (net_ratelimit()) | 28 | if (net_ratelimit()) |
| 26 | pr_info("nf_tables_ipv6: ignoring short SOCK_RAW " | 29 | pr_info("nf_tables_ipv6: ignoring short SOCK_RAW " |
| 27 | "packet\n"); | 30 | "packet\n"); |
| 28 | return NF_ACCEPT; | 31 | return NF_ACCEPT; |
| 29 | } | 32 | } |
| 33 | if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0) | ||
| 34 | return NF_DROP; | ||
| 30 | 35 | ||
| 31 | return nft_do_chain(ops, skb, in, out, okfn); | 36 | return nft_do_chain_pktinfo(&pkt, ops); |
| 32 | } | 37 | } |
| 33 | 38 | ||
| 34 | static struct nft_af_info nft_af_ipv6 __read_mostly = { | 39 | static struct nft_af_info nft_af_ipv6 __read_mostly = { |
| @@ -40,6 +45,22 @@ static struct nft_af_info nft_af_ipv6 __read_mostly = { | |||
| 40 | }, | 45 | }, |
| 41 | }; | 46 | }; |
| 42 | 47 | ||
| 48 | static unsigned int | ||
| 49 | nft_do_chain_ipv6(const struct nf_hook_ops *ops, | ||
| 50 | struct sk_buff *skb, | ||
| 51 | const struct net_device *in, | ||
| 52 | const struct net_device *out, | ||
| 53 | int (*okfn)(struct sk_buff *)) | ||
| 54 | { | ||
| 55 | struct nft_pktinfo pkt; | ||
| 56 | |||
| 57 | /* malformed packet, drop it */ | ||
| 58 | if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0) | ||
| 59 | return NF_DROP; | ||
| 60 | |||
| 61 | return nft_do_chain_pktinfo(&pkt, ops); | ||
| 62 | } | ||
| 63 | |||
| 43 | static struct nf_chain_type filter_ipv6 = { | 64 | static struct nf_chain_type filter_ipv6 = { |
| 44 | .family = NFPROTO_IPV6, | 65 | .family = NFPROTO_IPV6, |
| 45 | .name = "filter", | 66 | .name = "filter", |
| @@ -50,11 +71,11 @@ static struct nf_chain_type filter_ipv6 = { | |||
| 50 | (1 << NF_INET_PRE_ROUTING) | | 71 | (1 << NF_INET_PRE_ROUTING) | |
| 51 | (1 << NF_INET_POST_ROUTING), | 72 | (1 << NF_INET_POST_ROUTING), |
| 52 | .fn = { | 73 | .fn = { |
| 53 | [NF_INET_LOCAL_IN] = nft_do_chain, | 74 | [NF_INET_LOCAL_IN] = nft_do_chain_ipv6, |
| 54 | [NF_INET_LOCAL_OUT] = nft_do_chain, | 75 | [NF_INET_LOCAL_OUT] = nft_ipv6_output, |
| 55 | [NF_INET_FORWARD] = nft_do_chain, | 76 | [NF_INET_FORWARD] = nft_do_chain_ipv6, |
| 56 | [NF_INET_PRE_ROUTING] = nft_do_chain, | 77 | [NF_INET_PRE_ROUTING] = nft_do_chain_ipv6, |
| 57 | [NF_INET_POST_ROUTING] = nft_do_chain, | 78 | [NF_INET_POST_ROUTING] = nft_do_chain_ipv6, |
| 58 | }, | 79 | }, |
| 59 | }; | 80 | }; |
| 60 | 81 | ||
diff --git a/net/ipv6/netfilter/nft_chain_route_ipv6.c b/net/ipv6/netfilter/nft_chain_route_ipv6.c index 4cdc992fa067..3fe40f0456ad 100644 --- a/net/ipv6/netfilter/nft_chain_route_ipv6.c +++ b/net/ipv6/netfilter/nft_chain_route_ipv6.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/netfilter/nfnetlink.h> | 19 | #include <linux/netfilter/nfnetlink.h> |
| 20 | #include <linux/netfilter/nf_tables.h> | 20 | #include <linux/netfilter/nf_tables.h> |
| 21 | #include <net/netfilter/nf_tables.h> | 21 | #include <net/netfilter/nf_tables.h> |
| 22 | #include <net/netfilter/nf_tables_ipv6.h> | ||
| 22 | #include <net/route.h> | 23 | #include <net/route.h> |
| 23 | 24 | ||
| 24 | static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, | 25 | static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, |
| @@ -28,10 +29,15 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, | |||
| 28 | int (*okfn)(struct sk_buff *)) | 29 | int (*okfn)(struct sk_buff *)) |
| 29 | { | 30 | { |
| 30 | unsigned int ret; | 31 | unsigned int ret; |
| 32 | struct nft_pktinfo pkt; | ||
| 31 | struct in6_addr saddr, daddr; | 33 | struct in6_addr saddr, daddr; |
| 32 | u_int8_t hop_limit; | 34 | u_int8_t hop_limit; |
| 33 | u32 mark, flowlabel; | 35 | u32 mark, flowlabel; |
| 34 | 36 | ||
| 37 | /* malformed packet, drop it */ | ||
| 38 | if (nft_set_pktinfo_ipv6(&pkt, ops, skb, in, out) < 0) | ||
| 39 | return NF_DROP; | ||
| 40 | |||
| 35 | /* save source/dest address, mark, hoplimit, flowlabel, priority */ | 41 | /* save source/dest address, mark, hoplimit, flowlabel, priority */ |
| 36 | memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(saddr)); | 42 | memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(saddr)); |
| 37 | memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(daddr)); | 43 | memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(daddr)); |
| @@ -41,7 +47,7 @@ static unsigned int nf_route_table_hook(const struct nf_hook_ops *ops, | |||
| 41 | /* flowlabel and prio (includes version, which shouldn't change either */ | 47 | /* flowlabel and prio (includes version, which shouldn't change either */ |
| 42 | flowlabel = *((u32 *)ipv6_hdr(skb)); | 48 | flowlabel = *((u32 *)ipv6_hdr(skb)); |
| 43 | 49 | ||
| 44 | ret = nft_do_chain(ops, skb, in, out, okfn); | 50 | ret = nft_do_chain_pktinfo(&pkt, ops); |
| 45 | if (ret != NF_DROP && ret != NF_QUEUE && | 51 | if (ret != NF_DROP && ret != NF_QUEUE && |
| 46 | (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) || | 52 | (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) || |
| 47 | memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) || | 53 | memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) || |
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index aa184a46bbf3..49e362707379 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
| @@ -450,6 +450,15 @@ config NFT_LIMIT | |||
| 450 | depends on NF_TABLES | 450 | depends on NF_TABLES |
| 451 | tristate "Netfilter nf_tables limit module" | 451 | tristate "Netfilter nf_tables limit module" |
| 452 | 452 | ||
| 453 | config NFT_COMPAT | ||
| 454 | depends on NF_TABLES | ||
| 455 | depends on NETFILTER_XTABLES | ||
| 456 | tristate "Netfilter x_tables over nf_tables module" | ||
| 457 | help | ||
| 458 | This is required if you intend to use any of existing | ||
| 459 | x_tables match/target extensions over the nf_tables | ||
| 460 | framework. | ||
| 461 | |||
| 453 | config NETFILTER_XTABLES | 462 | config NETFILTER_XTABLES |
| 454 | tristate "Netfilter Xtables support (required for ip_tables)" | 463 | tristate "Netfilter Xtables support (required for ip_tables)" |
| 455 | default m if NETFILTER_ADVANCED=n | 464 | default m if NETFILTER_ADVANCED=n |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index b6b78754e4cc..a6781450b6fb 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
| @@ -70,6 +70,7 @@ nf_tables-objs += nft_immediate.o nft_cmp.o nft_lookup.o | |||
| 70 | nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o | 70 | nf_tables-objs += nft_bitwise.o nft_byteorder.o nft_payload.o |
| 71 | 71 | ||
| 72 | obj-$(CONFIG_NF_TABLES) += nf_tables.o | 72 | obj-$(CONFIG_NF_TABLES) += nf_tables.o |
| 73 | obj-$(CONFIG_NFT_COMPAT) += nft_compat.o | ||
| 73 | obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o | 74 | obj-$(CONFIG_NFT_EXTHDR) += nft_exthdr.o |
| 74 | obj-$(CONFIG_NFT_META) += nft_meta.o | 75 | obj-$(CONFIG_NFT_META) += nft_meta.o |
| 75 | obj-$(CONFIG_NFT_CT) += nft_ct.o | 76 | obj-$(CONFIG_NFT_CT) += nft_ct.o |
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 9c2d8d5af843..61e017b349cb 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c | |||
| @@ -438,7 +438,9 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = { | |||
| 438 | [NFTA_CHAIN_NAME] = { .type = NLA_STRING, | 438 | [NFTA_CHAIN_NAME] = { .type = NLA_STRING, |
| 439 | .len = NFT_CHAIN_MAXNAMELEN - 1 }, | 439 | .len = NFT_CHAIN_MAXNAMELEN - 1 }, |
| 440 | [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED }, | 440 | [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED }, |
| 441 | [NFTA_CHAIN_POLICY] = { .type = NLA_U32 }, | ||
| 441 | [NFTA_CHAIN_TYPE] = { .type = NLA_NUL_STRING }, | 442 | [NFTA_CHAIN_TYPE] = { .type = NLA_NUL_STRING }, |
| 443 | [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED }, | ||
| 442 | }; | 444 | }; |
| 443 | 445 | ||
| 444 | static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = { | 446 | static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = { |
| @@ -446,6 +448,33 @@ static const struct nla_policy nft_hook_policy[NFTA_HOOK_MAX + 1] = { | |||
| 446 | [NFTA_HOOK_PRIORITY] = { .type = NLA_U32 }, | 448 | [NFTA_HOOK_PRIORITY] = { .type = NLA_U32 }, |
| 447 | }; | 449 | }; |
| 448 | 450 | ||
| 451 | static int nft_dump_stats(struct sk_buff *skb, struct nft_stats __percpu *stats) | ||
| 452 | { | ||
| 453 | struct nft_stats *cpu_stats, total; | ||
| 454 | struct nlattr *nest; | ||
| 455 | int cpu; | ||
| 456 | |||
| 457 | memset(&total, 0, sizeof(total)); | ||
| 458 | for_each_possible_cpu(cpu) { | ||
| 459 | cpu_stats = per_cpu_ptr(stats, cpu); | ||
| 460 | total.pkts += cpu_stats->pkts; | ||
| 461 | total.bytes += cpu_stats->bytes; | ||
| 462 | } | ||
| 463 | nest = nla_nest_start(skb, NFTA_CHAIN_COUNTERS); | ||
| 464 | if (nest == NULL) | ||
| 465 | goto nla_put_failure; | ||
| 466 | |||
| 467 | if (nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.pkts)) || | ||
| 468 | nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes))) | ||
| 469 | goto nla_put_failure; | ||
| 470 | |||
| 471 | nla_nest_end(skb, nest); | ||
| 472 | return 0; | ||
| 473 | |||
| 474 | nla_put_failure: | ||
| 475 | return -ENOSPC; | ||
| 476 | } | ||
| 477 | |||
| 449 | static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, | 478 | static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, |
| 450 | int event, u32 flags, int family, | 479 | int event, u32 flags, int family, |
| 451 | const struct nft_table *table, | 480 | const struct nft_table *table, |
| @@ -472,8 +501,11 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, | |||
| 472 | goto nla_put_failure; | 501 | goto nla_put_failure; |
| 473 | 502 | ||
| 474 | if (chain->flags & NFT_BASE_CHAIN) { | 503 | if (chain->flags & NFT_BASE_CHAIN) { |
| 475 | const struct nf_hook_ops *ops = &nft_base_chain(chain)->ops; | 504 | const struct nft_base_chain *basechain = nft_base_chain(chain); |
| 476 | struct nlattr *nest = nla_nest_start(skb, NFTA_CHAIN_HOOK); | 505 | const struct nf_hook_ops *ops = &basechain->ops; |
| 506 | struct nlattr *nest; | ||
| 507 | |||
| 508 | nest = nla_nest_start(skb, NFTA_CHAIN_HOOK); | ||
| 477 | if (nest == NULL) | 509 | if (nest == NULL) |
| 478 | goto nla_put_failure; | 510 | goto nla_put_failure; |
| 479 | if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum))) | 511 | if (nla_put_be32(skb, NFTA_HOOK_HOOKNUM, htonl(ops->hooknum))) |
| @@ -482,11 +514,21 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, | |||
| 482 | goto nla_put_failure; | 514 | goto nla_put_failure; |
| 483 | nla_nest_end(skb, nest); | 515 | nla_nest_end(skb, nest); |
| 484 | 516 | ||
| 517 | if (nla_put_be32(skb, NFTA_CHAIN_POLICY, | ||
| 518 | htonl(basechain->policy))) | ||
| 519 | goto nla_put_failure; | ||
| 520 | |||
| 485 | if (nla_put_string(skb, NFTA_CHAIN_TYPE, | 521 | if (nla_put_string(skb, NFTA_CHAIN_TYPE, |
| 486 | chain_type[ops->pf][nft_base_chain(chain)->type]->name)) | 522 | chain_type[ops->pf][nft_base_chain(chain)->type]->name)) |
| 487 | goto nla_put_failure; | 523 | goto nla_put_failure; |
| 524 | |||
| 525 | if (nft_dump_stats(skb, nft_base_chain(chain)->stats)) | ||
| 526 | goto nla_put_failure; | ||
| 488 | } | 527 | } |
| 489 | 528 | ||
| 529 | if (nla_put_be32(skb, NFTA_CHAIN_USE, htonl(chain->use))) | ||
| 530 | goto nla_put_failure; | ||
| 531 | |||
| 490 | return nlmsg_end(skb, nlh); | 532 | return nlmsg_end(skb, nlh); |
| 491 | 533 | ||
| 492 | nla_put_failure: | 534 | nla_put_failure: |
| @@ -617,6 +659,67 @@ err: | |||
| 617 | return err; | 659 | return err; |
| 618 | } | 660 | } |
| 619 | 661 | ||
| 662 | static int | ||
| 663 | nf_tables_chain_policy(struct nft_base_chain *chain, const struct nlattr *attr) | ||
| 664 | { | ||
| 665 | switch (ntohl(nla_get_be32(attr))) { | ||
| 666 | case NF_DROP: | ||
| 667 | chain->policy = NF_DROP; | ||
| 668 | break; | ||
| 669 | case NF_ACCEPT: | ||
| 670 | chain->policy = NF_ACCEPT; | ||
| 671 | break; | ||
| 672 | default: | ||
| 673 | return -EINVAL; | ||
| 674 | } | ||
| 675 | return 0; | ||
| 676 | } | ||
| 677 | |||
| 678 | static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { | ||
| 679 | [NFTA_COUNTER_PACKETS] = { .type = NLA_U64 }, | ||
| 680 | [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, | ||
| 681 | }; | ||
| 682 | |||
| 683 | static int | ||
| 684 | nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) | ||
| 685 | { | ||
| 686 | struct nlattr *tb[NFTA_COUNTER_MAX+1]; | ||
| 687 | struct nft_stats __percpu *newstats; | ||
| 688 | struct nft_stats *stats; | ||
| 689 | int err; | ||
| 690 | |||
| 691 | err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy); | ||
| 692 | if (err < 0) | ||
| 693 | return err; | ||
| 694 | |||
| 695 | if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS]) | ||
| 696 | return -EINVAL; | ||
| 697 | |||
| 698 | newstats = alloc_percpu(struct nft_stats); | ||
| 699 | if (newstats == NULL) | ||
| 700 | return -ENOMEM; | ||
| 701 | |||
| 702 | /* Restore old counters on this cpu, no problem. Per-cpu statistics | ||
| 703 | * are not exposed to userspace. | ||
| 704 | */ | ||
| 705 | stats = this_cpu_ptr(newstats); | ||
| 706 | stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); | ||
| 707 | stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); | ||
| 708 | |||
| 709 | if (chain->stats) { | ||
| 710 | /* nfnl_lock is held, add some nfnl function for this, later */ | ||
| 711 | struct nft_stats __percpu *oldstats = | ||
| 712 | rcu_dereference_protected(chain->stats, 1); | ||
| 713 | |||
| 714 | rcu_assign_pointer(chain->stats, newstats); | ||
| 715 | synchronize_rcu(); | ||
| 716 | free_percpu(oldstats); | ||
| 717 | } else | ||
| 718 | rcu_assign_pointer(chain->stats, newstats); | ||
| 719 | |||
| 720 | return 0; | ||
| 721 | } | ||
| 722 | |||
| 620 | static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, | 723 | static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, |
| 621 | const struct nlmsghdr *nlh, | 724 | const struct nlmsghdr *nlh, |
| 622 | const struct nlattr * const nla[]) | 725 | const struct nlattr * const nla[]) |
| @@ -626,7 +729,7 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, | |||
| 626 | const struct nft_af_info *afi; | 729 | const struct nft_af_info *afi; |
| 627 | struct nft_table *table; | 730 | struct nft_table *table; |
| 628 | struct nft_chain *chain; | 731 | struct nft_chain *chain; |
| 629 | struct nft_base_chain *basechain; | 732 | struct nft_base_chain *basechain = NULL; |
| 630 | struct nlattr *ha[NFTA_HOOK_MAX + 1]; | 733 | struct nlattr *ha[NFTA_HOOK_MAX + 1]; |
| 631 | int family = nfmsg->nfgen_family; | 734 | int family = nfmsg->nfgen_family; |
| 632 | u64 handle = 0; | 735 | u64 handle = 0; |
| @@ -673,6 +776,26 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, | |||
| 673 | !IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]))) | 776 | !IS_ERR(nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]))) |
| 674 | return -EEXIST; | 777 | return -EEXIST; |
| 675 | 778 | ||
| 779 | if (nla[NFTA_CHAIN_POLICY]) { | ||
| 780 | if (!(chain->flags & NFT_BASE_CHAIN)) | ||
| 781 | return -EOPNOTSUPP; | ||
| 782 | |||
| 783 | err = nf_tables_chain_policy(nft_base_chain(chain), | ||
| 784 | nla[NFTA_CHAIN_POLICY]); | ||
| 785 | if (err < 0) | ||
| 786 | return err; | ||
| 787 | } | ||
| 788 | |||
| 789 | if (nla[NFTA_CHAIN_COUNTERS]) { | ||
| 790 | if (!(chain->flags & NFT_BASE_CHAIN)) | ||
| 791 | return -EOPNOTSUPP; | ||
| 792 | |||
| 793 | err = nf_tables_counters(nft_base_chain(chain), | ||
| 794 | nla[NFTA_CHAIN_COUNTERS]); | ||
| 795 | if (err < 0) | ||
| 796 | return err; | ||
| 797 | } | ||
| 798 | |||
| 676 | if (nla[NFTA_CHAIN_HANDLE] && name) | 799 | if (nla[NFTA_CHAIN_HANDLE] && name) |
| 677 | nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); | 800 | nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); |
| 678 | 801 | ||
| @@ -727,6 +850,36 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, | |||
| 727 | ops->hook = afi->hooks[ops->hooknum]; | 850 | ops->hook = afi->hooks[ops->hooknum]; |
| 728 | 851 | ||
| 729 | chain->flags |= NFT_BASE_CHAIN; | 852 | chain->flags |= NFT_BASE_CHAIN; |
| 853 | |||
| 854 | if (nla[NFTA_CHAIN_POLICY]) { | ||
| 855 | err = nf_tables_chain_policy(basechain, | ||
| 856 | nla[NFTA_CHAIN_POLICY]); | ||
| 857 | if (err < 0) { | ||
| 858 | free_percpu(basechain->stats); | ||
| 859 | kfree(basechain); | ||
| 860 | return err; | ||
| 861 | } | ||
| 862 | } else | ||
| 863 | basechain->policy = NF_ACCEPT; | ||
| 864 | |||
| 865 | if (nla[NFTA_CHAIN_COUNTERS]) { | ||
| 866 | err = nf_tables_counters(basechain, | ||
| 867 | nla[NFTA_CHAIN_COUNTERS]); | ||
| 868 | if (err < 0) { | ||
| 869 | free_percpu(basechain->stats); | ||
| 870 | kfree(basechain); | ||
| 871 | return err; | ||
| 872 | } | ||
| 873 | } else { | ||
| 874 | struct nft_stats __percpu *newstats; | ||
| 875 | |||
| 876 | newstats = alloc_percpu(struct nft_stats); | ||
| 877 | if (newstats == NULL) | ||
| 878 | return -ENOMEM; | ||
| 879 | |||
| 880 | rcu_assign_pointer(nft_base_chain(chain)->stats, | ||
| 881 | newstats); | ||
| 882 | } | ||
| 730 | } else { | 883 | } else { |
| 731 | chain = kzalloc(sizeof(*chain), GFP_KERNEL); | 884 | chain = kzalloc(sizeof(*chain), GFP_KERNEL); |
| 732 | if (chain == NULL) | 885 | if (chain == NULL) |
| @@ -739,6 +892,15 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, | |||
| 739 | 892 | ||
| 740 | list_add_tail(&chain->list, &table->chains); | 893 | list_add_tail(&chain->list, &table->chains); |
| 741 | table->use++; | 894 | table->use++; |
| 895 | |||
| 896 | if (chain->flags & NFT_BASE_CHAIN) { | ||
| 897 | err = nf_register_hook(&nft_base_chain(chain)->ops); | ||
| 898 | if (err < 0) { | ||
| 899 | free_percpu(basechain->stats); | ||
| 900 | kfree(basechain); | ||
| 901 | return err; | ||
| 902 | } | ||
| 903 | } | ||
| 742 | notify: | 904 | notify: |
| 743 | nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN, | 905 | nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN, |
| 744 | family); | 906 | family); |
| @@ -751,9 +913,10 @@ static void nf_tables_rcu_chain_destroy(struct rcu_head *head) | |||
| 751 | 913 | ||
| 752 | BUG_ON(chain->use > 0); | 914 | BUG_ON(chain->use > 0); |
| 753 | 915 | ||
| 754 | if (chain->flags & NFT_BASE_CHAIN) | 916 | if (chain->flags & NFT_BASE_CHAIN) { |
| 917 | free_percpu(nft_base_chain(chain)->stats); | ||
| 755 | kfree(nft_base_chain(chain)); | 918 | kfree(nft_base_chain(chain)); |
| 756 | else | 919 | } else |
| 757 | kfree(chain); | 920 | kfree(chain); |
| 758 | } | 921 | } |
| 759 | 922 | ||
| @@ -801,13 +964,15 @@ static void nft_ctx_init(struct nft_ctx *ctx, | |||
| 801 | const struct nlmsghdr *nlh, | 964 | const struct nlmsghdr *nlh, |
| 802 | const struct nft_af_info *afi, | 965 | const struct nft_af_info *afi, |
| 803 | const struct nft_table *table, | 966 | const struct nft_table *table, |
| 804 | const struct nft_chain *chain) | 967 | const struct nft_chain *chain, |
| 968 | const struct nlattr * const *nla) | ||
| 805 | { | 969 | { |
| 806 | ctx->skb = skb; | 970 | ctx->skb = skb; |
| 807 | ctx->nlh = nlh; | 971 | ctx->nlh = nlh; |
| 808 | ctx->afi = afi; | 972 | ctx->afi = afi; |
| 809 | ctx->table = table; | 973 | ctx->table = table; |
| 810 | ctx->chain = chain; | 974 | ctx->chain = chain; |
| 975 | ctx->nla = nla; | ||
| 811 | } | 976 | } |
| 812 | 977 | ||
| 813 | /* | 978 | /* |
| @@ -910,7 +1075,8 @@ struct nft_expr_info { | |||
| 910 | struct nlattr *tb[NFT_EXPR_MAXATTR + 1]; | 1075 | struct nlattr *tb[NFT_EXPR_MAXATTR + 1]; |
| 911 | }; | 1076 | }; |
| 912 | 1077 | ||
| 913 | static int nf_tables_expr_parse(const struct nlattr *nla, | 1078 | static int nf_tables_expr_parse(const struct nft_ctx *ctx, |
| 1079 | const struct nlattr *nla, | ||
| 914 | struct nft_expr_info *info) | 1080 | struct nft_expr_info *info) |
| 915 | { | 1081 | { |
| 916 | const struct nft_expr_type *type; | 1082 | const struct nft_expr_type *type; |
| @@ -935,7 +1101,8 @@ static int nf_tables_expr_parse(const struct nlattr *nla, | |||
| 935 | memset(info->tb, 0, sizeof(info->tb[0]) * (type->maxattr + 1)); | 1101 | memset(info->tb, 0, sizeof(info->tb[0]) * (type->maxattr + 1)); |
| 936 | 1102 | ||
| 937 | if (type->select_ops != NULL) { | 1103 | if (type->select_ops != NULL) { |
| 938 | ops = type->select_ops((const struct nlattr * const *)info->tb); | 1104 | ops = type->select_ops(ctx, |
| 1105 | (const struct nlattr * const *)info->tb); | ||
| 939 | if (IS_ERR(ops)) { | 1106 | if (IS_ERR(ops)) { |
| 940 | err = PTR_ERR(ops); | 1107 | err = PTR_ERR(ops); |
| 941 | goto err1; | 1108 | goto err1; |
| @@ -1012,6 +1179,7 @@ static const struct nla_policy nft_rule_policy[NFTA_RULE_MAX + 1] = { | |||
| 1012 | .len = NFT_CHAIN_MAXNAMELEN - 1 }, | 1179 | .len = NFT_CHAIN_MAXNAMELEN - 1 }, |
| 1013 | [NFTA_RULE_HANDLE] = { .type = NLA_U64 }, | 1180 | [NFTA_RULE_HANDLE] = { .type = NLA_U64 }, |
| 1014 | [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED }, | 1181 | [NFTA_RULE_EXPRESSIONS] = { .type = NLA_NESTED }, |
| 1182 | [NFTA_RULE_COMPAT] = { .type = NLA_NESTED }, | ||
| 1015 | }; | 1183 | }; |
| 1016 | 1184 | ||
| 1017 | static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq, | 1185 | static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq, |
| @@ -1269,6 +1437,8 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, | |||
| 1269 | handle = nf_tables_alloc_handle(table); | 1437 | handle = nf_tables_alloc_handle(table); |
| 1270 | } | 1438 | } |
| 1271 | 1439 | ||
| 1440 | nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla); | ||
| 1441 | |||
| 1272 | n = 0; | 1442 | n = 0; |
| 1273 | size = 0; | 1443 | size = 0; |
| 1274 | if (nla[NFTA_RULE_EXPRESSIONS]) { | 1444 | if (nla[NFTA_RULE_EXPRESSIONS]) { |
| @@ -1278,7 +1448,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, | |||
| 1278 | goto err1; | 1448 | goto err1; |
| 1279 | if (n == NFT_RULE_MAXEXPRS) | 1449 | if (n == NFT_RULE_MAXEXPRS) |
| 1280 | goto err1; | 1450 | goto err1; |
| 1281 | err = nf_tables_expr_parse(tmp, &info[n]); | 1451 | err = nf_tables_expr_parse(&ctx, tmp, &info[n]); |
| 1282 | if (err < 0) | 1452 | if (err < 0) |
| 1283 | goto err1; | 1453 | goto err1; |
| 1284 | size += info[n].ops->size; | 1454 | size += info[n].ops->size; |
| @@ -1294,7 +1464,6 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, | |||
| 1294 | rule->handle = handle; | 1464 | rule->handle = handle; |
| 1295 | rule->dlen = size; | 1465 | rule->dlen = size; |
| 1296 | 1466 | ||
| 1297 | nft_ctx_init(&ctx, skb, nlh, afi, table, chain); | ||
| 1298 | expr = nft_expr_first(rule); | 1467 | expr = nft_expr_first(rule); |
| 1299 | for (i = 0; i < n; i++) { | 1468 | for (i = 0; i < n; i++) { |
| 1300 | err = nf_tables_newexpr(&ctx, &info[i], expr); | 1469 | err = nf_tables_newexpr(&ctx, &info[i], expr); |
| @@ -1304,13 +1473,6 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, | |||
| 1304 | expr = nft_expr_next(expr); | 1473 | expr = nft_expr_next(expr); |
| 1305 | } | 1474 | } |
| 1306 | 1475 | ||
| 1307 | /* Register hook when first rule is inserted into a base chain */ | ||
| 1308 | if (list_empty(&chain->rules) && chain->flags & NFT_BASE_CHAIN) { | ||
| 1309 | err = nf_register_hook(&nft_base_chain(chain)->ops); | ||
| 1310 | if (err < 0) | ||
| 1311 | goto err2; | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { | 1476 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { |
| 1315 | list_replace_rcu(&old_rule->list, &rule->list); | 1477 | list_replace_rcu(&old_rule->list, &rule->list); |
| 1316 | nf_tables_rule_destroy(old_rule); | 1478 | nf_tables_rule_destroy(old_rule); |
| @@ -1379,10 +1541,6 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, | |||
| 1379 | } | 1541 | } |
| 1380 | } | 1542 | } |
| 1381 | 1543 | ||
| 1382 | /* Unregister hook when last rule from base chain is deleted */ | ||
| 1383 | if (list_empty(&chain->rules) && chain->flags & NFT_BASE_CHAIN) | ||
| 1384 | nf_unregister_hook(&nft_base_chain(chain)->ops); | ||
| 1385 | |||
| 1386 | return 0; | 1544 | return 0; |
| 1387 | } | 1545 | } |
| 1388 | 1546 | ||
| @@ -1470,7 +1628,7 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, | |||
| 1470 | return PTR_ERR(table); | 1628 | return PTR_ERR(table); |
| 1471 | } | 1629 | } |
| 1472 | 1630 | ||
| 1473 | nft_ctx_init(ctx, skb, nlh, afi, table, NULL); | 1631 | nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); |
| 1474 | return 0; | 1632 | return 0; |
| 1475 | } | 1633 | } |
| 1476 | 1634 | ||
| @@ -1799,7 +1957,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, | |||
| 1799 | if (IS_ERR(table)) | 1957 | if (IS_ERR(table)) |
| 1800 | return PTR_ERR(table); | 1958 | return PTR_ERR(table); |
| 1801 | 1959 | ||
| 1802 | nft_ctx_init(&ctx, skb, nlh, afi, table, NULL); | 1960 | nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla); |
| 1803 | 1961 | ||
| 1804 | set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]); | 1962 | set = nf_tables_set_lookup(table, nla[NFTA_SET_NAME]); |
| 1805 | if (IS_ERR(set)) { | 1963 | if (IS_ERR(set)) { |
| @@ -1987,7 +2145,7 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, | |||
| 1987 | if (IS_ERR(table)) | 2145 | if (IS_ERR(table)) |
| 1988 | return PTR_ERR(table); | 2146 | return PTR_ERR(table); |
| 1989 | 2147 | ||
| 1990 | nft_ctx_init(ctx, skb, nlh, afi, table, NULL); | 2148 | nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); |
| 1991 | return 0; | 2149 | return 0; |
| 1992 | } | 2150 | } |
| 1993 | 2151 | ||
| @@ -2435,23 +2593,27 @@ static int nf_tables_check_loops(const struct nft_ctx *ctx, | |||
| 2435 | { | 2593 | { |
| 2436 | const struct nft_rule *rule; | 2594 | const struct nft_rule *rule; |
| 2437 | const struct nft_expr *expr, *last; | 2595 | const struct nft_expr *expr, *last; |
| 2438 | const struct nft_data *data; | ||
| 2439 | const struct nft_set *set; | 2596 | const struct nft_set *set; |
| 2440 | struct nft_set_binding *binding; | 2597 | struct nft_set_binding *binding; |
| 2441 | struct nft_set_iter iter; | 2598 | struct nft_set_iter iter; |
| 2442 | int err; | ||
| 2443 | 2599 | ||
| 2444 | if (ctx->chain == chain) | 2600 | if (ctx->chain == chain) |
| 2445 | return -ELOOP; | 2601 | return -ELOOP; |
| 2446 | 2602 | ||
| 2447 | list_for_each_entry(rule, &chain->rules, list) { | 2603 | list_for_each_entry(rule, &chain->rules, list) { |
| 2448 | nft_rule_for_each_expr(expr, last, rule) { | 2604 | nft_rule_for_each_expr(expr, last, rule) { |
| 2449 | if (!expr->ops->get_verdict) | 2605 | const struct nft_data *data = NULL; |
| 2606 | int err; | ||
| 2607 | |||
| 2608 | if (!expr->ops->validate) | ||
| 2450 | continue; | 2609 | continue; |
| 2451 | 2610 | ||
| 2452 | data = expr->ops->get_verdict(expr); | 2611 | err = expr->ops->validate(ctx, expr, &data); |
| 2612 | if (err < 0) | ||
| 2613 | return err; | ||
| 2614 | |||
| 2453 | if (data == NULL) | 2615 | if (data == NULL) |
| 2454 | break; | 2616 | continue; |
| 2455 | 2617 | ||
| 2456 | switch (data->verdict) { | 2618 | switch (data->verdict) { |
| 2457 | case NFT_JUMP: | 2619 | case NFT_JUMP: |
diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 9aede59ed2d7..e51a45c12128 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c | |||
| @@ -60,27 +60,34 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr, | |||
| 60 | return true; | 60 | return true; |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | unsigned int nft_do_chain(const struct nf_hook_ops *ops, | 63 | struct nft_jumpstack { |
| 64 | struct sk_buff *skb, | 64 | const struct nft_chain *chain; |
| 65 | const struct net_device *in, | 65 | const struct nft_rule *rule; |
| 66 | const struct net_device *out, | 66 | }; |
| 67 | int (*okfn)(struct sk_buff *)) | 67 | |
| 68 | static inline void | ||
| 69 | nft_chain_stats(const struct nft_chain *this, const struct nft_pktinfo *pkt, | ||
| 70 | struct nft_jumpstack *jumpstack, unsigned int stackptr) | ||
| 71 | { | ||
| 72 | struct nft_stats __percpu *stats; | ||
| 73 | const struct nft_chain *chain = stackptr ? jumpstack[0].chain : this; | ||
| 74 | |||
| 75 | rcu_read_lock_bh(); | ||
| 76 | stats = rcu_dereference(nft_base_chain(chain)->stats); | ||
| 77 | __this_cpu_inc(stats->pkts); | ||
| 78 | __this_cpu_add(stats->bytes, pkt->skb->len); | ||
| 79 | rcu_read_unlock_bh(); | ||
| 80 | } | ||
| 81 | |||
| 82 | unsigned int | ||
| 83 | nft_do_chain_pktinfo(struct nft_pktinfo *pkt, const struct nf_hook_ops *ops) | ||
| 68 | { | 84 | { |
| 69 | const struct nft_chain *chain = ops->priv; | 85 | const struct nft_chain *chain = ops->priv; |
| 70 | const struct nft_rule *rule; | 86 | const struct nft_rule *rule; |
| 71 | const struct nft_expr *expr, *last; | 87 | const struct nft_expr *expr, *last; |
| 72 | struct nft_data data[NFT_REG_MAX + 1]; | 88 | struct nft_data data[NFT_REG_MAX + 1]; |
| 73 | const struct nft_pktinfo pkt = { | ||
| 74 | .skb = skb, | ||
| 75 | .in = in, | ||
| 76 | .out = out, | ||
| 77 | .hooknum = ops->hooknum, | ||
| 78 | }; | ||
| 79 | unsigned int stackptr = 0; | 89 | unsigned int stackptr = 0; |
| 80 | struct { | 90 | struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; |
| 81 | const struct nft_chain *chain; | ||
| 82 | const struct nft_rule *rule; | ||
| 83 | } jumpstack[NFT_JUMP_STACK_SIZE]; | ||
| 84 | 91 | ||
| 85 | do_chain: | 92 | do_chain: |
| 86 | rule = list_entry(&chain->rules, struct nft_rule, list); | 93 | rule = list_entry(&chain->rules, struct nft_rule, list); |
| @@ -91,8 +98,8 @@ next_rule: | |||
| 91 | if (expr->ops == &nft_cmp_fast_ops) | 98 | if (expr->ops == &nft_cmp_fast_ops) |
| 92 | nft_cmp_fast_eval(expr, data); | 99 | nft_cmp_fast_eval(expr, data); |
| 93 | else if (expr->ops != &nft_payload_fast_ops || | 100 | else if (expr->ops != &nft_payload_fast_ops || |
| 94 | !nft_payload_fast_eval(expr, data, &pkt)) | 101 | !nft_payload_fast_eval(expr, data, pkt)) |
| 95 | expr->ops->eval(expr, data, &pkt); | 102 | expr->ops->eval(expr, data, pkt); |
| 96 | 103 | ||
| 97 | if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE) | 104 | if (data[NFT_REG_VERDICT].verdict != NFT_CONTINUE) |
| 98 | break; | 105 | break; |
| @@ -135,10 +142,11 @@ next_rule: | |||
| 135 | rule = jumpstack[stackptr].rule; | 142 | rule = jumpstack[stackptr].rule; |
| 136 | goto next_rule; | 143 | goto next_rule; |
| 137 | } | 144 | } |
| 145 | nft_chain_stats(chain, pkt, jumpstack, stackptr); | ||
| 138 | 146 | ||
| 139 | return NF_ACCEPT; | 147 | return nft_base_chain(chain)->policy; |
| 140 | } | 148 | } |
| 141 | EXPORT_SYMBOL_GPL(nft_do_chain); | 149 | EXPORT_SYMBOL_GPL(nft_do_chain_pktinfo); |
| 142 | 150 | ||
| 143 | int __init nf_tables_core_module_init(void) | 151 | int __init nf_tables_core_module_init(void) |
| 144 | { | 152 | { |
diff --git a/net/netfilter/nft_cmp.c b/net/netfilter/nft_cmp.c index 37134f3e84fb..954925db414d 100644 --- a/net/netfilter/nft_cmp.c +++ b/net/netfilter/nft_cmp.c | |||
| @@ -162,7 +162,8 @@ const struct nft_expr_ops nft_cmp_fast_ops = { | |||
| 162 | .dump = nft_cmp_fast_dump, | 162 | .dump = nft_cmp_fast_dump, |
| 163 | }; | 163 | }; |
| 164 | 164 | ||
| 165 | static const struct nft_expr_ops *nft_cmp_select_ops(const struct nlattr * const tb[]) | 165 | static const struct nft_expr_ops * |
| 166 | nft_cmp_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) | ||
| 166 | { | 167 | { |
| 167 | struct nft_data_desc desc; | 168 | struct nft_data_desc desc; |
| 168 | struct nft_data data; | 169 | struct nft_data data; |
diff --git a/net/netfilter/nft_compat.c b/net/netfilter/nft_compat.c new file mode 100644 index 000000000000..4811f762e060 --- /dev/null +++ b/net/netfilter/nft_compat.c | |||
| @@ -0,0 +1,768 @@ | |||
| 1 | /* | ||
| 2 | * (C) 2012-2013 by 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 | * This software has been sponsored by Sophos Astaro <http://www.sophos.com> | ||
| 9 | */ | ||
| 10 | |||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/init.h> | ||
| 13 | #include <linux/module.h> | ||
| 14 | #include <linux/netlink.h> | ||
| 15 | #include <linux/netfilter.h> | ||
| 16 | #include <linux/netfilter/nfnetlink.h> | ||
| 17 | #include <linux/netfilter/nf_tables.h> | ||
| 18 | #include <linux/netfilter/nf_tables_compat.h> | ||
| 19 | #include <linux/netfilter/x_tables.h> | ||
| 20 | #include <linux/netfilter_ipv4/ip_tables.h> | ||
| 21 | #include <linux/netfilter_ipv6/ip6_tables.h> | ||
| 22 | #include <asm/uaccess.h> /* for set_fs */ | ||
| 23 | #include <net/netfilter/nf_tables.h> | ||
| 24 | |||
| 25 | union nft_entry { | ||
| 26 | struct ipt_entry e4; | ||
| 27 | struct ip6t_entry e6; | ||
| 28 | }; | ||
| 29 | |||
| 30 | static inline void | ||
| 31 | nft_compat_set_par(struct xt_action_param *par, void *xt, const void *xt_info) | ||
| 32 | { | ||
| 33 | par->target = xt; | ||
| 34 | par->targinfo = xt_info; | ||
| 35 | par->hotdrop = false; | ||
| 36 | } | ||
| 37 | |||
| 38 | static void nft_target_eval(const struct nft_expr *expr, | ||
| 39 | struct nft_data data[NFT_REG_MAX + 1], | ||
| 40 | const struct nft_pktinfo *pkt) | ||
| 41 | { | ||
| 42 | void *info = nft_expr_priv(expr); | ||
| 43 | struct xt_target *target = expr->ops->data; | ||
| 44 | struct sk_buff *skb = pkt->skb; | ||
| 45 | int ret; | ||
| 46 | |||
| 47 | nft_compat_set_par((struct xt_action_param *)&pkt->xt, target, info); | ||
| 48 | |||
| 49 | ret = target->target(skb, &pkt->xt); | ||
| 50 | |||
| 51 | if (pkt->xt.hotdrop) | ||
| 52 | ret = NF_DROP; | ||
| 53 | |||
| 54 | switch(ret) { | ||
| 55 | case XT_CONTINUE: | ||
| 56 | data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; | ||
| 57 | break; | ||
| 58 | default: | ||
| 59 | data[NFT_REG_VERDICT].verdict = ret; | ||
| 60 | break; | ||
| 61 | } | ||
| 62 | return; | ||
| 63 | } | ||
| 64 | |||
| 65 | static const struct nla_policy nft_target_policy[NFTA_TARGET_MAX + 1] = { | ||
| 66 | [NFTA_TARGET_NAME] = { .type = NLA_NUL_STRING }, | ||
| 67 | [NFTA_TARGET_REV] = { .type = NLA_U32 }, | ||
| 68 | [NFTA_TARGET_INFO] = { .type = NLA_BINARY }, | ||
| 69 | }; | ||
| 70 | |||
| 71 | static void | ||
| 72 | nft_target_set_tgchk_param(struct xt_tgchk_param *par, | ||
| 73 | const struct nft_ctx *ctx, | ||
| 74 | struct xt_target *target, void *info, | ||
| 75 | union nft_entry *entry, u8 proto, bool inv) | ||
| 76 | { | ||
| 77 | par->net = &init_net; | ||
| 78 | par->table = ctx->table->name; | ||
| 79 | switch (ctx->afi->family) { | ||
| 80 | case AF_INET: | ||
| 81 | entry->e4.ip.proto = proto; | ||
| 82 | entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0; | ||
| 83 | break; | ||
| 84 | case AF_INET6: | ||
| 85 | entry->e6.ipv6.proto = proto; | ||
| 86 | entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; | ||
| 87 | break; | ||
| 88 | } | ||
| 89 | par->entryinfo = entry; | ||
| 90 | par->target = target; | ||
| 91 | par->targinfo = info; | ||
| 92 | if (ctx->chain->flags & NFT_BASE_CHAIN) { | ||
| 93 | const struct nft_base_chain *basechain = | ||
| 94 | nft_base_chain(ctx->chain); | ||
| 95 | const struct nf_hook_ops *ops = &basechain->ops; | ||
| 96 | |||
| 97 | par->hook_mask = 1 << ops->hooknum; | ||
| 98 | } | ||
| 99 | par->family = ctx->afi->family; | ||
| 100 | } | ||
| 101 | |||
| 102 | static void target_compat_from_user(struct xt_target *t, void *in, void *out) | ||
| 103 | { | ||
| 104 | #ifdef CONFIG_COMPAT | ||
| 105 | if (t->compat_from_user) { | ||
| 106 | int pad; | ||
| 107 | |||
| 108 | t->compat_from_user(out, in); | ||
| 109 | pad = XT_ALIGN(t->targetsize) - t->targetsize; | ||
| 110 | if (pad > 0) | ||
| 111 | memset(out + t->targetsize, 0, pad); | ||
| 112 | } else | ||
| 113 | #endif | ||
| 114 | memcpy(out, in, XT_ALIGN(t->targetsize)); | ||
| 115 | } | ||
| 116 | |||
| 117 | static inline int nft_compat_target_offset(struct xt_target *target) | ||
| 118 | { | ||
| 119 | #ifdef CONFIG_COMPAT | ||
| 120 | return xt_compat_target_offset(target); | ||
| 121 | #else | ||
| 122 | return 0; | ||
| 123 | #endif | ||
| 124 | } | ||
| 125 | |||
| 126 | static const struct nla_policy nft_rule_compat_policy[NFTA_RULE_COMPAT_MAX + 1] = { | ||
| 127 | [NFTA_RULE_COMPAT_PROTO] = { .type = NLA_U32 }, | ||
| 128 | [NFTA_RULE_COMPAT_FLAGS] = { .type = NLA_U32 }, | ||
| 129 | }; | ||
| 130 | |||
| 131 | static u8 nft_parse_compat(const struct nlattr *attr, bool *inv) | ||
| 132 | { | ||
| 133 | struct nlattr *tb[NFTA_RULE_COMPAT_MAX+1]; | ||
| 134 | u32 flags; | ||
| 135 | int err; | ||
| 136 | |||
| 137 | err = nla_parse_nested(tb, NFTA_RULE_COMPAT_MAX, attr, | ||
| 138 | nft_rule_compat_policy); | ||
| 139 | if (err < 0) | ||
| 140 | return err; | ||
| 141 | |||
| 142 | if (!tb[NFTA_RULE_COMPAT_PROTO] || !tb[NFTA_RULE_COMPAT_FLAGS]) | ||
| 143 | return -EINVAL; | ||
| 144 | |||
| 145 | flags = ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_FLAGS])); | ||
| 146 | if (flags & ~NFT_RULE_COMPAT_F_MASK) | ||
| 147 | return -EINVAL; | ||
| 148 | if (flags & NFT_RULE_COMPAT_F_INV) | ||
| 149 | *inv = true; | ||
| 150 | |||
| 151 | return ntohl(nla_get_be32(tb[NFTA_RULE_COMPAT_PROTO])); | ||
| 152 | } | ||
| 153 | |||
| 154 | static int | ||
| 155 | nft_target_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | ||
| 156 | const struct nlattr * const tb[]) | ||
| 157 | { | ||
| 158 | void *info = nft_expr_priv(expr); | ||
| 159 | struct xt_target *target = expr->ops->data; | ||
| 160 | struct xt_tgchk_param par; | ||
| 161 | size_t size = XT_ALIGN(nla_len(tb[NFTA_TARGET_INFO])); | ||
| 162 | u8 proto = 0; | ||
| 163 | bool inv = false; | ||
| 164 | union nft_entry e = {}; | ||
| 165 | int ret; | ||
| 166 | |||
| 167 | target_compat_from_user(target, nla_data(tb[NFTA_TARGET_INFO]), info); | ||
| 168 | |||
| 169 | if (ctx->nla[NFTA_RULE_COMPAT]) | ||
| 170 | proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv); | ||
| 171 | |||
| 172 | nft_target_set_tgchk_param(&par, ctx, target, info, &e, proto, inv); | ||
| 173 | |||
| 174 | ret = xt_check_target(&par, size, proto, inv); | ||
| 175 | if (ret < 0) | ||
| 176 | goto err; | ||
| 177 | |||
| 178 | /* The standard target cannot be used */ | ||
| 179 | if (target->target == NULL) { | ||
| 180 | ret = -EINVAL; | ||
| 181 | goto err; | ||
| 182 | } | ||
| 183 | |||
| 184 | return 0; | ||
| 185 | err: | ||
| 186 | module_put(target->me); | ||
| 187 | return ret; | ||
| 188 | } | ||
| 189 | |||
| 190 | static void | ||
| 191 | nft_target_destroy(const struct nft_expr *expr) | ||
| 192 | { | ||
| 193 | struct xt_target *target = expr->ops->data; | ||
| 194 | |||
| 195 | module_put(target->me); | ||
| 196 | } | ||
| 197 | |||
| 198 | static int | ||
| 199 | target_dump_info(struct sk_buff *skb, const struct xt_target *t, const void *in) | ||
| 200 | { | ||
| 201 | int ret; | ||
| 202 | |||
| 203 | #ifdef CONFIG_COMPAT | ||
| 204 | if (t->compat_to_user) { | ||
| 205 | mm_segment_t old_fs; | ||
| 206 | void *out; | ||
| 207 | |||
| 208 | out = kmalloc(XT_ALIGN(t->targetsize), GFP_ATOMIC); | ||
| 209 | if (out == NULL) | ||
| 210 | return -ENOMEM; | ||
| 211 | |||
| 212 | /* We want to reuse existing compat_to_user */ | ||
| 213 | old_fs = get_fs(); | ||
| 214 | set_fs(KERNEL_DS); | ||
| 215 | t->compat_to_user(out, in); | ||
| 216 | set_fs(old_fs); | ||
| 217 | ret = nla_put(skb, NFTA_TARGET_INFO, XT_ALIGN(t->targetsize), out); | ||
| 218 | kfree(out); | ||
| 219 | } else | ||
| 220 | #endif | ||
| 221 | ret = nla_put(skb, NFTA_TARGET_INFO, XT_ALIGN(t->targetsize), in); | ||
| 222 | |||
| 223 | return ret; | ||
| 224 | } | ||
| 225 | |||
| 226 | static int nft_target_dump(struct sk_buff *skb, const struct nft_expr *expr) | ||
| 227 | { | ||
| 228 | const struct xt_target *target = expr->ops->data; | ||
| 229 | void *info = nft_expr_priv(expr); | ||
| 230 | |||
| 231 | if (nla_put_string(skb, NFTA_TARGET_NAME, target->name) || | ||
| 232 | nla_put_be32(skb, NFTA_TARGET_REV, htonl(target->revision)) || | ||
| 233 | target_dump_info(skb, target, info)) | ||
| 234 | goto nla_put_failure; | ||
| 235 | |||
| 236 | return 0; | ||
| 237 | |||
| 238 | nla_put_failure: | ||
| 239 | return -1; | ||
| 240 | } | ||
| 241 | |||
| 242 | static int nft_target_validate(const struct nft_ctx *ctx, | ||
| 243 | const struct nft_expr *expr, | ||
| 244 | const struct nft_data **data) | ||
| 245 | { | ||
| 246 | struct xt_target *target = expr->ops->data; | ||
| 247 | unsigned int hook_mask = 0; | ||
| 248 | |||
| 249 | if (ctx->chain->flags & NFT_BASE_CHAIN) { | ||
| 250 | const struct nft_base_chain *basechain = | ||
| 251 | nft_base_chain(ctx->chain); | ||
| 252 | const struct nf_hook_ops *ops = &basechain->ops; | ||
| 253 | |||
| 254 | hook_mask = 1 << ops->hooknum; | ||
| 255 | if (hook_mask & target->hooks) | ||
| 256 | return 0; | ||
| 257 | |||
| 258 | /* This target is being called from an invalid chain */ | ||
| 259 | return -EINVAL; | ||
| 260 | } | ||
| 261 | return 0; | ||
| 262 | } | ||
| 263 | |||
| 264 | static void nft_match_eval(const struct nft_expr *expr, | ||
| 265 | struct nft_data data[NFT_REG_MAX + 1], | ||
| 266 | const struct nft_pktinfo *pkt) | ||
| 267 | { | ||
| 268 | void *info = nft_expr_priv(expr); | ||
| 269 | struct xt_match *match = expr->ops->data; | ||
| 270 | struct sk_buff *skb = pkt->skb; | ||
| 271 | bool ret; | ||
| 272 | |||
| 273 | nft_compat_set_par((struct xt_action_param *)&pkt->xt, match, info); | ||
| 274 | |||
| 275 | ret = match->match(skb, (struct xt_action_param *)&pkt->xt); | ||
| 276 | |||
| 277 | if (pkt->xt.hotdrop) { | ||
| 278 | data[NFT_REG_VERDICT].verdict = NF_DROP; | ||
| 279 | return; | ||
| 280 | } | ||
| 281 | |||
| 282 | switch(ret) { | ||
| 283 | case true: | ||
| 284 | data[NFT_REG_VERDICT].verdict = NFT_CONTINUE; | ||
| 285 | break; | ||
| 286 | case false: | ||
| 287 | data[NFT_REG_VERDICT].verdict = NFT_BREAK; | ||
| 288 | break; | ||
| 289 | } | ||
| 290 | } | ||
| 291 | |||
| 292 | static const struct nla_policy nft_match_policy[NFTA_MATCH_MAX + 1] = { | ||
| 293 | [NFTA_MATCH_NAME] = { .type = NLA_NUL_STRING }, | ||
| 294 | [NFTA_MATCH_REV] = { .type = NLA_U32 }, | ||
| 295 | [NFTA_MATCH_INFO] = { .type = NLA_BINARY }, | ||
| 296 | }; | ||
| 297 | |||
| 298 | /* struct xt_mtchk_param and xt_tgchk_param look very similar */ | ||
| 299 | static void | ||
| 300 | nft_match_set_mtchk_param(struct xt_mtchk_param *par, const struct nft_ctx *ctx, | ||
| 301 | struct xt_match *match, void *info, | ||
| 302 | union nft_entry *entry, u8 proto, bool inv) | ||
| 303 | { | ||
| 304 | par->net = &init_net; | ||
| 305 | par->table = ctx->table->name; | ||
| 306 | switch (ctx->afi->family) { | ||
| 307 | case AF_INET: | ||
| 308 | entry->e4.ip.proto = proto; | ||
| 309 | entry->e4.ip.invflags = inv ? IPT_INV_PROTO : 0; | ||
| 310 | break; | ||
| 311 | case AF_INET6: | ||
| 312 | entry->e6.ipv6.proto = proto; | ||
| 313 | entry->e6.ipv6.invflags = inv ? IP6T_INV_PROTO : 0; | ||
| 314 | break; | ||
| 315 | } | ||
| 316 | par->entryinfo = entry; | ||
| 317 | par->match = match; | ||
| 318 | par->matchinfo = info; | ||
| 319 | if (ctx->chain->flags & NFT_BASE_CHAIN) { | ||
| 320 | const struct nft_base_chain *basechain = | ||
| 321 | nft_base_chain(ctx->chain); | ||
| 322 | const struct nf_hook_ops *ops = &basechain->ops; | ||
| 323 | |||
| 324 | par->hook_mask = 1 << ops->hooknum; | ||
| 325 | } | ||
| 326 | par->family = ctx->afi->family; | ||
| 327 | } | ||
| 328 | |||
| 329 | static void match_compat_from_user(struct xt_match *m, void *in, void *out) | ||
| 330 | { | ||
| 331 | #ifdef CONFIG_COMPAT | ||
| 332 | if (m->compat_from_user) { | ||
| 333 | int pad; | ||
| 334 | |||
| 335 | m->compat_from_user(out, in); | ||
| 336 | pad = XT_ALIGN(m->matchsize) - m->matchsize; | ||
| 337 | if (pad > 0) | ||
| 338 | memset(out + m->matchsize, 0, pad); | ||
| 339 | } else | ||
| 340 | #endif | ||
| 341 | memcpy(out, in, XT_ALIGN(m->matchsize)); | ||
| 342 | } | ||
| 343 | |||
| 344 | static int | ||
| 345 | nft_match_init(const struct nft_ctx *ctx, const struct nft_expr *expr, | ||
| 346 | const struct nlattr * const tb[]) | ||
| 347 | { | ||
| 348 | void *info = nft_expr_priv(expr); | ||
| 349 | struct xt_match *match = expr->ops->data; | ||
| 350 | struct xt_mtchk_param par; | ||
| 351 | size_t size = XT_ALIGN(nla_len(tb[NFTA_MATCH_INFO])); | ||
| 352 | u8 proto = 0; | ||
| 353 | bool inv = false; | ||
| 354 | union nft_entry e = {}; | ||
| 355 | int ret; | ||
| 356 | |||
| 357 | match_compat_from_user(match, nla_data(tb[NFTA_MATCH_INFO]), info); | ||
| 358 | |||
| 359 | if (ctx->nla[NFTA_RULE_COMPAT]) | ||
| 360 | proto = nft_parse_compat(ctx->nla[NFTA_RULE_COMPAT], &inv); | ||
| 361 | |||
| 362 | nft_match_set_mtchk_param(&par, ctx, match, info, &e, proto, inv); | ||
| 363 | |||
| 364 | ret = xt_check_match(&par, size, proto, inv); | ||
| 365 | if (ret < 0) | ||
| 366 | goto err; | ||
| 367 | |||
| 368 | return 0; | ||
| 369 | err: | ||
| 370 | module_put(match->me); | ||
| 371 | return ret; | ||
| 372 | } | ||
| 373 | |||
| 374 | static void | ||
| 375 | nft_match_destroy(const struct nft_expr *expr) | ||
| 376 | { | ||
| 377 | struct xt_match *match = expr->ops->data; | ||
| 378 | |||
| 379 | module_put(match->me); | ||
| 380 | } | ||
| 381 | |||
| 382 | static int | ||
| 383 | match_dump_info(struct sk_buff *skb, const struct xt_match *m, const void *in) | ||
| 384 | { | ||
| 385 | int ret; | ||
| 386 | |||
| 387 | #ifdef CONFIG_COMPAT | ||
| 388 | if (m->compat_to_user) { | ||
| 389 | mm_segment_t old_fs; | ||
| 390 | void *out; | ||
| 391 | |||
| 392 | out = kmalloc(XT_ALIGN(m->matchsize), GFP_ATOMIC); | ||
| 393 | if (out == NULL) | ||
| 394 | return -ENOMEM; | ||
| 395 | |||
| 396 | /* We want to reuse existing compat_to_user */ | ||
| 397 | old_fs = get_fs(); | ||
| 398 | set_fs(KERNEL_DS); | ||
| 399 | m->compat_to_user(out, in); | ||
| 400 | set_fs(old_fs); | ||
| 401 | ret = nla_put(skb, NFTA_MATCH_INFO, XT_ALIGN(m->matchsize), out); | ||
| 402 | kfree(out); | ||
| 403 | } else | ||
| 404 | #endif | ||
| 405 | ret = nla_put(skb, NFTA_MATCH_INFO, XT_ALIGN(m->matchsize), in); | ||
| 406 | |||
| 407 | return ret; | ||
| 408 | } | ||
| 409 | |||
| 410 | static inline int nft_compat_match_offset(struct xt_match *match) | ||
| 411 | { | ||
| 412 | #ifdef CONFIG_COMPAT | ||
| 413 | return xt_compat_match_offset(match); | ||
| 414 | #else | ||
| 415 | return 0; | ||
| 416 | #endif | ||
| 417 | } | ||
| 418 | |||
| 419 | static int nft_match_dump(struct sk_buff *skb, const struct nft_expr *expr) | ||
| 420 | { | ||
| 421 | void *info = nft_expr_priv(expr); | ||
| 422 | struct xt_match *match = expr->ops->data; | ||
| 423 | |||
| 424 | if (nla_put_string(skb, NFTA_MATCH_NAME, match->name) || | ||
| 425 | nla_put_be32(skb, NFTA_MATCH_REV, htonl(match->revision)) || | ||
| 426 | match_dump_info(skb, match, info)) | ||
| 427 | goto nla_put_failure; | ||
| 428 | |||
| 429 | return 0; | ||
| 430 | |||
| 431 | nla_put_failure: | ||
| 432 | return -1; | ||
| 433 | } | ||
| 434 | |||
| 435 | static int nft_match_validate(const struct nft_ctx *ctx, | ||
| 436 | const struct nft_expr *expr, | ||
| 437 | const struct nft_data **data) | ||
| 438 | { | ||
| 439 | struct xt_match *match = expr->ops->data; | ||
| 440 | unsigned int hook_mask = 0; | ||
| 441 | |||
| 442 | if (ctx->chain->flags & NFT_BASE_CHAIN) { | ||
| 443 | const struct nft_base_chain *basechain = | ||
| 444 | nft_base_chain(ctx->chain); | ||
| 445 | const struct nf_hook_ops *ops = &basechain->ops; | ||
| 446 | |||
| 447 | hook_mask = 1 << ops->hooknum; | ||
| 448 | if (hook_mask & match->hooks) | ||
| 449 | return 0; | ||
| 450 | |||
| 451 | /* This match is being called from an invalid chain */ | ||
| 452 | return -EINVAL; | ||
| 453 | } | ||
| 454 | return 0; | ||
| 455 | } | ||
| 456 | |||
| 457 | static int | ||
| 458 | nfnl_compat_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, | ||
| 459 | int event, u16 family, const char *name, | ||
| 460 | int rev, int target) | ||
| 461 | { | ||
| 462 | struct nlmsghdr *nlh; | ||
| 463 | struct nfgenmsg *nfmsg; | ||
| 464 | unsigned int flags = portid ? NLM_F_MULTI : 0; | ||
| 465 | |||
| 466 | event |= NFNL_SUBSYS_NFT_COMPAT << 8; | ||
| 467 | nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); | ||
| 468 | if (nlh == NULL) | ||
| 469 | goto nlmsg_failure; | ||
| 470 | |||
| 471 | nfmsg = nlmsg_data(nlh); | ||
| 472 | nfmsg->nfgen_family = family; | ||
| 473 | nfmsg->version = NFNETLINK_V0; | ||
| 474 | nfmsg->res_id = 0; | ||
| 475 | |||
| 476 | if (nla_put_string(skb, NFTA_COMPAT_NAME, name) || | ||
| 477 | nla_put_be32(skb, NFTA_COMPAT_REV, htonl(rev)) || | ||
| 478 | nla_put_be32(skb, NFTA_COMPAT_TYPE, htonl(target))) | ||
| 479 | goto nla_put_failure; | ||
| 480 | |||
| 481 | nlmsg_end(skb, nlh); | ||
| 482 | return skb->len; | ||
| 483 | |||
| 484 | nlmsg_failure: | ||
| 485 | nla_put_failure: | ||
| 486 | nlmsg_cancel(skb, nlh); | ||
| 487 | return -1; | ||
| 488 | } | ||
| 489 | |||
| 490 | static int | ||
| 491 | nfnl_compat_get(struct sock *nfnl, struct sk_buff *skb, | ||
| 492 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) | ||
| 493 | { | ||
| 494 | int ret = 0, target; | ||
| 495 | struct nfgenmsg *nfmsg; | ||
| 496 | const char *fmt; | ||
| 497 | const char *name; | ||
| 498 | u32 rev; | ||
| 499 | struct sk_buff *skb2; | ||
| 500 | |||
| 501 | if (tb[NFTA_COMPAT_NAME] == NULL || | ||
| 502 | tb[NFTA_COMPAT_REV] == NULL || | ||
| 503 | tb[NFTA_COMPAT_TYPE] == NULL) | ||
| 504 | return -EINVAL; | ||
| 505 | |||
| 506 | name = nla_data(tb[NFTA_COMPAT_NAME]); | ||
| 507 | rev = ntohl(nla_get_be32(tb[NFTA_COMPAT_REV])); | ||
| 508 | target = ntohl(nla_get_be32(tb[NFTA_COMPAT_TYPE])); | ||
| 509 | |||
| 510 | nfmsg = nlmsg_data(nlh); | ||
| 511 | |||
| 512 | switch(nfmsg->nfgen_family) { | ||
| 513 | case AF_INET: | ||
| 514 | fmt = "ipt_%s"; | ||
| 515 | break; | ||
| 516 | case AF_INET6: | ||
| 517 | fmt = "ip6t_%s"; | ||
| 518 | break; | ||
| 519 | default: | ||
| 520 | pr_err("nft_compat: unsupported protocol %d\n", | ||
| 521 | nfmsg->nfgen_family); | ||
| 522 | return -EINVAL; | ||
| 523 | } | ||
| 524 | |||
| 525 | try_then_request_module(xt_find_revision(nfmsg->nfgen_family, name, | ||
| 526 | rev, target, &ret), | ||
| 527 | fmt, name); | ||
| 528 | |||
| 529 | if (ret < 0) | ||
| 530 | return ret; | ||
| 531 | |||
| 532 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
| 533 | if (skb2 == NULL) | ||
| 534 | return -ENOMEM; | ||
| 535 | |||
| 536 | /* include the best revision for this extension in the message */ | ||
| 537 | if (nfnl_compat_fill_info(skb2, NETLINK_CB(skb).portid, | ||
| 538 | nlh->nlmsg_seq, | ||
| 539 | NFNL_MSG_TYPE(nlh->nlmsg_type), | ||
| 540 | NFNL_MSG_COMPAT_GET, | ||
| 541 | nfmsg->nfgen_family, | ||
| 542 | name, ret, target) <= 0) { | ||
| 543 | kfree_skb(skb2); | ||
| 544 | return -ENOSPC; | ||
| 545 | } | ||
| 546 | |||
| 547 | ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, | ||
| 548 | MSG_DONTWAIT); | ||
| 549 | if (ret > 0) | ||
| 550 | ret = 0; | ||
| 551 | |||
| 552 | return ret == -EAGAIN ? -ENOBUFS : ret; | ||
| 553 | } | ||
| 554 | |||
| 555 | static const struct nla_policy nfnl_compat_policy_get[NFTA_COMPAT_MAX+1] = { | ||
| 556 | [NFTA_COMPAT_NAME] = { .type = NLA_NUL_STRING, | ||
| 557 | .len = NFT_COMPAT_NAME_MAX-1 }, | ||
| 558 | [NFTA_COMPAT_REV] = { .type = NLA_U32 }, | ||
| 559 | [NFTA_COMPAT_TYPE] = { .type = NLA_U32 }, | ||
| 560 | }; | ||
| 561 | |||
| 562 | static const struct nfnl_callback nfnl_nft_compat_cb[NFNL_MSG_COMPAT_MAX] = { | ||
| 563 | [NFNL_MSG_COMPAT_GET] = { .call = nfnl_compat_get, | ||
| 564 | .attr_count = NFTA_COMPAT_MAX, | ||
| 565 | .policy = nfnl_compat_policy_get }, | ||
| 566 | }; | ||
| 567 | |||
| 568 | static const struct nfnetlink_subsystem nfnl_compat_subsys = { | ||
| 569 | .name = "nft-compat", | ||
| 570 | .subsys_id = NFNL_SUBSYS_NFT_COMPAT, | ||
| 571 | .cb_count = NFNL_MSG_COMPAT_MAX, | ||
| 572 | .cb = nfnl_nft_compat_cb, | ||
| 573 | }; | ||
| 574 | |||
| 575 | static LIST_HEAD(nft_match_list); | ||
| 576 | |||
| 577 | struct nft_xt { | ||
| 578 | struct list_head head; | ||
| 579 | struct nft_expr_ops ops; | ||
| 580 | }; | ||
| 581 | |||
| 582 | static struct nft_expr_type nft_match_type; | ||
| 583 | |||
| 584 | static const struct nft_expr_ops * | ||
| 585 | nft_match_select_ops(const struct nft_ctx *ctx, | ||
| 586 | const struct nlattr * const tb[]) | ||
| 587 | { | ||
| 588 | struct nft_xt *nft_match; | ||
| 589 | struct xt_match *match; | ||
| 590 | char *mt_name; | ||
| 591 | __u32 rev, family; | ||
| 592 | |||
| 593 | if (tb[NFTA_MATCH_NAME] == NULL || | ||
| 594 | tb[NFTA_MATCH_REV] == NULL || | ||
| 595 | tb[NFTA_MATCH_INFO] == NULL) | ||
| 596 | return ERR_PTR(-EINVAL); | ||
| 597 | |||
| 598 | mt_name = nla_data(tb[NFTA_MATCH_NAME]); | ||
| 599 | rev = ntohl(nla_get_be32(tb[NFTA_MATCH_REV])); | ||
| 600 | family = ctx->afi->family; | ||
| 601 | |||
| 602 | /* Re-use the existing match if it's already loaded. */ | ||
| 603 | list_for_each_entry(nft_match, &nft_match_list, head) { | ||
| 604 | struct xt_match *match = nft_match->ops.data; | ||
| 605 | |||
| 606 | if (strcmp(match->name, mt_name) == 0 && | ||
| 607 | match->revision == rev && match->family == family) | ||
| 608 | return &nft_match->ops; | ||
| 609 | } | ||
| 610 | |||
| 611 | match = xt_request_find_match(family, mt_name, rev); | ||
| 612 | if (IS_ERR(match)) | ||
| 613 | return ERR_PTR(-ENOENT); | ||
| 614 | |||
| 615 | /* This is the first time we use this match, allocate operations */ | ||
| 616 | nft_match = kzalloc(sizeof(struct nft_xt), GFP_KERNEL); | ||
| 617 | if (nft_match == NULL) | ||
| 618 | return ERR_PTR(-ENOMEM); | ||
| 619 | |||
| 620 | nft_match->ops.type = &nft_match_type; | ||
| 621 | nft_match->ops.size = NFT_EXPR_SIZE(XT_ALIGN(match->matchsize) + | ||
| 622 | nft_compat_match_offset(match)); | ||
| 623 | nft_match->ops.eval = nft_match_eval; | ||
| 624 | nft_match->ops.init = nft_match_init; | ||
| 625 | nft_match->ops.destroy = nft_match_destroy; | ||
| 626 | nft_match->ops.dump = nft_match_dump; | ||
| 627 | nft_match->ops.validate = nft_match_validate; | ||
| 628 | nft_match->ops.data = match; | ||
| 629 | |||
| 630 | list_add(&nft_match->head, &nft_match_list); | ||
| 631 | |||
| 632 | return &nft_match->ops; | ||
| 633 | } | ||
| 634 | |||
| 635 | static void nft_match_release(void) | ||
| 636 | { | ||
| 637 | struct nft_xt *nft_match; | ||
| 638 | |||
| 639 | list_for_each_entry(nft_match, &nft_match_list, head) | ||
| 640 | kfree(nft_match); | ||
| 641 | } | ||
| 642 | |||
| 643 | static struct nft_expr_type nft_match_type __read_mostly = { | ||
| 644 | .name = "match", | ||
| 645 | .select_ops = nft_match_select_ops, | ||
| 646 | .policy = nft_match_policy, | ||
| 647 | .maxattr = NFTA_MATCH_MAX, | ||
| 648 | .owner = THIS_MODULE, | ||
| 649 | }; | ||
| 650 | |||
| 651 | static LIST_HEAD(nft_target_list); | ||
| 652 | |||
| 653 | static struct nft_expr_type nft_target_type; | ||
| 654 | |||
| 655 | static const struct nft_expr_ops * | ||
| 656 | nft_target_select_ops(const struct nft_ctx *ctx, | ||
| 657 | const struct nlattr * const tb[]) | ||
| 658 | { | ||
| 659 | struct nft_xt *nft_target; | ||
| 660 | struct xt_target *target; | ||
| 661 | char *tg_name; | ||
| 662 | __u32 rev, family; | ||
| 663 | |||
| 664 | if (tb[NFTA_TARGET_NAME] == NULL || | ||
| 665 | tb[NFTA_TARGET_REV] == NULL || | ||
| 666 | tb[NFTA_TARGET_INFO] == NULL) | ||
| 667 | return ERR_PTR(-EINVAL); | ||
| 668 | |||
| 669 | tg_name = nla_data(tb[NFTA_TARGET_NAME]); | ||
| 670 | rev = ntohl(nla_get_be32(tb[NFTA_TARGET_REV])); | ||
| 671 | family = ctx->afi->family; | ||
| 672 | |||
| 673 | /* Re-use the existing target if it's already loaded. */ | ||
| 674 | list_for_each_entry(nft_target, &nft_match_list, head) { | ||
| 675 | struct xt_target *target = nft_target->ops.data; | ||
| 676 | |||
| 677 | if (strcmp(target->name, tg_name) == 0 && | ||
| 678 | target->revision == rev && target->family == family) | ||
| 679 | return &nft_target->ops; | ||
| 680 | } | ||
| 681 | |||
| 682 | target = xt_request_find_target(family, tg_name, rev); | ||
| 683 | if (IS_ERR(target)) | ||
| 684 | return ERR_PTR(-ENOENT); | ||
| 685 | |||
| 686 | /* This is the first time we use this target, allocate operations */ | ||
| 687 | nft_target = kzalloc(sizeof(struct nft_xt), GFP_KERNEL); | ||
| 688 | if (nft_target == NULL) | ||
| 689 | return ERR_PTR(-ENOMEM); | ||
| 690 | |||
| 691 | nft_target->ops.type = &nft_target_type; | ||
| 692 | nft_target->ops.size = NFT_EXPR_SIZE(XT_ALIGN(target->targetsize) + | ||
| 693 | nft_compat_target_offset(target)); | ||
| 694 | nft_target->ops.eval = nft_target_eval; | ||
| 695 | nft_target->ops.init = nft_target_init; | ||
| 696 | nft_target->ops.destroy = nft_target_destroy; | ||
| 697 | nft_target->ops.dump = nft_target_dump; | ||
| 698 | nft_target->ops.validate = nft_target_validate; | ||
| 699 | nft_target->ops.data = target; | ||
| 700 | |||
| 701 | list_add(&nft_target->head, &nft_target_list); | ||
| 702 | |||
| 703 | return &nft_target->ops; | ||
| 704 | } | ||
| 705 | |||
| 706 | static void nft_target_release(void) | ||
| 707 | { | ||
| 708 | struct nft_xt *nft_target; | ||
| 709 | |||
| 710 | list_for_each_entry(nft_target, &nft_target_list, head) | ||
| 711 | kfree(nft_target); | ||
| 712 | } | ||
| 713 | |||
| 714 | static struct nft_expr_type nft_target_type __read_mostly = { | ||
| 715 | .name = "target", | ||
| 716 | .select_ops = nft_target_select_ops, | ||
| 717 | .policy = nft_target_policy, | ||
| 718 | .maxattr = NFTA_TARGET_MAX, | ||
| 719 | .owner = THIS_MODULE, | ||
| 720 | }; | ||
| 721 | |||
| 722 | static int __init nft_compat_module_init(void) | ||
| 723 | { | ||
| 724 | int ret; | ||
| 725 | |||
| 726 | ret = nft_register_expr(&nft_match_type); | ||
| 727 | if (ret < 0) | ||
| 728 | return ret; | ||
| 729 | |||
| 730 | ret = nft_register_expr(&nft_target_type); | ||
| 731 | if (ret < 0) | ||
| 732 | goto err_match; | ||
| 733 | |||
| 734 | ret = nfnetlink_subsys_register(&nfnl_compat_subsys); | ||
| 735 | if (ret < 0) { | ||
| 736 | pr_err("nft_compat: cannot register with nfnetlink.\n"); | ||
| 737 | goto err_target; | ||
| 738 | } | ||
| 739 | |||
| 740 | pr_info("nf_tables_compat: (c) 2012 Pablo Neira Ayuso <pablo@netfilter.org>\n"); | ||
| 741 | |||
| 742 | return ret; | ||
| 743 | |||
| 744 | err_target: | ||
| 745 | nft_unregister_expr(&nft_target_type); | ||
| 746 | err_match: | ||
| 747 | nft_unregister_expr(&nft_match_type); | ||
| 748 | return ret; | ||
| 749 | } | ||
| 750 | |||
| 751 | static void __exit nft_compat_module_exit(void) | ||
| 752 | { | ||
| 753 | nfnetlink_subsys_unregister(&nfnl_compat_subsys); | ||
| 754 | nft_unregister_expr(&nft_target_type); | ||
| 755 | nft_unregister_expr(&nft_match_type); | ||
| 756 | nft_match_release(); | ||
| 757 | nft_target_release(); | ||
| 758 | } | ||
| 759 | |||
| 760 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_NFT_COMPAT); | ||
| 761 | |||
| 762 | module_init(nft_compat_module_init); | ||
| 763 | module_exit(nft_compat_module_exit); | ||
| 764 | |||
| 765 | MODULE_LICENSE("GPL"); | ||
| 766 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); | ||
| 767 | MODULE_ALIAS_NFT_EXPR("match"); | ||
| 768 | MODULE_ALIAS_NFT_EXPR("target"); | ||
diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c index 1bfeeaf865b6..f169501f1ad4 100644 --- a/net/netfilter/nft_immediate.c +++ b/net/netfilter/nft_immediate.c | |||
| @@ -90,14 +90,16 @@ nla_put_failure: | |||
| 90 | return -1; | 90 | return -1; |
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | static const struct nft_data *nft_immediate_get_verdict(const struct nft_expr *expr) | 93 | static int nft_immediate_validate(const struct nft_ctx *ctx, |
| 94 | const struct nft_expr *expr, | ||
| 95 | const struct nft_data **data) | ||
| 94 | { | 96 | { |
| 95 | const struct nft_immediate_expr *priv = nft_expr_priv(expr); | 97 | const struct nft_immediate_expr *priv = nft_expr_priv(expr); |
| 96 | 98 | ||
| 97 | if (priv->dreg == NFT_REG_VERDICT) | 99 | if (priv->dreg == NFT_REG_VERDICT) |
| 98 | return &priv->data; | 100 | *data = &priv->data; |
| 99 | else | 101 | |
| 100 | return NULL; | 102 | return 0; |
| 101 | } | 103 | } |
| 102 | 104 | ||
| 103 | static struct nft_expr_type nft_imm_type; | 105 | static struct nft_expr_type nft_imm_type; |
| @@ -108,7 +110,7 @@ static const struct nft_expr_ops nft_imm_ops = { | |||
| 108 | .init = nft_immediate_init, | 110 | .init = nft_immediate_init, |
| 109 | .destroy = nft_immediate_destroy, | 111 | .destroy = nft_immediate_destroy, |
| 110 | .dump = nft_immediate_dump, | 112 | .dump = nft_immediate_dump, |
| 111 | .get_verdict = nft_immediate_get_verdict, | 113 | .validate = nft_immediate_validate, |
| 112 | }; | 114 | }; |
| 113 | 115 | ||
| 114 | static struct nft_expr_type nft_imm_type __read_mostly = { | 116 | static struct nft_expr_type nft_imm_type __read_mostly = { |
diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 7cf13f7e1e94..bc8bdb2c1ba7 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c | |||
| @@ -107,7 +107,9 @@ const struct nft_expr_ops nft_payload_fast_ops = { | |||
| 107 | .dump = nft_payload_dump, | 107 | .dump = nft_payload_dump, |
| 108 | }; | 108 | }; |
| 109 | 109 | ||
| 110 | static const struct nft_expr_ops *nft_payload_select_ops(const struct nlattr * const tb[]) | 110 | static const struct nft_expr_ops * |
| 111 | nft_payload_select_ops(const struct nft_ctx *ctx, | ||
| 112 | const struct nlattr * const tb[]) | ||
| 111 | { | 113 | { |
| 112 | enum nft_payload_bases base; | 114 | enum nft_payload_bases base; |
| 113 | unsigned int offset, len; | 115 | unsigned int offset, len; |
