Commit 8af750d7 authored by David S. Miller's avatar David S. Miller

Merge git://git.kernel.org/pub/scm/linux/kernel/git/pablo/nftables

Pablo Neira Ayuso says:

====================
Netfilter/nftables updates for net-next

The following patchset contains Netfilter/nftables updates for net-next,
most relevantly they are:

1) Add set element update notification via netlink, from Arturo Borrero.

2) Put all object updates in one single message batch that is sent to
   kernel-space. Before this patch only rules where included in the batch.
   This series also introduces the generic transaction infrastructure so
   updates to all objects (tables, chains, rules and sets) are applied in
   an all-or-nothing fashion, these series from me.

3) Defer release of objects via call_rcu to reduce the time required to
   commit changes. The assumption is that all objects are destroyed in
   reverse order to ensure that dependencies betweem them are fulfilled
   (ie. rules and sets are destroyed first, then chains, and finally
   tables).

4) Allow to match by bridge port name, from Tomasz Bursztyka. This series
   include two patches to prepare this new feature.

5) Implement the proper set selection based on the characteristics of the
   data. The new infrastructure also allows you to specify your preferences
   in terms of memory and computational complexity so the underlying set
   type is also selected according to your needs, from Patrick McHardy.

6) Several cleanup patches for nft expressions, including one minor possible
   compilation breakage due to missing mark support, also from Patrick.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 758bd61a c7c32e72
...@@ -72,21 +72,23 @@ static inline void nft_data_debug(const struct nft_data *data) ...@@ -72,21 +72,23 @@ static inline void nft_data_debug(const struct nft_data *data)
* struct nft_ctx - nf_tables rule/set context * struct nft_ctx - nf_tables rule/set context
* *
* @net: net namespace * @net: net namespace
* @skb: netlink skb
* @nlh: netlink message header
* @afi: address family info * @afi: address family info
* @table: the table the chain is contained in * @table: the table the chain is contained in
* @chain: the chain the rule is contained in * @chain: the chain the rule is contained in
* @nla: netlink attributes * @nla: netlink attributes
* @portid: netlink portID of the original message
* @seq: netlink sequence number
* @report: notify via unicast netlink message
*/ */
struct nft_ctx { struct nft_ctx {
struct net *net; struct net *net;
const struct sk_buff *skb; struct nft_af_info *afi;
const struct nlmsghdr *nlh; struct nft_table *table;
const struct nft_af_info *afi; struct nft_chain *chain;
const struct nft_table *table;
const struct nft_chain *chain;
const struct nlattr * const *nla; const struct nlattr * const *nla;
u32 portid;
u32 seq;
bool report;
}; };
struct nft_data_desc { struct nft_data_desc {
...@@ -145,6 +147,44 @@ struct nft_set_iter { ...@@ -145,6 +147,44 @@ struct nft_set_iter {
const struct nft_set_elem *elem); const struct nft_set_elem *elem);
}; };
/**
* struct nft_set_desc - description of set elements
*
* @klen: key length
* @dlen: data length
* @size: number of set elements
*/
struct nft_set_desc {
unsigned int klen;
unsigned int dlen;
unsigned int size;
};
/**
* enum nft_set_class - performance class
*
* @NFT_LOOKUP_O_1: constant, O(1)
* @NFT_LOOKUP_O_LOG_N: logarithmic, O(log N)
* @NFT_LOOKUP_O_N: linear, O(N)
*/
enum nft_set_class {
NFT_SET_CLASS_O_1,
NFT_SET_CLASS_O_LOG_N,
NFT_SET_CLASS_O_N,
};
/**
* struct nft_set_estimate - estimation of memory and performance
* characteristics
*
* @size: required memory
* @class: lookup performance class
*/
struct nft_set_estimate {
unsigned int size;
enum nft_set_class class;
};
/** /**
* struct nft_set_ops - nf_tables set operations * struct nft_set_ops - nf_tables set operations
* *
...@@ -174,7 +214,11 @@ struct nft_set_ops { ...@@ -174,7 +214,11 @@ struct nft_set_ops {
struct nft_set_iter *iter); struct nft_set_iter *iter);
unsigned int (*privsize)(const struct nlattr * const nla[]); unsigned int (*privsize)(const struct nlattr * const nla[]);
bool (*estimate)(const struct nft_set_desc *desc,
u32 features,
struct nft_set_estimate *est);
int (*init)(const struct nft_set *set, int (*init)(const struct nft_set *set,
const struct nft_set_desc *desc,
const struct nlattr * const nla[]); const struct nlattr * const nla[]);
void (*destroy)(const struct nft_set *set); void (*destroy)(const struct nft_set *set);
...@@ -194,6 +238,8 @@ void nft_unregister_set(struct nft_set_ops *ops); ...@@ -194,6 +238,8 @@ void nft_unregister_set(struct nft_set_ops *ops);
* @name: name of the set * @name: name of the set
* @ktype: key type (numeric type defined by userspace, not used in the kernel) * @ktype: key type (numeric type defined by userspace, not used in the kernel)
* @dtype: data type (verdict or numeric type defined by userspace) * @dtype: data type (verdict or numeric type defined by userspace)
* @size: maximum set size
* @nelems: number of elements
* @ops: set ops * @ops: set ops
* @flags: set flags * @flags: set flags
* @klen: key length * @klen: key length
...@@ -206,6 +252,8 @@ struct nft_set { ...@@ -206,6 +252,8 @@ struct nft_set {
char name[IFNAMSIZ]; char name[IFNAMSIZ];
u32 ktype; u32 ktype;
u32 dtype; u32 dtype;
u32 size;
u32 nelems;
/* runtime data below here */ /* runtime data below here */
const struct nft_set_ops *ops ____cacheline_aligned; const struct nft_set_ops *ops ____cacheline_aligned;
u16 flags; u16 flags;
...@@ -222,6 +270,8 @@ static inline void *nft_set_priv(const struct nft_set *set) ...@@ -222,6 +270,8 @@ static inline void *nft_set_priv(const struct nft_set *set)
struct nft_set *nf_tables_set_lookup(const struct nft_table *table, struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
const struct nlattr *nla); const struct nlattr *nla);
struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
const struct nlattr *nla);
/** /**
* struct nft_set_binding - nf_tables set binding * struct nft_set_binding - nf_tables set binding
...@@ -341,18 +391,75 @@ struct nft_rule { ...@@ -341,18 +391,75 @@ struct nft_rule {
}; };
/** /**
* struct nft_rule_trans - nf_tables rule update in transaction * struct nft_trans - nf_tables object update in transaction
* *
* @rcu_head: rcu head to defer release of transaction data
* @list: used internally * @list: used internally
* @ctx: rule context * @msg_type: message type
* @rule: rule that needs to be updated * @ctx: transaction context
* @data: internal information related to the transaction
*/ */
struct nft_rule_trans { struct nft_trans {
struct rcu_head rcu_head;
struct list_head list; struct list_head list;
int msg_type;
struct nft_ctx ctx; struct nft_ctx ctx;
char data[0];
};
struct nft_trans_rule {
struct nft_rule *rule; struct nft_rule *rule;
}; };
#define nft_trans_rule(trans) \
(((struct nft_trans_rule *)trans->data)->rule)
struct nft_trans_set {
struct nft_set *set;
u32 set_id;
};
#define nft_trans_set(trans) \
(((struct nft_trans_set *)trans->data)->set)
#define nft_trans_set_id(trans) \
(((struct nft_trans_set *)trans->data)->set_id)
struct nft_trans_chain {
bool update;
char name[NFT_CHAIN_MAXNAMELEN];
struct nft_stats __percpu *stats;
u8 policy;
};
#define nft_trans_chain_update(trans) \
(((struct nft_trans_chain *)trans->data)->update)
#define nft_trans_chain_name(trans) \
(((struct nft_trans_chain *)trans->data)->name)
#define nft_trans_chain_stats(trans) \
(((struct nft_trans_chain *)trans->data)->stats)
#define nft_trans_chain_policy(trans) \
(((struct nft_trans_chain *)trans->data)->policy)
struct nft_trans_table {
bool update;
bool enable;
};
#define nft_trans_table_update(trans) \
(((struct nft_trans_table *)trans->data)->update)
#define nft_trans_table_enable(trans) \
(((struct nft_trans_table *)trans->data)->enable)
struct nft_trans_elem {
struct nft_set *set;
struct nft_set_elem elem;
};
#define nft_trans_elem_set(trans) \
(((struct nft_trans_elem *)trans->data)->set)
#define nft_trans_elem(trans) \
(((struct nft_trans_elem *)trans->data)->elem)
static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule) static inline struct nft_expr *nft_expr_first(const struct nft_rule *rule)
{ {
return (struct nft_expr *)&rule->data[0]; return (struct nft_expr *)&rule->data[0];
...@@ -385,6 +492,7 @@ static inline void *nft_userdata(const struct nft_rule *rule) ...@@ -385,6 +492,7 @@ static inline void *nft_userdata(const struct nft_rule *rule)
enum nft_chain_flags { enum nft_chain_flags {
NFT_BASE_CHAIN = 0x1, NFT_BASE_CHAIN = 0x1,
NFT_CHAIN_INACTIVE = 0x2,
}; };
/** /**
......
#ifndef _NFT_META_H_
#define _NFT_META_H_
struct nft_meta {
enum nft_meta_keys key:8;
union {
enum nft_registers dreg:8;
enum nft_registers sreg:8;
};
};
extern const struct nla_policy nft_meta_policy[];
int nft_meta_get_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[]);
int nft_meta_set_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[]);
int nft_meta_get_dump(struct sk_buff *skb,
const struct nft_expr *expr);
int nft_meta_set_dump(struct sk_buff *skb,
const struct nft_expr *expr);
void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt);
void nft_meta_set_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt);
#endif
...@@ -211,6 +211,29 @@ enum nft_set_flags { ...@@ -211,6 +211,29 @@ enum nft_set_flags {
NFT_SET_MAP = 0x8, NFT_SET_MAP = 0x8,
}; };
/**
* enum nft_set_policies - set selection policy
*
* @NFT_SET_POL_PERFORMANCE: prefer high performance over low memory use
* @NFT_SET_POL_MEMORY: prefer low memory use over high performance
*/
enum nft_set_policies {
NFT_SET_POL_PERFORMANCE,
NFT_SET_POL_MEMORY,
};
/**
* enum nft_set_desc_attributes - set element description
*
* @NFTA_SET_DESC_SIZE: number of elements in set (NLA_U32)
*/
enum nft_set_desc_attributes {
NFTA_SET_DESC_UNSPEC,
NFTA_SET_DESC_SIZE,
__NFTA_SET_DESC_MAX
};
#define NFTA_SET_DESC_MAX (__NFTA_SET_DESC_MAX - 1)
/** /**
* enum nft_set_attributes - nf_tables set netlink attributes * enum nft_set_attributes - nf_tables set netlink attributes
* *
...@@ -221,6 +244,9 @@ enum nft_set_flags { ...@@ -221,6 +244,9 @@ enum nft_set_flags {
* @NFTA_SET_KEY_LEN: key data length (NLA_U32) * @NFTA_SET_KEY_LEN: key data length (NLA_U32)
* @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32) * @NFTA_SET_DATA_TYPE: mapping data type (NLA_U32)
* @NFTA_SET_DATA_LEN: mapping data length (NLA_U32) * @NFTA_SET_DATA_LEN: mapping data length (NLA_U32)
* @NFTA_SET_POLICY: selection policy (NLA_U32)
* @NFTA_SET_DESC: set description (NLA_NESTED)
* @NFTA_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
*/ */
enum nft_set_attributes { enum nft_set_attributes {
NFTA_SET_UNSPEC, NFTA_SET_UNSPEC,
...@@ -231,6 +257,9 @@ enum nft_set_attributes { ...@@ -231,6 +257,9 @@ enum nft_set_attributes {
NFTA_SET_KEY_LEN, NFTA_SET_KEY_LEN,
NFTA_SET_DATA_TYPE, NFTA_SET_DATA_TYPE,
NFTA_SET_DATA_LEN, NFTA_SET_DATA_LEN,
NFTA_SET_POLICY,
NFTA_SET_DESC,
NFTA_SET_ID,
__NFTA_SET_MAX __NFTA_SET_MAX
}; };
#define NFTA_SET_MAX (__NFTA_SET_MAX - 1) #define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
...@@ -266,12 +295,14 @@ enum nft_set_elem_attributes { ...@@ -266,12 +295,14 @@ enum nft_set_elem_attributes {
* @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING) * @NFTA_SET_ELEM_LIST_TABLE: table of the set to be changed (NLA_STRING)
* @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING) * @NFTA_SET_ELEM_LIST_SET: name of the set to be changed (NLA_STRING)
* @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes) * @NFTA_SET_ELEM_LIST_ELEMENTS: list of set elements (NLA_NESTED: nft_set_elem_attributes)
* @NFTA_SET_ELEM_LIST_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
*/ */
enum nft_set_elem_list_attributes { enum nft_set_elem_list_attributes {
NFTA_SET_ELEM_LIST_UNSPEC, NFTA_SET_ELEM_LIST_UNSPEC,
NFTA_SET_ELEM_LIST_TABLE, NFTA_SET_ELEM_LIST_TABLE,
NFTA_SET_ELEM_LIST_SET, NFTA_SET_ELEM_LIST_SET,
NFTA_SET_ELEM_LIST_ELEMENTS, NFTA_SET_ELEM_LIST_ELEMENTS,
NFTA_SET_ELEM_LIST_SET_ID,
__NFTA_SET_ELEM_LIST_MAX __NFTA_SET_ELEM_LIST_MAX
}; };
#define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1) #define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1)
...@@ -457,12 +488,14 @@ enum nft_cmp_attributes { ...@@ -457,12 +488,14 @@ enum nft_cmp_attributes {
* @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING) * @NFTA_LOOKUP_SET: name of the set where to look for (NLA_STRING)
* @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers) * @NFTA_LOOKUP_SREG: source register of the data to look for (NLA_U32: nft_registers)
* @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers) * @NFTA_LOOKUP_DREG: destination register (NLA_U32: nft_registers)
* @NFTA_LOOKUP_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
*/ */
enum nft_lookup_attributes { enum nft_lookup_attributes {
NFTA_LOOKUP_UNSPEC, NFTA_LOOKUP_UNSPEC,
NFTA_LOOKUP_SET, NFTA_LOOKUP_SET,
NFTA_LOOKUP_SREG, NFTA_LOOKUP_SREG,
NFTA_LOOKUP_DREG, NFTA_LOOKUP_DREG,
NFTA_LOOKUP_SET_ID,
__NFTA_LOOKUP_MAX __NFTA_LOOKUP_MAX
}; };
#define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1) #define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1)
...@@ -536,6 +569,8 @@ enum nft_exthdr_attributes { ...@@ -536,6 +569,8 @@ enum nft_exthdr_attributes {
* @NFT_META_SECMARK: packet secmark (skb->secmark) * @NFT_META_SECMARK: packet secmark (skb->secmark)
* @NFT_META_NFPROTO: netfilter protocol * @NFT_META_NFPROTO: netfilter protocol
* @NFT_META_L4PROTO: layer 4 protocol number * @NFT_META_L4PROTO: layer 4 protocol number
* @NFT_META_BRI_IIFNAME: packet input bridge interface name
* @NFT_META_BRI_OIFNAME: packet output bridge interface name
*/ */
enum nft_meta_keys { enum nft_meta_keys {
NFT_META_LEN, NFT_META_LEN,
...@@ -555,6 +590,8 @@ enum nft_meta_keys { ...@@ -555,6 +590,8 @@ enum nft_meta_keys {
NFT_META_SECMARK, NFT_META_SECMARK,
NFT_META_NFPROTO, NFT_META_NFPROTO,
NFT_META_L4PROTO, NFT_META_L4PROTO,
NFT_META_BRI_IIFNAME,
NFT_META_BRI_OIFNAME,
}; };
/** /**
......
...@@ -16,4 +16,4 @@ bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o ...@@ -16,4 +16,4 @@ bridge-$(CONFIG_BRIDGE_IGMP_SNOOPING) += br_multicast.o br_mdb.o
bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/ obj-$(CONFIG_BRIDGE_NETFILTER) += netfilter/
...@@ -2,13 +2,25 @@ ...@@ -2,13 +2,25 @@
# Bridge netfilter configuration # Bridge netfilter configuration
# #
# #
config NF_TABLES_BRIDGE menuconfig NF_TABLES_BRIDGE
depends on NF_TABLES depends on NF_TABLES
select BRIDGE_NETFILTER
tristate "Ethernet Bridge nf_tables support" tristate "Ethernet Bridge nf_tables support"
if NF_TABLES_BRIDGE
config NFT_BRIDGE_META
tristate "Netfilter nf_table bridge meta support"
depends on NFT_META
help
Add support for bridge dedicated meta key.
endif # NF_TABLES_BRIDGE
menuconfig BRIDGE_NF_EBTABLES menuconfig BRIDGE_NF_EBTABLES
tristate "Ethernet Bridge tables (ebtables) support" tristate "Ethernet Bridge tables (ebtables) support"
depends on BRIDGE && NETFILTER depends on BRIDGE && NETFILTER
select BRIDGE_NETFILTER
select NETFILTER_XTABLES select NETFILTER_XTABLES
help help
ebtables is a general, extensible frame/packet identification ebtables is a general, extensible frame/packet identification
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# #
obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o obj-$(CONFIG_NF_TABLES_BRIDGE) += nf_tables_bridge.o
obj-$(CONFIG_NFT_BRIDGE_META) += nft_meta_bridge.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o obj-$(CONFIG_BRIDGE_NF_EBTABLES) += ebtables.o
......
/*
* Copyright (c) 2014 Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/netlink.h>
#include <linux/netfilter.h>
#include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_meta.h>
#include "../br_private.h"
static void nft_meta_bridge_get_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{
const struct nft_meta *priv = nft_expr_priv(expr);
const struct net_device *in = pkt->in, *out = pkt->out;
struct nft_data *dest = &data[priv->dreg];
const struct net_bridge_port *p;
switch (priv->key) {
case NFT_META_BRI_IIFNAME:
if (in == NULL || (p = br_port_get_rcu(in)) == NULL)
goto err;
break;
case NFT_META_BRI_OIFNAME:
if (out == NULL || (p = br_port_get_rcu(out)) == NULL)
goto err;
break;
default:
goto out;
}
strncpy((char *)dest->data, p->br->dev->name, sizeof(dest->data));
return;
out:
return nft_meta_get_eval(expr, data, pkt);
err:
data[NFT_REG_VERDICT].verdict = NFT_BREAK;
}
static int nft_meta_bridge_get_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_meta *priv = nft_expr_priv(expr);
int err;
priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
switch (priv->key) {
case NFT_META_BRI_IIFNAME:
case NFT_META_BRI_OIFNAME:
break;
default:
return nft_meta_get_init(ctx, expr, tb);
}
priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG]));
err = nft_validate_output_register(priv->dreg);
if (err < 0)
return err;
err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
if (err < 0)
return err;
return 0;
}
static struct nft_expr_type nft_meta_bridge_type;
static const struct nft_expr_ops nft_meta_bridge_get_ops = {
.type = &nft_meta_bridge_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_bridge_get_eval,
.init = nft_meta_bridge_get_init,
.dump = nft_meta_get_dump,
};
static const struct nft_expr_ops nft_meta_bridge_set_ops = {
.type = &nft_meta_bridge_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_set_eval,
.init = nft_meta_set_init,
.dump = nft_meta_set_dump,
};
static const struct nft_expr_ops *
nft_meta_bridge_select_ops(const struct nft_ctx *ctx,
const struct nlattr * const tb[])
{
if (tb[NFTA_META_KEY] == NULL)
return ERR_PTR(-EINVAL);
if (tb[NFTA_META_DREG] && tb[NFTA_META_SREG])
return ERR_PTR(-EINVAL);
if (tb[NFTA_META_DREG])
return &nft_meta_bridge_get_ops;
if (tb[NFTA_META_SREG])
return &nft_meta_bridge_set_ops;
return ERR_PTR(-EINVAL);
}
static struct nft_expr_type nft_meta_bridge_type __read_mostly = {
.family = NFPROTO_BRIDGE,
.name = "meta",
.select_ops = &nft_meta_bridge_select_ops,
.policy = nft_meta_policy,
.maxattr = NFTA_META_MAX,
.owner = THIS_MODULE,
};
static int __init nft_meta_bridge_module_init(void)
{
return nft_register_expr(&nft_meta_bridge_type);
}
static void __exit nft_meta_bridge_module_exit(void)
{
nft_unregister_expr(&nft_meta_bridge_type);
}
module_init(nft_meta_bridge_module_init);
module_exit(nft_meta_bridge_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>");
MODULE_ALIAS_NFT_AF_EXPR(AF_BRIDGE, "meta");
...@@ -88,6 +88,45 @@ nf_tables_afinfo_lookup(struct net *net, int family, bool autoload) ...@@ -88,6 +88,45 @@ nf_tables_afinfo_lookup(struct net *net, int family, bool autoload)
return ERR_PTR(-EAFNOSUPPORT); return ERR_PTR(-EAFNOSUPPORT);
} }
static void nft_ctx_init(struct nft_ctx *ctx,
const struct sk_buff *skb,
const struct nlmsghdr *nlh,
struct nft_af_info *afi,
struct nft_table *table,
struct nft_chain *chain,
const struct nlattr * const *nla)
{
ctx->net = sock_net(skb->sk);
ctx->afi = afi;
ctx->table = table;
ctx->chain = chain;
ctx->nla = nla;
ctx->portid = NETLINK_CB(skb).portid;
ctx->report = nlmsg_report(nlh);
ctx->seq = nlh->nlmsg_seq;
}
static struct nft_trans *nft_trans_alloc(struct nft_ctx *ctx, int msg_type,
u32 size)
{
struct nft_trans *trans;
trans = kzalloc(sizeof(struct nft_trans) + size, GFP_KERNEL);
if (trans == NULL)
return NULL;
trans->msg_type = msg_type;
trans->ctx = *ctx;
return trans;
}
static void nft_trans_destroy(struct nft_trans *trans)
{
list_del(&trans->list);
kfree(trans);
}
/* /*
* Tables * Tables
*/ */
...@@ -197,20 +236,13 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq, ...@@ -197,20 +236,13 @@ static int nf_tables_fill_table_info(struct sk_buff *skb, u32 portid, u32 seq,
return -1; return -1;
} }
static int nf_tables_table_notify(const struct sk_buff *oskb, static int nf_tables_table_notify(const struct nft_ctx *ctx, int event)
const struct nlmsghdr *nlh,
const struct nft_table *table,
int event, int family)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
u32 seq = nlh ? nlh->nlmsg_seq : 0;
struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
bool report;
int err; int err;
report = nlh ? nlmsg_report(nlh) : false; if (!ctx->report &&
if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
return 0; return 0;
err = -ENOBUFS; err = -ENOBUFS;
...@@ -218,18 +250,20 @@ static int nf_tables_table_notify(const struct sk_buff *oskb, ...@@ -218,18 +250,20 @@ static int nf_tables_table_notify(const struct sk_buff *oskb,
if (skb == NULL) if (skb == NULL)
goto err; goto err;
err = nf_tables_fill_table_info(skb, portid, seq, event, 0, err = nf_tables_fill_table_info(skb, ctx->portid, ctx->seq, event, 0,
family, table); ctx->afi->family, ctx->table);
if (err < 0) { if (err < 0) {
kfree_skb(skb); kfree_skb(skb);
goto err; goto err;
} }
err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
GFP_KERNEL); ctx->report, GFP_KERNEL);
err: err:
if (err < 0) if (err < 0) {
nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
err);
}
return err; return err;
} }
...@@ -269,6 +303,9 @@ static int nf_tables_dump_tables(struct sk_buff *skb, ...@@ -269,6 +303,9 @@ static int nf_tables_dump_tables(struct sk_buff *skb,
return skb->len; return skb->len;
} }
/* Internal table flags */
#define NFT_TABLE_INACTIVE (1 << 15)
static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
...@@ -295,6 +332,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb, ...@@ -295,6 +332,8 @@ static int nf_tables_gettable(struct sock *nlsk, struct sk_buff *skb,
table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
if (table->flags & NFT_TABLE_INACTIVE)
return -ENOENT;
skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb2) if (!skb2)
...@@ -343,7 +382,7 @@ static int nf_tables_table_enable(const struct nft_af_info *afi, ...@@ -343,7 +382,7 @@ static int nf_tables_table_enable(const struct nft_af_info *afi,
return err; return err;
} }
static int nf_tables_table_disable(const struct nft_af_info *afi, static void nf_tables_table_disable(const struct nft_af_info *afi,
struct nft_table *table) struct nft_table *table)
{ {
struct nft_chain *chain; struct nft_chain *chain;
...@@ -353,45 +392,63 @@ static int nf_tables_table_disable(const struct nft_af_info *afi, ...@@ -353,45 +392,63 @@ static int nf_tables_table_disable(const struct nft_af_info *afi,
nf_unregister_hooks(nft_base_chain(chain)->ops, nf_unregister_hooks(nft_base_chain(chain)->ops,
afi->nops); afi->nops);
} }
return 0;
} }
static int nf_tables_updtable(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_updtable(struct nft_ctx *ctx)
const struct nlmsghdr *nlh,
const struct nlattr * const nla[],
struct nft_af_info *afi, struct nft_table *table)
{ {
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); struct nft_trans *trans;
int family = nfmsg->nfgen_family, ret = 0; u32 flags;
int ret = 0;
if (nla[NFTA_TABLE_FLAGS]) { if (!ctx->nla[NFTA_TABLE_FLAGS])
u32 flags; return 0;
flags = ntohl(nla_get_be32(nla[NFTA_TABLE_FLAGS])); flags = ntohl(nla_get_be32(ctx->nla[NFTA_TABLE_FLAGS]));
if (flags & ~NFT_TABLE_F_DORMANT) if (flags & ~NFT_TABLE_F_DORMANT)
return -EINVAL; return -EINVAL;
trans = nft_trans_alloc(ctx, NFT_MSG_NEWTABLE,
sizeof(struct nft_trans_table));
if (trans == NULL)
return -ENOMEM;
if ((flags & NFT_TABLE_F_DORMANT) && if ((flags & NFT_TABLE_F_DORMANT) &&
!(table->flags & NFT_TABLE_F_DORMANT)) { !(ctx->table->flags & NFT_TABLE_F_DORMANT)) {
ret = nf_tables_table_disable(afi, table); nft_trans_table_enable(trans) = false;
if (ret >= 0) } else if (!(flags & NFT_TABLE_F_DORMANT) &&
table->flags |= NFT_TABLE_F_DORMANT; ctx->table->flags & NFT_TABLE_F_DORMANT) {
} else if (!(flags & NFT_TABLE_F_DORMANT) && ret = nf_tables_table_enable(ctx->afi, ctx->table);
table->flags & NFT_TABLE_F_DORMANT) { if (ret >= 0) {
ret = nf_tables_table_enable(afi, table); ctx->table->flags &= ~NFT_TABLE_F_DORMANT;
if (ret >= 0) nft_trans_table_enable(trans) = true;
table->flags &= ~NFT_TABLE_F_DORMANT;
} }
if (ret < 0)
goto err;
} }
if (ret < 0)
goto err;
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family); nft_trans_table_update(trans) = true;
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0;
err: err:
nft_trans_destroy(trans);
return ret; return ret;
} }
static int nft_trans_table_add(struct nft_ctx *ctx, int msg_type)
{
struct nft_trans *trans;
trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_table));
if (trans == NULL)
return -ENOMEM;
if (msg_type == NFT_MSG_NEWTABLE)
ctx->table->flags |= NFT_TABLE_INACTIVE;
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0;
}
static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
...@@ -403,6 +460,8 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, ...@@ -403,6 +460,8 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
u32 flags = 0; u32 flags = 0;
struct nft_ctx ctx;
int err;
afi = nf_tables_afinfo_lookup(net, family, true); afi = nf_tables_afinfo_lookup(net, family, true);
if (IS_ERR(afi)) if (IS_ERR(afi))
...@@ -417,11 +476,15 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, ...@@ -417,11 +476,15 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
} }
if (table != NULL) { if (table != NULL) {
if (table->flags & NFT_TABLE_INACTIVE)
return -ENOENT;
if (nlh->nlmsg_flags & NLM_F_EXCL) if (nlh->nlmsg_flags & NLM_F_EXCL)
return -EEXIST; return -EEXIST;
if (nlh->nlmsg_flags & NLM_F_REPLACE) if (nlh->nlmsg_flags & NLM_F_REPLACE)
return -EOPNOTSUPP; return -EOPNOTSUPP;
return nf_tables_updtable(nlsk, skb, nlh, nla, afi, table);
nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
return nf_tables_updtable(&ctx);
} }
if (nla[NFTA_TABLE_FLAGS]) { if (nla[NFTA_TABLE_FLAGS]) {
...@@ -444,8 +507,14 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb, ...@@ -444,8 +507,14 @@ static int nf_tables_newtable(struct sock *nlsk, struct sk_buff *skb,
INIT_LIST_HEAD(&table->sets); INIT_LIST_HEAD(&table->sets);
table->flags = flags; table->flags = flags;
nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE);
if (err < 0) {
kfree(table);
module_put(afi->owner);
return err;
}
list_add_tail(&table->list, &afi->tables); list_add_tail(&table->list, &afi->tables);
nf_tables_table_notify(skb, nlh, table, NFT_MSG_NEWTABLE, family);
return 0; return 0;
} }
...@@ -457,7 +526,8 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, ...@@ -457,7 +526,8 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
struct nft_af_info *afi; struct nft_af_info *afi;
struct nft_table *table; struct nft_table *table;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family, err;
struct nft_ctx ctx;
afi = nf_tables_afinfo_lookup(net, family, false); afi = nf_tables_afinfo_lookup(net, family, false);
if (IS_ERR(afi)) if (IS_ERR(afi))
...@@ -466,17 +536,27 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb, ...@@ -466,17 +536,27 @@ static int nf_tables_deltable(struct sock *nlsk, struct sk_buff *skb,
table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]); table = nf_tables_table_lookup(afi, nla[NFTA_TABLE_NAME]);
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
if (table->flags & NFT_TABLE_INACTIVE)
return -ENOENT;
if (!list_empty(&table->chains) || !list_empty(&table->sets)) if (!list_empty(&table->chains) || !list_empty(&table->sets))
return -EBUSY; return -EBUSY;
nft_ctx_init(&ctx, skb, nlh, afi, table, NULL, nla);
err = nft_trans_table_add(&ctx, NFT_MSG_DELTABLE);
if (err < 0)
return err;
list_del(&table->list); list_del(&table->list);
nf_tables_table_notify(skb, nlh, table, NFT_MSG_DELTABLE, family);
kfree(table);
module_put(afi->owner);
return 0; return 0;
} }
static void nf_tables_table_destroy(struct nft_ctx *ctx)
{
kfree(ctx->table);
module_put(ctx->afi->owner);
}
int nft_register_chain_type(const struct nf_chain_type *ctype) int nft_register_chain_type(const struct nf_chain_type *ctype)
{ {
int err = 0; int err = 0;
...@@ -541,7 +621,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = { ...@@ -541,7 +621,7 @@ static const struct nla_policy nft_chain_policy[NFTA_CHAIN_MAX + 1] = {
.len = NFT_CHAIN_MAXNAMELEN - 1 }, .len = NFT_CHAIN_MAXNAMELEN - 1 },
[NFTA_CHAIN_HOOK] = { .type = NLA_NESTED }, [NFTA_CHAIN_HOOK] = { .type = NLA_NESTED },
[NFTA_CHAIN_POLICY] = { .type = NLA_U32 }, [NFTA_CHAIN_POLICY] = { .type = NLA_U32 },
[NFTA_CHAIN_TYPE] = { .type = NLA_NUL_STRING }, [NFTA_CHAIN_TYPE] = { .type = NLA_STRING },
[NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED }, [NFTA_CHAIN_COUNTERS] = { .type = NLA_NESTED },
}; };
...@@ -637,21 +717,13 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq, ...@@ -637,21 +717,13 @@ static int nf_tables_fill_chain_info(struct sk_buff *skb, u32 portid, u32 seq,
return -1; return -1;
} }
static int nf_tables_chain_notify(const struct sk_buff *oskb, static int nf_tables_chain_notify(const struct nft_ctx *ctx, int event)
const struct nlmsghdr *nlh,
const struct nft_table *table,
const struct nft_chain *chain,
int event, int family)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 portid = oskb ? NETLINK_CB(oskb).portid : 0;
struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
u32 seq = nlh ? nlh->nlmsg_seq : 0;
bool report;
int err; int err;
report = nlh ? nlmsg_report(nlh) : false; if (!ctx->report &&
if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
return 0; return 0;
err = -ENOBUFS; err = -ENOBUFS;
...@@ -659,18 +731,21 @@ static int nf_tables_chain_notify(const struct sk_buff *oskb, ...@@ -659,18 +731,21 @@ static int nf_tables_chain_notify(const struct sk_buff *oskb,
if (skb == NULL) if (skb == NULL)
goto err; goto err;
err = nf_tables_fill_chain_info(skb, portid, seq, event, 0, family, err = nf_tables_fill_chain_info(skb, ctx->portid, ctx->seq, event, 0,
table, chain); ctx->afi->family, ctx->table,
ctx->chain);
if (err < 0) { if (err < 0) {
kfree_skb(skb); kfree_skb(skb);
goto err; goto err;
} }
err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
GFP_KERNEL); ctx->report, GFP_KERNEL);
err: err:
if (err < 0) if (err < 0) {
nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
err);
}
return err; return err;
} }
...@@ -740,10 +815,14 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -740,10 +815,14 @@ static int nf_tables_getchain(struct sock *nlsk, struct sk_buff *skb,
table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
if (table->flags & NFT_TABLE_INACTIVE)
return -ENOENT;
chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
if (IS_ERR(chain)) if (IS_ERR(chain))
return PTR_ERR(chain); return PTR_ERR(chain);
if (chain->flags & NFT_CHAIN_INACTIVE)
return -ENOENT;
skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb2) if (!skb2)
...@@ -767,8 +846,7 @@ static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { ...@@ -767,8 +846,7 @@ static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
[NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, [NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
}; };
static int static struct nft_stats __percpu *nft_stats_alloc(const struct nlattr *attr)
nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
{ {
struct nlattr *tb[NFTA_COUNTER_MAX+1]; struct nlattr *tb[NFTA_COUNTER_MAX+1];
struct nft_stats __percpu *newstats; struct nft_stats __percpu *newstats;
...@@ -777,14 +855,14 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) ...@@ -777,14 +855,14 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy); err = nla_parse_nested(tb, NFTA_COUNTER_MAX, attr, nft_counter_policy);
if (err < 0) if (err < 0)
return err; return ERR_PTR(err);
if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS]) if (!tb[NFTA_COUNTER_BYTES] || !tb[NFTA_COUNTER_PACKETS])
return -EINVAL; return ERR_PTR(-EINVAL);
newstats = alloc_percpu(struct nft_stats); newstats = alloc_percpu(struct nft_stats);
if (newstats == NULL) if (newstats == NULL)
return -ENOMEM; return ERR_PTR(-ENOMEM);
/* Restore old counters on this cpu, no problem. Per-cpu statistics /* Restore old counters on this cpu, no problem. Per-cpu statistics
* are not exposed to userspace. * are not exposed to userspace.
...@@ -793,6 +871,12 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) ...@@ -793,6 +871,12 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES])); stats->bytes = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_BYTES]));
stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS])); stats->pkts = be64_to_cpu(nla_get_be64(tb[NFTA_COUNTER_PACKETS]));
return newstats;
}
static void nft_chain_stats_replace(struct nft_base_chain *chain,
struct nft_stats __percpu *newstats)
{
if (chain->stats) { if (chain->stats) {
struct nft_stats __percpu *oldstats = struct nft_stats __percpu *oldstats =
nft_dereference(chain->stats); nft_dereference(chain->stats);
...@@ -802,17 +886,43 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr) ...@@ -802,17 +886,43 @@ nf_tables_counters(struct nft_base_chain *chain, const struct nlattr *attr)
free_percpu(oldstats); free_percpu(oldstats);
} else } else
rcu_assign_pointer(chain->stats, newstats); rcu_assign_pointer(chain->stats, newstats);
}
static int nft_trans_chain_add(struct nft_ctx *ctx, int msg_type)
{
struct nft_trans *trans;
trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_chain));
if (trans == NULL)
return -ENOMEM;
if (msg_type == NFT_MSG_NEWCHAIN)
ctx->chain->flags |= NFT_CHAIN_INACTIVE;
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0; return 0;
} }
static void nf_tables_chain_destroy(struct nft_chain *chain)
{
BUG_ON(chain->use > 0);
if (chain->flags & NFT_BASE_CHAIN) {
module_put(nft_base_chain(chain)->type->owner);
free_percpu(nft_base_chain(chain)->stats);
kfree(nft_base_chain(chain));
} else {
kfree(chain);
}
}
static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
{ {
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nlattr * uninitialized_var(name); const struct nlattr * uninitialized_var(name);
const struct nft_af_info *afi; struct nft_af_info *afi;
struct nft_table *table; struct nft_table *table;
struct nft_chain *chain; struct nft_chain *chain;
struct nft_base_chain *basechain = NULL; struct nft_base_chain *basechain = NULL;
...@@ -822,8 +932,10 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -822,8 +932,10 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
u8 policy = NF_ACCEPT; u8 policy = NF_ACCEPT;
u64 handle = 0; u64 handle = 0;
unsigned int i; unsigned int i;
struct nft_stats __percpu *stats;
int err; int err;
bool create; bool create;
struct nft_ctx ctx;
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
...@@ -869,6 +981,11 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -869,6 +981,11 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
} }
if (chain != NULL) { if (chain != NULL) {
struct nft_stats *stats = NULL;
struct nft_trans *trans;
if (chain->flags & NFT_CHAIN_INACTIVE)
return -ENOENT;
if (nlh->nlmsg_flags & NLM_F_EXCL) if (nlh->nlmsg_flags & NLM_F_EXCL)
return -EEXIST; return -EEXIST;
if (nlh->nlmsg_flags & NLM_F_REPLACE) if (nlh->nlmsg_flags & NLM_F_REPLACE)
...@@ -882,19 +999,31 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -882,19 +999,31 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
if (!(chain->flags & NFT_BASE_CHAIN)) if (!(chain->flags & NFT_BASE_CHAIN))
return -EOPNOTSUPP; return -EOPNOTSUPP;
err = nf_tables_counters(nft_base_chain(chain), stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
nla[NFTA_CHAIN_COUNTERS]); if (IS_ERR(stats))
if (err < 0) return PTR_ERR(stats);
return err;
} }
if (nla[NFTA_CHAIN_POLICY]) nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
nft_base_chain(chain)->policy = policy; trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN,
sizeof(struct nft_trans_chain));
if (trans == NULL)
return -ENOMEM;
nft_trans_chain_stats(trans) = stats;
nft_trans_chain_update(trans) = true;
if (nla[NFTA_CHAIN_HANDLE] && name) if (nla[NFTA_CHAIN_POLICY])
nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); nft_trans_chain_policy(trans) = policy;
else
nft_trans_chain_policy(trans) = -1;
goto notify; if (nla[NFTA_CHAIN_HANDLE] && name) {
nla_strlcpy(nft_trans_chain_name(trans), name,
NFT_CHAIN_MAXNAMELEN);
}
list_add_tail(&trans->list, &net->nft.commit_list);
return 0;
} }
if (table->use == UINT_MAX) if (table->use == UINT_MAX)
...@@ -939,23 +1068,21 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -939,23 +1068,21 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
return -ENOMEM; return -ENOMEM;
if (nla[NFTA_CHAIN_COUNTERS]) { if (nla[NFTA_CHAIN_COUNTERS]) {
err = nf_tables_counters(basechain, stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]);
nla[NFTA_CHAIN_COUNTERS]); if (IS_ERR(stats)) {
if (err < 0) {
module_put(type->owner); module_put(type->owner);
kfree(basechain); kfree(basechain);
return err; return PTR_ERR(stats);
} }
basechain->stats = stats;
} else { } else {
struct nft_stats __percpu *newstats; stats = alloc_percpu(struct nft_stats);
if (IS_ERR(stats)) {
newstats = alloc_percpu(struct nft_stats);
if (newstats == NULL) {
module_put(type->owner); module_put(type->owner);
kfree(basechain); kfree(basechain);
return -ENOMEM; return PTR_ERR(stats);
} }
rcu_assign_pointer(basechain->stats, newstats); rcu_assign_pointer(basechain->stats, stats);
} }
basechain->type = type; basechain->type = type;
...@@ -992,31 +1119,26 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -992,31 +1119,26 @@ static int nf_tables_newchain(struct sock *nlsk, struct sk_buff *skb,
if (!(table->flags & NFT_TABLE_F_DORMANT) && if (!(table->flags & NFT_TABLE_F_DORMANT) &&
chain->flags & NFT_BASE_CHAIN) { chain->flags & NFT_BASE_CHAIN) {
err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops); err = nf_register_hooks(nft_base_chain(chain)->ops, afi->nops);
if (err < 0) { if (err < 0)
module_put(basechain->type->owner); goto err1;
free_percpu(basechain->stats);
kfree(basechain);
return err;
}
} }
list_add_tail(&chain->list, &table->chains);
table->use++;
notify:
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_NEWCHAIN,
family);
return 0;
}
static void nf_tables_chain_destroy(struct nft_chain *chain) nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
{ err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN);
BUG_ON(chain->use > 0); if (err < 0)
goto err2;
if (chain->flags & NFT_BASE_CHAIN) { list_add_tail(&chain->list, &table->chains);
module_put(nft_base_chain(chain)->type->owner); return 0;
free_percpu(nft_base_chain(chain)->stats); err2:
kfree(nft_base_chain(chain)); if (!(table->flags & NFT_TABLE_F_DORMANT) &&
} else chain->flags & NFT_BASE_CHAIN) {
kfree(chain); nf_unregister_hooks(nft_base_chain(chain)->ops,
afi->nops);
}
err1:
nf_tables_chain_destroy(chain);
return err;
} }
static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
...@@ -1024,11 +1146,13 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -1024,11 +1146,13 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
{ {
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi; struct nft_af_info *afi;
struct nft_table *table; struct nft_table *table;
struct nft_chain *chain; struct nft_chain *chain;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family; int family = nfmsg->nfgen_family;
struct nft_ctx ctx;
int err;
afi = nf_tables_afinfo_lookup(net, family, false); afi = nf_tables_afinfo_lookup(net, family, false);
if (IS_ERR(afi)) if (IS_ERR(afi))
...@@ -1037,48 +1161,26 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb, ...@@ -1037,48 +1161,26 @@ static int nf_tables_delchain(struct sock *nlsk, struct sk_buff *skb,
table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]); table = nf_tables_table_lookup(afi, nla[NFTA_CHAIN_TABLE]);
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
if (table->flags & NFT_TABLE_INACTIVE)
return -ENOENT;
chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]); chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME]);
if (IS_ERR(chain)) if (IS_ERR(chain))
return PTR_ERR(chain); return PTR_ERR(chain);
if (chain->flags & NFT_CHAIN_INACTIVE)
return -ENOENT;
if (!list_empty(&chain->rules) || chain->use > 0) if (!list_empty(&chain->rules) || chain->use > 0)
return -EBUSY; return -EBUSY;
list_del(&chain->list); nft_ctx_init(&ctx, skb, nlh, afi, table, chain, nla);
table->use--; err = nft_trans_chain_add(&ctx, NFT_MSG_DELCHAIN);
if (err < 0)
if (!(table->flags & NFT_TABLE_F_DORMANT) && return err;
chain->flags & NFT_BASE_CHAIN)
nf_unregister_hooks(nft_base_chain(chain)->ops, afi->nops);
nf_tables_chain_notify(skb, nlh, table, chain, NFT_MSG_DELCHAIN,
family);
/* Make sure all rule references are gone before this is released */
synchronize_rcu();
nf_tables_chain_destroy(chain); list_del(&chain->list);
return 0; return 0;
} }
static void nft_ctx_init(struct nft_ctx *ctx,
const struct sk_buff *skb,
const struct nlmsghdr *nlh,
const struct nft_af_info *afi,
const struct nft_table *table,
const struct nft_chain *chain,
const struct nlattr * const *nla)
{
ctx->net = sock_net(skb->sk);
ctx->skb = skb;
ctx->nlh = nlh;
ctx->afi = afi;
ctx->table = table;
ctx->chain = chain;
ctx->nla = nla;
}
/* /*
* Expressions * Expressions
*/ */
...@@ -1093,7 +1195,10 @@ static void nft_ctx_init(struct nft_ctx *ctx, ...@@ -1093,7 +1195,10 @@ static void nft_ctx_init(struct nft_ctx *ctx,
int nft_register_expr(struct nft_expr_type *type) int nft_register_expr(struct nft_expr_type *type)
{ {
nfnl_lock(NFNL_SUBSYS_NFTABLES); nfnl_lock(NFNL_SUBSYS_NFTABLES);
list_add_tail(&type->list, &nf_tables_expressions); if (type->family == NFPROTO_UNSPEC)
list_add_tail(&type->list, &nf_tables_expressions);
else
list_add(&type->list, &nf_tables_expressions);
nfnl_unlock(NFNL_SUBSYS_NFTABLES); nfnl_unlock(NFNL_SUBSYS_NFTABLES);
return 0; return 0;
} }
...@@ -1361,22 +1466,15 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq, ...@@ -1361,22 +1466,15 @@ static int nf_tables_fill_rule_info(struct sk_buff *skb, u32 portid, u32 seq,
return -1; return -1;
} }
static int nf_tables_rule_notify(const struct sk_buff *oskb, static int nf_tables_rule_notify(const struct nft_ctx *ctx,
const struct nlmsghdr *nlh,
const struct nft_table *table,
const struct nft_chain *chain,
const struct nft_rule *rule, const struct nft_rule *rule,
int event, u32 flags, int family) int event)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 portid = NETLINK_CB(oskb).portid;
struct net *net = oskb ? sock_net(oskb->sk) : &init_net;
u32 seq = nlh->nlmsg_seq;
bool report;
int err; int err;
report = nlmsg_report(nlh); if (!ctx->report &&
if (!report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES)) !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
return 0; return 0;
err = -ENOBUFS; err = -ENOBUFS;
...@@ -1384,18 +1482,21 @@ static int nf_tables_rule_notify(const struct sk_buff *oskb, ...@@ -1384,18 +1482,21 @@ static int nf_tables_rule_notify(const struct sk_buff *oskb,
if (skb == NULL) if (skb == NULL)
goto err; goto err;
err = nf_tables_fill_rule_info(skb, portid, seq, event, flags, err = nf_tables_fill_rule_info(skb, ctx->portid, ctx->seq, event, 0,
family, table, chain, rule); ctx->afi->family, ctx->table,
ctx->chain, rule);
if (err < 0) { if (err < 0) {
kfree_skb(skb); kfree_skb(skb);
goto err; goto err;
} }
err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, report, err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
GFP_KERNEL); ctx->report, GFP_KERNEL);
err: err:
if (err < 0) if (err < 0) {
nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err); nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
err);
}
return err; return err;
} }
...@@ -1511,10 +1612,14 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1511,10 +1612,14 @@ static int nf_tables_getrule(struct sock *nlsk, struct sk_buff *skb,
table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
if (table->flags & NFT_TABLE_INACTIVE)
return -ENOENT;
chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
if (IS_ERR(chain)) if (IS_ERR(chain))
return PTR_ERR(chain); return PTR_ERR(chain);
if (chain->flags & NFT_CHAIN_INACTIVE)
return -ENOENT;
rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]); rule = nf_tables_rule_lookup(chain, nla[NFTA_RULE_HANDLE]);
if (IS_ERR(rule)) if (IS_ERR(rule))
...@@ -1554,37 +1659,36 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx, ...@@ -1554,37 +1659,36 @@ static void nf_tables_rule_destroy(const struct nft_ctx *ctx,
kfree(rule); kfree(rule);
} }
#define NFT_RULE_MAXEXPRS 128 static struct nft_trans *nft_trans_rule_add(struct nft_ctx *ctx, int msg_type,
struct nft_rule *rule)
static struct nft_expr_info *info;
static struct nft_rule_trans *
nf_tables_trans_add(struct nft_ctx *ctx, struct nft_rule *rule)
{ {
struct nft_rule_trans *rupd; struct nft_trans *trans;
rupd = kmalloc(sizeof(struct nft_rule_trans), GFP_KERNEL); trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_rule));
if (rupd == NULL) if (trans == NULL)
return NULL; return NULL;
rupd->ctx = *ctx; nft_trans_rule(trans) = rule;
rupd->rule = rule; list_add_tail(&trans->list, &ctx->net->nft.commit_list);
list_add_tail(&rupd->list, &ctx->net->nft.commit_list);
return rupd; return trans;
} }
#define NFT_RULE_MAXEXPRS 128
static struct nft_expr_info *info;
static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
{ {
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi; struct nft_af_info *afi;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct nft_table *table; struct nft_table *table;
struct nft_chain *chain; struct nft_chain *chain;
struct nft_rule *rule, *old_rule = NULL; struct nft_rule *rule, *old_rule = NULL;
struct nft_rule_trans *repl = NULL; struct nft_trans *trans = NULL;
struct nft_expr *expr; struct nft_expr *expr;
struct nft_ctx ctx; struct nft_ctx ctx;
struct nlattr *tmp; struct nlattr *tmp;
...@@ -1682,8 +1786,9 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1682,8 +1786,9 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
if (nlh->nlmsg_flags & NLM_F_REPLACE) { if (nlh->nlmsg_flags & NLM_F_REPLACE) {
if (nft_rule_is_active_next(net, old_rule)) { if (nft_rule_is_active_next(net, old_rule)) {
repl = nf_tables_trans_add(&ctx, old_rule); trans = nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE,
if (repl == NULL) { old_rule);
if (trans == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto err2; goto err2;
} }
...@@ -1705,7 +1810,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1705,7 +1810,7 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
list_add_rcu(&rule->list, &chain->rules); list_add_rcu(&rule->list, &chain->rules);
} }
if (nf_tables_trans_add(&ctx, rule) == NULL) { if (nft_trans_rule_add(&ctx, NFT_MSG_NEWRULE, rule) == NULL) {
err = -ENOMEM; err = -ENOMEM;
goto err3; goto err3;
} }
...@@ -1713,11 +1818,10 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1713,11 +1818,10 @@ static int nf_tables_newrule(struct sock *nlsk, struct sk_buff *skb,
err3: err3:
list_del_rcu(&rule->list); list_del_rcu(&rule->list);
if (repl) { if (trans) {
list_del_rcu(&repl->rule->list); list_del_rcu(&nft_trans_rule(trans)->list);
list_del(&repl->list); nft_rule_clear(net, nft_trans_rule(trans));
nft_rule_clear(net, repl->rule); nft_trans_destroy(trans);
kfree(repl);
} }
err2: err2:
nf_tables_rule_destroy(&ctx, rule); nf_tables_rule_destroy(&ctx, rule);
...@@ -1734,7 +1838,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule) ...@@ -1734,7 +1838,7 @@ nf_tables_delrule_one(struct nft_ctx *ctx, struct nft_rule *rule)
{ {
/* You cannot delete the same rule twice */ /* You cannot delete the same rule twice */
if (nft_rule_is_active_next(ctx->net, rule)) { if (nft_rule_is_active_next(ctx->net, rule)) {
if (nf_tables_trans_add(ctx, rule) == NULL) if (nft_trans_rule_add(ctx, NFT_MSG_DELRULE, rule) == NULL)
return -ENOMEM; return -ENOMEM;
nft_rule_disactivate_next(ctx->net, rule); nft_rule_disactivate_next(ctx->net, rule);
return 0; return 0;
...@@ -1760,9 +1864,9 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1760,9 +1864,9 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
{ {
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi; struct nft_af_info *afi;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
const struct nft_table *table; struct nft_table *table;
struct nft_chain *chain = NULL; struct nft_chain *chain = NULL;
struct nft_rule *rule; struct nft_rule *rule;
int family = nfmsg->nfgen_family, err = 0; int family = nfmsg->nfgen_family, err = 0;
...@@ -1775,6 +1879,8 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1775,6 +1879,8 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]); table = nf_tables_table_lookup(afi, nla[NFTA_RULE_TABLE]);
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
if (table->flags & NFT_TABLE_INACTIVE)
return -ENOENT;
if (nla[NFTA_RULE_CHAIN]) { if (nla[NFTA_RULE_CHAIN]) {
chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]); chain = nf_tables_chain_lookup(table, nla[NFTA_RULE_CHAIN]);
...@@ -1807,88 +1913,6 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb, ...@@ -1807,88 +1913,6 @@ static int nf_tables_delrule(struct sock *nlsk, struct sk_buff *skb,
return err; return err;
} }
static int nf_tables_commit(struct sk_buff *skb)
{
struct net *net = sock_net(skb->sk);
struct nft_rule_trans *rupd, *tmp;
/* Bump generation counter, invalidate any dump in progress */
net->nft.genctr++;
/* A new generation has just started */
net->nft.gencursor = gencursor_next(net);
/* Make sure all packets have left the previous generation before
* purging old rules.
*/
synchronize_rcu();
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
/* This rule was inactive in the past and just became active.
* Clear the next bit of the genmask since its meaning has
* changed, now it is the future.
*/
if (nft_rule_is_active(net, rupd->rule)) {
nft_rule_clear(net, rupd->rule);
nf_tables_rule_notify(skb, rupd->ctx.nlh,
rupd->ctx.table, rupd->ctx.chain,
rupd->rule, NFT_MSG_NEWRULE, 0,
rupd->ctx.afi->family);
list_del(&rupd->list);
kfree(rupd);
continue;
}
/* This rule is in the past, get rid of it */
list_del_rcu(&rupd->rule->list);
nf_tables_rule_notify(skb, rupd->ctx.nlh,
rupd->ctx.table, rupd->ctx.chain,
rupd->rule, NFT_MSG_DELRULE, 0,
rupd->ctx.afi->family);
}
/* Make sure we don't see any packet traversing old rules */
synchronize_rcu();
/* Now we can safely release unused old rules */
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
list_del(&rupd->list);
kfree(rupd);
}
return 0;
}
static int nf_tables_abort(struct sk_buff *skb)
{
struct net *net = sock_net(skb->sk);
struct nft_rule_trans *rupd, *tmp;
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
if (!nft_rule_is_active_next(net, rupd->rule)) {
nft_rule_clear(net, rupd->rule);
list_del(&rupd->list);
kfree(rupd);
continue;
}
/* This rule is inactive, get rid of it */
list_del_rcu(&rupd->rule->list);
}
/* Make sure we don't see any packet accessing aborted rules */
synchronize_rcu();
list_for_each_entry_safe(rupd, tmp, &net->nft.commit_list, list) {
nf_tables_rule_destroy(&rupd->ctx, rupd->rule);
list_del(&rupd->list);
kfree(rupd);
}
return 0;
}
/* /*
* Sets * Sets
*/ */
...@@ -1912,9 +1936,18 @@ void nft_unregister_set(struct nft_set_ops *ops) ...@@ -1912,9 +1936,18 @@ void nft_unregister_set(struct nft_set_ops *ops)
} }
EXPORT_SYMBOL_GPL(nft_unregister_set); EXPORT_SYMBOL_GPL(nft_unregister_set);
static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const nla[]) /*
* Select a set implementation based on the data characteristics and the
* given policy. The total memory use might not be known if no size is
* given, in that case the amount of memory per element is used.
*/
static const struct nft_set_ops *
nft_select_set_ops(const struct nlattr * const nla[],
const struct nft_set_desc *desc,
enum nft_set_policies policy)
{ {
const struct nft_set_ops *ops; const struct nft_set_ops *ops, *bops;
struct nft_set_estimate est, best;
u32 features; u32 features;
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
...@@ -1932,15 +1965,45 @@ static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const ...@@ -1932,15 +1965,45 @@ static const struct nft_set_ops *nft_select_set_ops(const struct nlattr * const
features &= NFT_SET_INTERVAL | NFT_SET_MAP; features &= NFT_SET_INTERVAL | NFT_SET_MAP;
} }
// FIXME: implement selection properly bops = NULL;
best.size = ~0;
best.class = ~0;
list_for_each_entry(ops, &nf_tables_set_ops, list) { list_for_each_entry(ops, &nf_tables_set_ops, list) {
if ((ops->features & features) != features) if ((ops->features & features) != features)
continue; continue;
if (!ops->estimate(desc, features, &est))
continue;
switch (policy) {
case NFT_SET_POL_PERFORMANCE:
if (est.class < best.class)
break;
if (est.class == best.class && est.size < best.size)
break;
continue;
case NFT_SET_POL_MEMORY:
if (est.size < best.size)
break;
if (est.size == best.size && est.class < best.class)
break;
continue;
default:
break;
}
if (!try_module_get(ops->owner)) if (!try_module_get(ops->owner))
continue; continue;
return ops; if (bops != NULL)
module_put(bops->owner);
bops = ops;
best = est;
} }
if (bops != NULL)
return bops;
return ERR_PTR(-EOPNOTSUPP); return ERR_PTR(-EOPNOTSUPP);
} }
...@@ -1953,6 +2016,13 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = { ...@@ -1953,6 +2016,13 @@ static const struct nla_policy nft_set_policy[NFTA_SET_MAX + 1] = {
[NFTA_SET_KEY_LEN] = { .type = NLA_U32 }, [NFTA_SET_KEY_LEN] = { .type = NLA_U32 },
[NFTA_SET_DATA_TYPE] = { .type = NLA_U32 }, [NFTA_SET_DATA_TYPE] = { .type = NLA_U32 },
[NFTA_SET_DATA_LEN] = { .type = NLA_U32 }, [NFTA_SET_DATA_LEN] = { .type = NLA_U32 },
[NFTA_SET_POLICY] = { .type = NLA_U32 },
[NFTA_SET_DESC] = { .type = NLA_NESTED },
[NFTA_SET_ID] = { .type = NLA_U32 },
};
static const struct nla_policy nft_set_desc_policy[NFTA_SET_DESC_MAX + 1] = {
[NFTA_SET_DESC_SIZE] = { .type = NLA_U32 },
}; };
static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
...@@ -1962,8 +2032,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, ...@@ -1962,8 +2032,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi = NULL; struct nft_af_info *afi = NULL;
const struct nft_table *table = NULL; struct nft_table *table = NULL;
if (nfmsg->nfgen_family != NFPROTO_UNSPEC) { if (nfmsg->nfgen_family != NFPROTO_UNSPEC) {
afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
...@@ -1978,6 +2048,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx, ...@@ -1978,6 +2048,8 @@ static int nft_ctx_init_from_setattr(struct nft_ctx *ctx,
table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]); table = nf_tables_table_lookup(afi, nla[NFTA_SET_TABLE]);
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
if (table->flags & NFT_TABLE_INACTIVE)
return -ENOENT;
} }
nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
...@@ -1999,13 +2071,27 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table, ...@@ -1999,13 +2071,27 @@ struct nft_set *nf_tables_set_lookup(const struct nft_table *table,
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
} }
struct nft_set *nf_tables_set_lookup_byid(const struct net *net,
const struct nlattr *nla)
{
struct nft_trans *trans;
u32 id = ntohl(nla_get_be32(nla));
list_for_each_entry(trans, &net->nft.commit_list, list) {
if (trans->msg_type == NFT_MSG_NEWSET &&
id == nft_trans_set_id(trans))
return nft_trans_set(trans);
}
return ERR_PTR(-ENOENT);
}
static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
const char *name) const char *name)
{ {
const struct nft_set *i; const struct nft_set *i;
const char *p; const char *p;
unsigned long *inuse; unsigned long *inuse;
unsigned int n = 0; unsigned int n = 0, min = 0;
p = strnchr(name, IFNAMSIZ, '%'); p = strnchr(name, IFNAMSIZ, '%');
if (p != NULL) { if (p != NULL) {
...@@ -2015,23 +2101,28 @@ static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, ...@@ -2015,23 +2101,28 @@ static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set,
inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL); inuse = (unsigned long *)get_zeroed_page(GFP_KERNEL);
if (inuse == NULL) if (inuse == NULL)
return -ENOMEM; return -ENOMEM;
cont:
list_for_each_entry(i, &ctx->table->sets, list) { list_for_each_entry(i, &ctx->table->sets, list) {
int tmp; int tmp;
if (!sscanf(i->name, name, &tmp)) if (!sscanf(i->name, name, &tmp))
continue; continue;
if (tmp < 0 || tmp >= BITS_PER_BYTE * PAGE_SIZE) if (tmp < min || tmp >= min + BITS_PER_BYTE * PAGE_SIZE)
continue; continue;
set_bit(tmp, inuse); set_bit(tmp - min, inuse);
} }
n = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE); n = find_first_zero_bit(inuse, BITS_PER_BYTE * PAGE_SIZE);
if (n >= BITS_PER_BYTE * PAGE_SIZE) {
min += BITS_PER_BYTE * PAGE_SIZE;
memset(inuse, 0, PAGE_SIZE);
goto cont;
}
free_page((unsigned long)inuse); free_page((unsigned long)inuse);
} }
snprintf(set->name, sizeof(set->name), name, n); snprintf(set->name, sizeof(set->name), name, min + n);
list_for_each_entry(i, &ctx->table->sets, list) { list_for_each_entry(i, &ctx->table->sets, list) {
if (!strcmp(set->name, i->name)) if (!strcmp(set->name, i->name))
return -ENFILE; return -ENFILE;
...@@ -2044,8 +2135,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, ...@@ -2044,8 +2135,9 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
{ {
struct nfgenmsg *nfmsg; struct nfgenmsg *nfmsg;
struct nlmsghdr *nlh; struct nlmsghdr *nlh;
u32 portid = NETLINK_CB(ctx->skb).portid; struct nlattr *desc;
u32 seq = ctx->nlh->nlmsg_seq; u32 portid = ctx->portid;
u32 seq = ctx->seq;
event |= NFNL_SUBSYS_NFTABLES << 8; event |= NFNL_SUBSYS_NFTABLES << 8;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
...@@ -2077,6 +2169,14 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx, ...@@ -2077,6 +2169,14 @@ static int nf_tables_fill_set(struct sk_buff *skb, const struct nft_ctx *ctx,
goto nla_put_failure; goto nla_put_failure;
} }
desc = nla_nest_start(skb, NFTA_SET_DESC);
if (desc == NULL)
goto nla_put_failure;
if (set->size &&
nla_put_be32(skb, NFTA_SET_DESC_SIZE, htonl(set->size)))
goto nla_put_failure;
nla_nest_end(skb, desc);
return nlmsg_end(skb, nlh); return nlmsg_end(skb, nlh);
nla_put_failure: nla_put_failure:
...@@ -2089,12 +2189,11 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx, ...@@ -2089,12 +2189,11 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx,
int event) int event)
{ {
struct sk_buff *skb; struct sk_buff *skb;
u32 portid = NETLINK_CB(ctx->skb).portid; u32 portid = ctx->portid;
bool report;
int err; int err;
report = nlmsg_report(ctx->nlh); if (!ctx->report &&
if (!report && !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES)) !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
return 0; return 0;
err = -ENOBUFS; err = -ENOBUFS;
...@@ -2108,8 +2207,8 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx, ...@@ -2108,8 +2207,8 @@ static int nf_tables_set_notify(const struct nft_ctx *ctx,
goto err; goto err;
} }
err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES, report, err = nfnetlink_send(skb, ctx->net, portid, NFNLGRP_NFTABLES,
GFP_KERNEL); ctx->report, GFP_KERNEL);
err: err:
if (err < 0) if (err < 0)
nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err); nfnetlink_set_err(ctx->net, portid, NFNLGRP_NFTABLES, err);
...@@ -2183,7 +2282,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb, ...@@ -2183,7 +2282,7 @@ static int nf_tables_dump_sets_all(struct nft_ctx *ctx, struct sk_buff *skb,
{ {
const struct nft_set *set; const struct nft_set *set;
unsigned int idx, s_idx = cb->args[0]; unsigned int idx, s_idx = cb->args[0];
const struct nft_af_info *afi; struct nft_af_info *afi;
struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2]; struct nft_table *table, *cur_table = (struct nft_table *)cb->args[2];
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
int cur_family = cb->args[3]; int cur_family = cb->args[3];
...@@ -2260,6 +2359,8 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2260,6 +2359,8 @@ static int nf_tables_dump_sets(struct sk_buff *skb, struct netlink_callback *cb)
return ret; return ret;
} }
#define NFT_SET_INACTIVE (1 << 15) /* Internal set flag */
static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
...@@ -2289,6 +2390,8 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2289,6 +2390,8 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
if (IS_ERR(set)) if (IS_ERR(set))
return PTR_ERR(set); return PTR_ERR(set);
if (set->flags & NFT_SET_INACTIVE)
return -ENOENT;
skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
if (skb2 == NULL) if (skb2 == NULL)
...@@ -2305,13 +2408,50 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2305,13 +2408,50 @@ static int nf_tables_getset(struct sock *nlsk, struct sk_buff *skb,
return err; return err;
} }
static int nf_tables_set_desc_parse(const struct nft_ctx *ctx,
struct nft_set_desc *desc,
const struct nlattr *nla)
{
struct nlattr *da[NFTA_SET_DESC_MAX + 1];
int err;
err = nla_parse_nested(da, NFTA_SET_DESC_MAX, nla, nft_set_desc_policy);
if (err < 0)
return err;
if (da[NFTA_SET_DESC_SIZE] != NULL)
desc->size = ntohl(nla_get_be32(da[NFTA_SET_DESC_SIZE]));
return 0;
}
static int nft_trans_set_add(struct nft_ctx *ctx, int msg_type,
struct nft_set *set)
{
struct nft_trans *trans;
trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_set));
if (trans == NULL)
return -ENOMEM;
if (msg_type == NFT_MSG_NEWSET && ctx->nla[NFTA_SET_ID] != NULL) {
nft_trans_set_id(trans) =
ntohl(nla_get_be32(ctx->nla[NFTA_SET_ID]));
set->flags |= NFT_SET_INACTIVE;
}
nft_trans_set(trans) = set;
list_add_tail(&trans->list, &ctx->net->nft.commit_list);
return 0;
}
static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
{ {
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_set_ops *ops; const struct nft_set_ops *ops;
const struct nft_af_info *afi; struct nft_af_info *afi;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct nft_table *table; struct nft_table *table;
struct nft_set *set; struct nft_set *set;
...@@ -2319,14 +2459,18 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2319,14 +2459,18 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
char name[IFNAMSIZ]; char name[IFNAMSIZ];
unsigned int size; unsigned int size;
bool create; bool create;
u32 ktype, klen, dlen, dtype, flags; u32 ktype, dtype, flags, policy;
struct nft_set_desc desc;
int err; int err;
if (nla[NFTA_SET_TABLE] == NULL || if (nla[NFTA_SET_TABLE] == NULL ||
nla[NFTA_SET_NAME] == NULL || nla[NFTA_SET_NAME] == NULL ||
nla[NFTA_SET_KEY_LEN] == NULL) nla[NFTA_SET_KEY_LEN] == NULL ||
nla[NFTA_SET_ID] == NULL)
return -EINVAL; return -EINVAL;
memset(&desc, 0, sizeof(desc));
ktype = NFT_DATA_VALUE; ktype = NFT_DATA_VALUE;
if (nla[NFTA_SET_KEY_TYPE] != NULL) { if (nla[NFTA_SET_KEY_TYPE] != NULL) {
ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE])); ktype = ntohl(nla_get_be32(nla[NFTA_SET_KEY_TYPE]));
...@@ -2334,8 +2478,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2334,8 +2478,8 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
return -EINVAL; return -EINVAL;
} }
klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN])); desc.klen = ntohl(nla_get_be32(nla[NFTA_SET_KEY_LEN]));
if (klen == 0 || klen > FIELD_SIZEOF(struct nft_data, data)) if (desc.klen == 0 || desc.klen > FIELD_SIZEOF(struct nft_data, data))
return -EINVAL; return -EINVAL;
flags = 0; flags = 0;
...@@ -2347,7 +2491,6 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2347,7 +2491,6 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
} }
dtype = 0; dtype = 0;
dlen = 0;
if (nla[NFTA_SET_DATA_TYPE] != NULL) { if (nla[NFTA_SET_DATA_TYPE] != NULL) {
if (!(flags & NFT_SET_MAP)) if (!(flags & NFT_SET_MAP))
return -EINVAL; return -EINVAL;
...@@ -2360,15 +2503,25 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2360,15 +2503,25 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
if (dtype != NFT_DATA_VERDICT) { if (dtype != NFT_DATA_VERDICT) {
if (nla[NFTA_SET_DATA_LEN] == NULL) if (nla[NFTA_SET_DATA_LEN] == NULL)
return -EINVAL; return -EINVAL;
dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN])); desc.dlen = ntohl(nla_get_be32(nla[NFTA_SET_DATA_LEN]));
if (dlen == 0 || if (desc.dlen == 0 ||
dlen > FIELD_SIZEOF(struct nft_data, data)) desc.dlen > FIELD_SIZEOF(struct nft_data, data))
return -EINVAL; return -EINVAL;
} else } else
dlen = sizeof(struct nft_data); desc.dlen = sizeof(struct nft_data);
} else if (flags & NFT_SET_MAP) } else if (flags & NFT_SET_MAP)
return -EINVAL; return -EINVAL;
policy = NFT_SET_POL_PERFORMANCE;
if (nla[NFTA_SET_POLICY] != NULL)
policy = ntohl(nla_get_be32(nla[NFTA_SET_POLICY]));
if (nla[NFTA_SET_DESC] != NULL) {
err = nf_tables_set_desc_parse(&ctx, &desc, nla[NFTA_SET_DESC]);
if (err < 0)
return err;
}
create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false;
afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create); afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, create);
...@@ -2399,7 +2552,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2399,7 +2552,7 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
if (!(nlh->nlmsg_flags & NLM_F_CREATE)) if (!(nlh->nlmsg_flags & NLM_F_CREATE))
return -ENOENT; return -ENOENT;
ops = nft_select_set_ops(nla); ops = nft_select_set_ops(nla, &desc, policy);
if (IS_ERR(ops)) if (IS_ERR(ops))
return PTR_ERR(ops); return PTR_ERR(ops);
...@@ -2420,17 +2573,21 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2420,17 +2573,21 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
INIT_LIST_HEAD(&set->bindings); INIT_LIST_HEAD(&set->bindings);
set->ops = ops; set->ops = ops;
set->ktype = ktype; set->ktype = ktype;
set->klen = klen; set->klen = desc.klen;
set->dtype = dtype; set->dtype = dtype;
set->dlen = dlen; set->dlen = desc.dlen;
set->flags = flags; set->flags = flags;
set->size = desc.size;
err = ops->init(set, nla); err = ops->init(set, &desc, nla);
if (err < 0)
goto err2;
err = nft_trans_set_add(&ctx, NFT_MSG_NEWSET, set);
if (err < 0) if (err < 0)
goto err2; goto err2;
list_add_tail(&set->list, &table->sets); list_add_tail(&set->list, &table->sets);
nf_tables_set_notify(&ctx, set, NFT_MSG_NEWSET);
return 0; return 0;
err2: err2:
...@@ -2440,16 +2597,20 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2440,16 +2597,20 @@ static int nf_tables_newset(struct sock *nlsk, struct sk_buff *skb,
return err; return err;
} }
static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set) static void nft_set_destroy(struct nft_set *set)
{ {
list_del(&set->list);
nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
set->ops->destroy(set); set->ops->destroy(set);
module_put(set->ops->owner); module_put(set->ops->owner);
kfree(set); kfree(set);
} }
static void nf_tables_set_destroy(const struct nft_ctx *ctx, struct nft_set *set)
{
list_del(&set->list);
nf_tables_set_notify(ctx, set, NFT_MSG_DELSET);
nft_set_destroy(set);
}
static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
...@@ -2471,10 +2632,16 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb, ...@@ -2471,10 +2632,16 @@ static int nf_tables_delset(struct sock *nlsk, struct sk_buff *skb,
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]); set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME]);
if (IS_ERR(set)) if (IS_ERR(set))
return PTR_ERR(set); return PTR_ERR(set);
if (set->flags & NFT_SET_INACTIVE)
return -ENOENT;
if (!list_empty(&set->bindings)) if (!list_empty(&set->bindings))
return -EBUSY; return -EBUSY;
nf_tables_set_destroy(&ctx, set); err = nft_trans_set_add(&ctx, NFT_MSG_DELSET, set);
if (err < 0)
return err;
list_del(&set->list);
return 0; return 0;
} }
...@@ -2534,7 +2701,8 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -2534,7 +2701,8 @@ void nf_tables_unbind_set(const struct nft_ctx *ctx, struct nft_set *set,
{ {
list_del(&binding->list); list_del(&binding->list);
if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS) if (list_empty(&set->bindings) && set->flags & NFT_SET_ANONYMOUS &&
!(set->flags & NFT_SET_INACTIVE))
nf_tables_set_destroy(ctx, set); nf_tables_set_destroy(ctx, set);
} }
...@@ -2552,16 +2720,18 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX + ...@@ -2552,16 +2720,18 @@ static const struct nla_policy nft_set_elem_list_policy[NFTA_SET_ELEM_LIST_MAX +
[NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING }, [NFTA_SET_ELEM_LIST_TABLE] = { .type = NLA_STRING },
[NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING }, [NFTA_SET_ELEM_LIST_SET] = { .type = NLA_STRING },
[NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED }, [NFTA_SET_ELEM_LIST_ELEMENTS] = { .type = NLA_NESTED },
[NFTA_SET_ELEM_LIST_SET_ID] = { .type = NLA_U32 },
}; };
static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
const struct sk_buff *skb, const struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[],
bool trans)
{ {
const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
const struct nft_af_info *afi; struct nft_af_info *afi;
const struct nft_table *table; struct nft_table *table;
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false); afi = nf_tables_afinfo_lookup(net, nfmsg->nfgen_family, false);
...@@ -2571,6 +2741,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx, ...@@ -2571,6 +2741,8 @@ static int nft_ctx_init_from_elemattr(struct nft_ctx *ctx,
table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]); table = nf_tables_table_lookup(afi, nla[NFTA_SET_ELEM_LIST_TABLE]);
if (IS_ERR(table)) if (IS_ERR(table))
return PTR_ERR(table); return PTR_ERR(table);
if (!trans && (table->flags & NFT_TABLE_INACTIVE))
return -ENOENT;
nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla); nft_ctx_init(ctx, skb, nlh, afi, table, NULL, nla);
return 0; return 0;
...@@ -2644,13 +2816,16 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2644,13 +2816,16 @@ static int nf_tables_dump_set(struct sk_buff *skb, struct netlink_callback *cb)
if (err < 0) if (err < 0)
return err; return err;
err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla); err = nft_ctx_init_from_elemattr(&ctx, cb->skb, cb->nlh, (void *)nla,
false);
if (err < 0) if (err < 0)
return err; return err;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
if (IS_ERR(set)) if (IS_ERR(set))
return PTR_ERR(set); return PTR_ERR(set);
if (set->flags & NFT_SET_INACTIVE)
return -ENOENT;
event = NFT_MSG_NEWSETELEM; event = NFT_MSG_NEWSETELEM;
event |= NFNL_SUBSYS_NFTABLES << 8; event |= NFNL_SUBSYS_NFTABLES << 8;
...@@ -2707,13 +2882,15 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb, ...@@ -2707,13 +2882,15 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
struct nft_ctx ctx; struct nft_ctx ctx;
int err; int err;
err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
if (err < 0) if (err < 0)
return err; return err;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
if (IS_ERR(set)) if (IS_ERR(set))
return PTR_ERR(set); return PTR_ERR(set);
if (set->flags & NFT_SET_INACTIVE)
return -ENOENT;
if (nlh->nlmsg_flags & NLM_F_DUMP) { if (nlh->nlmsg_flags & NLM_F_DUMP) {
struct netlink_dump_control c = { struct netlink_dump_control c = {
...@@ -2724,7 +2901,98 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb, ...@@ -2724,7 +2901,98 @@ static int nf_tables_getsetelem(struct sock *nlsk, struct sk_buff *skb,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, static int nf_tables_fill_setelem_info(struct sk_buff *skb,
const struct nft_ctx *ctx, u32 seq,
u32 portid, int event, u16 flags,
const struct nft_set *set,
const struct nft_set_elem *elem)
{
struct nfgenmsg *nfmsg;
struct nlmsghdr *nlh;
struct nlattr *nest;
int err;
event |= NFNL_SUBSYS_NFTABLES << 8;
nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg),
flags);
if (nlh == NULL)
goto nla_put_failure;
nfmsg = nlmsg_data(nlh);
nfmsg->nfgen_family = ctx->afi->family;
nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = 0;
if (nla_put_string(skb, NFTA_SET_TABLE, ctx->table->name))
goto nla_put_failure;
if (nla_put_string(skb, NFTA_SET_NAME, set->name))
goto nla_put_failure;
nest = nla_nest_start(skb, NFTA_SET_ELEM_LIST_ELEMENTS);
if (nest == NULL)
goto nla_put_failure;
err = nf_tables_fill_setelem(skb, set, elem);
if (err < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
return nlmsg_end(skb, nlh);
nla_put_failure:
nlmsg_trim(skb, nlh);
return -1;
}
static int nf_tables_setelem_notify(const struct nft_ctx *ctx,
const struct nft_set *set,
const struct nft_set_elem *elem,
int event, u16 flags)
{
struct net *net = ctx->net;
u32 portid = ctx->portid;
struct sk_buff *skb;
int err;
if (!ctx->report && !nfnetlink_has_listeners(net, NFNLGRP_NFTABLES))
return 0;
err = -ENOBUFS;
skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (skb == NULL)
goto err;
err = nf_tables_fill_setelem_info(skb, ctx, 0, portid, event, flags,
set, elem);
if (err < 0) {
kfree_skb(skb);
goto err;
}
err = nfnetlink_send(skb, net, portid, NFNLGRP_NFTABLES, ctx->report,
GFP_KERNEL);
err:
if (err < 0)
nfnetlink_set_err(net, portid, NFNLGRP_NFTABLES, err);
return err;
}
static struct nft_trans *nft_trans_elem_alloc(struct nft_ctx *ctx,
int msg_type,
struct nft_set *set)
{
struct nft_trans *trans;
trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_elem));
if (trans == NULL)
return NULL;
nft_trans_elem_set(trans) = set;
return trans;
}
static int nft_add_set_elem(struct nft_ctx *ctx, struct nft_set *set,
const struct nlattr *attr) const struct nlattr *attr)
{ {
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
...@@ -2732,8 +3000,12 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -2732,8 +3000,12 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
struct nft_set_elem elem; struct nft_set_elem elem;
struct nft_set_binding *binding; struct nft_set_binding *binding;
enum nft_registers dreg; enum nft_registers dreg;
struct nft_trans *trans;
int err; int err;
if (set->size && set->nelems == set->size)
return -ENFILE;
err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
nft_set_elem_policy); nft_set_elem_policy);
if (err < 0) if (err < 0)
...@@ -2786,7 +3058,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -2786,7 +3058,7 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
struct nft_ctx bind_ctx = { struct nft_ctx bind_ctx = {
.afi = ctx->afi, .afi = ctx->afi,
.table = ctx->table, .table = ctx->table,
.chain = binding->chain, .chain = (struct nft_chain *)binding->chain,
}; };
err = nft_validate_data_load(&bind_ctx, dreg, err = nft_validate_data_load(&bind_ctx, dreg,
...@@ -2796,12 +3068,20 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -2796,12 +3068,20 @@ static int nft_add_set_elem(const struct nft_ctx *ctx, struct nft_set *set,
} }
} }
trans = nft_trans_elem_alloc(ctx, NFT_MSG_NEWSETELEM, set);
if (trans == NULL)
goto err3;
err = set->ops->insert(set, &elem); err = set->ops->insert(set, &elem);
if (err < 0) if (err < 0)
goto err3; goto err4;
nft_trans_elem(trans) = elem;
list_add(&trans->list, &ctx->net->nft.commit_list);
return 0; return 0;
err4:
kfree(trans);
err3: err3:
if (nla[NFTA_SET_ELEM_DATA] != NULL) if (nla[NFTA_SET_ELEM_DATA] != NULL)
nft_data_uninit(&elem.data, d2.type); nft_data_uninit(&elem.data, d2.type);
...@@ -2815,35 +3095,44 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb, ...@@ -2815,35 +3095,44 @@ static int nf_tables_newsetelem(struct sock *nlsk, struct sk_buff *skb,
const struct nlmsghdr *nlh, const struct nlmsghdr *nlh,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
{ {
struct net *net = sock_net(skb->sk);
const struct nlattr *attr; const struct nlattr *attr;
struct nft_set *set; struct nft_set *set;
struct nft_ctx ctx; struct nft_ctx ctx;
int rem, err; int rem, err = 0;
err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, true);
if (err < 0) if (err < 0)
return err; return err;
set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]); set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_ELEM_LIST_SET]);
if (IS_ERR(set)) if (IS_ERR(set)) {
return PTR_ERR(set); if (nla[NFTA_SET_ELEM_LIST_SET_ID]) {
set = nf_tables_set_lookup_byid(net,
nla[NFTA_SET_ELEM_LIST_SET_ID]);
}
if (IS_ERR(set))
return PTR_ERR(set);
}
if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT) if (!list_empty(&set->bindings) && set->flags & NFT_SET_CONSTANT)
return -EBUSY; return -EBUSY;
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
err = nft_add_set_elem(&ctx, set, attr); err = nft_add_set_elem(&ctx, set, attr);
if (err < 0) if (err < 0)
return err; break;
} }
return 0; return err;
} }
static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set, static int nft_del_setelem(struct nft_ctx *ctx, struct nft_set *set,
const struct nlattr *attr) const struct nlattr *attr)
{ {
struct nlattr *nla[NFTA_SET_ELEM_MAX + 1]; struct nlattr *nla[NFTA_SET_ELEM_MAX + 1];
struct nft_data_desc desc; struct nft_data_desc desc;
struct nft_set_elem elem; struct nft_set_elem elem;
struct nft_trans *trans;
int err; int err;
err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr, err = nla_parse_nested(nla, NFTA_SET_ELEM_MAX, attr,
...@@ -2867,7 +3156,12 @@ static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set, ...@@ -2867,7 +3156,12 @@ static int nft_del_setelem(const struct nft_ctx *ctx, struct nft_set *set,
if (err < 0) if (err < 0)
goto err2; goto err2;
set->ops->remove(set, &elem); trans = nft_trans_elem_alloc(ctx, NFT_MSG_DELSETELEM, set);
if (trans == NULL)
goto err2;
nft_trans_elem(trans) = elem;
list_add(&trans->list, &ctx->net->nft.commit_list);
nft_data_uninit(&elem.key, NFT_DATA_VALUE); nft_data_uninit(&elem.key, NFT_DATA_VALUE);
if (set->flags & NFT_SET_MAP) if (set->flags & NFT_SET_MAP)
...@@ -2886,9 +3180,9 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, ...@@ -2886,9 +3180,9 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
const struct nlattr *attr; const struct nlattr *attr;
struct nft_set *set; struct nft_set *set;
struct nft_ctx ctx; struct nft_ctx ctx;
int rem, err; int rem, err = 0;
err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla); err = nft_ctx_init_from_elemattr(&ctx, skb, nlh, nla, false);
if (err < 0) if (err < 0)
return err; return err;
...@@ -2901,14 +3195,14 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb, ...@@ -2901,14 +3195,14 @@ static int nf_tables_delsetelem(struct sock *nlsk, struct sk_buff *skb,
nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) { nla_for_each_nested(attr, nla[NFTA_SET_ELEM_LIST_ELEMENTS], rem) {
err = nft_del_setelem(&ctx, set, attr); err = nft_del_setelem(&ctx, set, attr);
if (err < 0) if (err < 0)
return err; break;
} }
return 0; return err;
} }
static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
[NFT_MSG_NEWTABLE] = { [NFT_MSG_NEWTABLE] = {
.call = nf_tables_newtable, .call_batch = nf_tables_newtable,
.attr_count = NFTA_TABLE_MAX, .attr_count = NFTA_TABLE_MAX,
.policy = nft_table_policy, .policy = nft_table_policy,
}, },
...@@ -2918,12 +3212,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { ...@@ -2918,12 +3212,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
.policy = nft_table_policy, .policy = nft_table_policy,
}, },
[NFT_MSG_DELTABLE] = { [NFT_MSG_DELTABLE] = {
.call = nf_tables_deltable, .call_batch = nf_tables_deltable,
.attr_count = NFTA_TABLE_MAX, .attr_count = NFTA_TABLE_MAX,
.policy = nft_table_policy, .policy = nft_table_policy,
}, },
[NFT_MSG_NEWCHAIN] = { [NFT_MSG_NEWCHAIN] = {
.call = nf_tables_newchain, .call_batch = nf_tables_newchain,
.attr_count = NFTA_CHAIN_MAX, .attr_count = NFTA_CHAIN_MAX,
.policy = nft_chain_policy, .policy = nft_chain_policy,
}, },
...@@ -2933,7 +3227,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { ...@@ -2933,7 +3227,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
.policy = nft_chain_policy, .policy = nft_chain_policy,
}, },
[NFT_MSG_DELCHAIN] = { [NFT_MSG_DELCHAIN] = {
.call = nf_tables_delchain, .call_batch = nf_tables_delchain,
.attr_count = NFTA_CHAIN_MAX, .attr_count = NFTA_CHAIN_MAX,
.policy = nft_chain_policy, .policy = nft_chain_policy,
}, },
...@@ -2953,7 +3247,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { ...@@ -2953,7 +3247,7 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
.policy = nft_rule_policy, .policy = nft_rule_policy,
}, },
[NFT_MSG_NEWSET] = { [NFT_MSG_NEWSET] = {
.call = nf_tables_newset, .call_batch = nf_tables_newset,
.attr_count = NFTA_SET_MAX, .attr_count = NFTA_SET_MAX,
.policy = nft_set_policy, .policy = nft_set_policy,
}, },
...@@ -2963,12 +3257,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { ...@@ -2963,12 +3257,12 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
.policy = nft_set_policy, .policy = nft_set_policy,
}, },
[NFT_MSG_DELSET] = { [NFT_MSG_DELSET] = {
.call = nf_tables_delset, .call_batch = nf_tables_delset,
.attr_count = NFTA_SET_MAX, .attr_count = NFTA_SET_MAX,
.policy = nft_set_policy, .policy = nft_set_policy,
}, },
[NFT_MSG_NEWSETELEM] = { [NFT_MSG_NEWSETELEM] = {
.call = nf_tables_newsetelem, .call_batch = nf_tables_newsetelem,
.attr_count = NFTA_SET_ELEM_LIST_MAX, .attr_count = NFTA_SET_ELEM_LIST_MAX,
.policy = nft_set_elem_list_policy, .policy = nft_set_elem_list_policy,
}, },
...@@ -2978,12 +3272,270 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = { ...@@ -2978,12 +3272,270 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
.policy = nft_set_elem_list_policy, .policy = nft_set_elem_list_policy,
}, },
[NFT_MSG_DELSETELEM] = { [NFT_MSG_DELSETELEM] = {
.call = nf_tables_delsetelem, .call_batch = nf_tables_delsetelem,
.attr_count = NFTA_SET_ELEM_LIST_MAX, .attr_count = NFTA_SET_ELEM_LIST_MAX,
.policy = nft_set_elem_list_policy, .policy = nft_set_elem_list_policy,
}, },
}; };
static void nft_chain_commit_update(struct nft_trans *trans)
{
struct nft_base_chain *basechain;
if (nft_trans_chain_name(trans)[0])
strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans));
if (!(trans->ctx.chain->flags & NFT_BASE_CHAIN))
return;
basechain = nft_base_chain(trans->ctx.chain);
nft_chain_stats_replace(basechain, nft_trans_chain_stats(trans));
switch (nft_trans_chain_policy(trans)) {
case NF_DROP:
case NF_ACCEPT:
basechain->policy = nft_trans_chain_policy(trans);
break;
}
}
/* Schedule objects for release via rcu to make sure no packets are accesing
* removed rules.
*/
static void nf_tables_commit_release_rcu(struct rcu_head *rt)
{
struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
switch (trans->msg_type) {
case NFT_MSG_DELTABLE:
nf_tables_table_destroy(&trans->ctx);
break;
case NFT_MSG_DELCHAIN:
nf_tables_chain_destroy(trans->ctx.chain);
break;
case NFT_MSG_DELRULE:
nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
break;
case NFT_MSG_DELSET:
nft_set_destroy(nft_trans_set(trans));
break;
}
kfree(trans);
}
static int nf_tables_commit(struct sk_buff *skb)
{
struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next;
struct nft_set *set;
/* Bump generation counter, invalidate any dump in progress */
net->nft.genctr++;
/* A new generation has just started */
net->nft.gencursor = gencursor_next(net);
/* Make sure all packets have left the previous generation before
* purging old rules.
*/
synchronize_rcu();
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
switch (trans->msg_type) {
case NFT_MSG_NEWTABLE:
if (nft_trans_table_update(trans)) {
if (!nft_trans_table_enable(trans)) {
nf_tables_table_disable(trans->ctx.afi,
trans->ctx.table);
trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
}
} else {
trans->ctx.table->flags &= ~NFT_TABLE_INACTIVE;
}
nf_tables_table_notify(&trans->ctx, NFT_MSG_NEWTABLE);
nft_trans_destroy(trans);
break;
case NFT_MSG_DELTABLE:
nf_tables_table_notify(&trans->ctx, NFT_MSG_DELTABLE);
break;
case NFT_MSG_NEWCHAIN:
if (nft_trans_chain_update(trans))
nft_chain_commit_update(trans);
else {
trans->ctx.chain->flags &= ~NFT_CHAIN_INACTIVE;
trans->ctx.table->use++;
}
nf_tables_chain_notify(&trans->ctx, NFT_MSG_NEWCHAIN);
nft_trans_destroy(trans);
break;
case NFT_MSG_DELCHAIN:
trans->ctx.table->use--;
nf_tables_chain_notify(&trans->ctx, NFT_MSG_DELCHAIN);
if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
trans->ctx.chain->flags & NFT_BASE_CHAIN) {
nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
trans->ctx.afi->nops);
}
break;
case NFT_MSG_NEWRULE:
nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
nf_tables_rule_notify(&trans->ctx,
nft_trans_rule(trans),
NFT_MSG_NEWRULE);
nft_trans_destroy(trans);
break;
case NFT_MSG_DELRULE:
list_del_rcu(&nft_trans_rule(trans)->list);
nf_tables_rule_notify(&trans->ctx,
nft_trans_rule(trans),
NFT_MSG_DELRULE);
break;
case NFT_MSG_NEWSET:
nft_trans_set(trans)->flags &= ~NFT_SET_INACTIVE;
nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
NFT_MSG_NEWSET);
nft_trans_destroy(trans);
break;
case NFT_MSG_DELSET:
nf_tables_set_notify(&trans->ctx, nft_trans_set(trans),
NFT_MSG_DELSET);
break;
case NFT_MSG_NEWSETELEM:
nft_trans_elem_set(trans)->nelems++;
nf_tables_setelem_notify(&trans->ctx,
nft_trans_elem_set(trans),
&nft_trans_elem(trans),
NFT_MSG_NEWSETELEM, 0);
nft_trans_destroy(trans);
break;
case NFT_MSG_DELSETELEM:
nft_trans_elem_set(trans)->nelems--;
nf_tables_setelem_notify(&trans->ctx,
nft_trans_elem_set(trans),
&nft_trans_elem(trans),
NFT_MSG_DELSETELEM, 0);
set = nft_trans_elem_set(trans);
set->ops->get(set, &nft_trans_elem(trans));
set->ops->remove(set, &nft_trans_elem(trans));
nft_trans_destroy(trans);
break;
}
}
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
list_del(&trans->list);
trans->ctx.nla = NULL;
call_rcu(&trans->rcu_head, nf_tables_commit_release_rcu);
}
return 0;
}
/* Schedule objects for release via rcu to make sure no packets are accesing
* aborted rules.
*/
static void nf_tables_abort_release_rcu(struct rcu_head *rt)
{
struct nft_trans *trans = container_of(rt, struct nft_trans, rcu_head);
switch (trans->msg_type) {
case NFT_MSG_NEWTABLE:
nf_tables_table_destroy(&trans->ctx);
break;
case NFT_MSG_NEWCHAIN:
nf_tables_chain_destroy(trans->ctx.chain);
break;
case NFT_MSG_NEWRULE:
nf_tables_rule_destroy(&trans->ctx, nft_trans_rule(trans));
break;
case NFT_MSG_NEWSET:
nft_set_destroy(nft_trans_set(trans));
break;
}
kfree(trans);
}
static int nf_tables_abort(struct sk_buff *skb)
{
struct net *net = sock_net(skb->sk);
struct nft_trans *trans, *next;
struct nft_set *set;
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
switch (trans->msg_type) {
case NFT_MSG_NEWTABLE:
if (nft_trans_table_update(trans)) {
if (nft_trans_table_enable(trans)) {
nf_tables_table_disable(trans->ctx.afi,
trans->ctx.table);
trans->ctx.table->flags |= NFT_TABLE_F_DORMANT;
}
nft_trans_destroy(trans);
} else {
list_del(&trans->ctx.table->list);
}
break;
case NFT_MSG_DELTABLE:
list_add_tail(&trans->ctx.table->list,
&trans->ctx.afi->tables);
nft_trans_destroy(trans);
break;
case NFT_MSG_NEWCHAIN:
if (nft_trans_chain_update(trans)) {
if (nft_trans_chain_stats(trans))
free_percpu(nft_trans_chain_stats(trans));
nft_trans_destroy(trans);
} else {
list_del(&trans->ctx.chain->list);
if (!(trans->ctx.table->flags & NFT_TABLE_F_DORMANT) &&
trans->ctx.chain->flags & NFT_BASE_CHAIN) {
nf_unregister_hooks(nft_base_chain(trans->ctx.chain)->ops,
trans->ctx.afi->nops);
}
}
break;
case NFT_MSG_DELCHAIN:
list_add_tail(&trans->ctx.chain->list,
&trans->ctx.table->chains);
nft_trans_destroy(trans);
break;
case NFT_MSG_NEWRULE:
list_del_rcu(&nft_trans_rule(trans)->list);
break;
case NFT_MSG_DELRULE:
nft_rule_clear(trans->ctx.net, nft_trans_rule(trans));
nft_trans_destroy(trans);
break;
case NFT_MSG_NEWSET:
list_del(&nft_trans_set(trans)->list);
break;
case NFT_MSG_DELSET:
list_add_tail(&nft_trans_set(trans)->list,
&trans->ctx.table->sets);
nft_trans_destroy(trans);
break;
case NFT_MSG_NEWSETELEM:
set = nft_trans_elem_set(trans);
set->ops->get(set, &nft_trans_elem(trans));
set->ops->remove(set, &nft_trans_elem(trans));
nft_trans_destroy(trans);
break;
case NFT_MSG_DELSETELEM:
nft_trans_destroy(trans);
break;
}
}
list_for_each_entry_safe(trans, next, &net->nft.commit_list, list) {
list_del(&trans->list);
trans->ctx.nla = NULL;
call_rcu(&trans->rcu_head, nf_tables_abort_release_rcu);
}
return 0;
}
static const struct nfnetlink_subsystem nf_tables_subsys = { static const struct nfnetlink_subsystem nf_tables_subsys = {
.name = "nf_tables", .name = "nf_tables",
.subsys_id = NFNL_SUBSYS_NFTABLES, .subsys_id = NFNL_SUBSYS_NFTABLES,
......
...@@ -215,22 +215,14 @@ static void nft_ct_l3proto_module_put(uint8_t family) ...@@ -215,22 +215,14 @@ static void nft_ct_l3proto_module_put(uint8_t family)
nf_ct_l3proto_module_put(family); nf_ct_l3proto_module_put(family);
} }
static int nft_ct_init_validate_get(const struct nft_expr *expr, static int nft_ct_get_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[]) const struct nft_expr *expr,
const struct nlattr * const tb[])
{ {
struct nft_ct *priv = nft_expr_priv(expr); struct nft_ct *priv = nft_expr_priv(expr);
int err;
if (tb[NFTA_CT_DIRECTION] != NULL) { priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
switch (priv->dir) {
case IP_CT_DIR_ORIGINAL:
case IP_CT_DIR_REPLY:
break;
default:
return -EINVAL;
}
}
switch (priv->key) { switch (priv->key) {
case NFT_CT_STATE: case NFT_CT_STATE:
case NFT_CT_DIRECTION: case NFT_CT_DIRECTION:
...@@ -262,55 +254,55 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr, ...@@ -262,55 +254,55 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr,
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
return 0; if (tb[NFTA_CT_DIRECTION] != NULL) {
} priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
switch (priv->dir) {
static int nft_ct_init_validate_set(uint32_t key) case IP_CT_DIR_ORIGINAL:
{ case IP_CT_DIR_REPLY:
switch (key) { break;
case NFT_CT_MARK: default:
break; return -EINVAL;
default: }
return -EOPNOTSUPP;
} }
priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
err = nft_validate_output_register(priv->dreg);
if (err < 0)
return err;
err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
if (err < 0)
return err;
err = nft_ct_l3proto_try_module_get(ctx->afi->family);
if (err < 0)
return err;
return 0; return 0;
} }
static int nft_ct_init(const struct nft_ctx *ctx, static int nft_ct_set_init(const struct nft_ctx *ctx,
const struct nft_expr *expr, const struct nft_expr *expr,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_ct *priv = nft_expr_priv(expr); struct nft_ct *priv = nft_expr_priv(expr);
int err; int err;
priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
switch (priv->key) {
if (tb[NFTA_CT_DREG]) { #ifdef CONFIG_NF_CONNTRACK_MARK
err = nft_ct_init_validate_get(expr, tb); case NFT_CT_MARK:
if (err < 0) break;
return err; #endif
default:
priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); return -EOPNOTSUPP;
err = nft_validate_output_register(priv->dreg);
if (err < 0)
return err;
err = nft_validate_data_load(ctx, priv->dreg, NULL,
NFT_DATA_VALUE);
if (err < 0)
return err;
} else {
err = nft_ct_init_validate_set(priv->key);
if (err < 0)
return err;
priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
err = nft_validate_input_register(priv->sreg);
if (err < 0)
return err;
} }
priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
err = nft_validate_input_register(priv->sreg);
if (err < 0)
return err;
err = nft_ct_l3proto_try_module_get(ctx->afi->family); err = nft_ct_l3proto_try_module_get(ctx->afi->family);
if (err < 0) if (err < 0)
return err; return err;
...@@ -370,7 +362,7 @@ static const struct nft_expr_ops nft_ct_get_ops = { ...@@ -370,7 +362,7 @@ static const struct nft_expr_ops nft_ct_get_ops = {
.type = &nft_ct_type, .type = &nft_ct_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
.eval = nft_ct_get_eval, .eval = nft_ct_get_eval,
.init = nft_ct_init, .init = nft_ct_get_init,
.destroy = nft_ct_destroy, .destroy = nft_ct_destroy,
.dump = nft_ct_get_dump, .dump = nft_ct_get_dump,
}; };
...@@ -379,7 +371,7 @@ static const struct nft_expr_ops nft_ct_set_ops = { ...@@ -379,7 +371,7 @@ static const struct nft_expr_ops nft_ct_set_ops = {
.type = &nft_ct_type, .type = &nft_ct_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
.eval = nft_ct_set_eval, .eval = nft_ct_set_eval,
.init = nft_ct_init, .init = nft_ct_set_init,
.destroy = nft_ct_destroy, .destroy = nft_ct_destroy,
.dump = nft_ct_set_dump, .dump = nft_ct_set_dump,
}; };
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/log2.h>
#include <linux/jhash.h> #include <linux/jhash.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
...@@ -19,7 +20,7 @@ ...@@ -19,7 +20,7 @@
#include <linux/netfilter/nf_tables.h> #include <linux/netfilter/nf_tables.h>
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#define NFT_HASH_MIN_SIZE 4 #define NFT_HASH_MIN_SIZE 4UL
struct nft_hash { struct nft_hash {
struct nft_hash_table __rcu *tbl; struct nft_hash_table __rcu *tbl;
...@@ -27,7 +28,6 @@ struct nft_hash { ...@@ -27,7 +28,6 @@ struct nft_hash {
struct nft_hash_table { struct nft_hash_table {
unsigned int size; unsigned int size;
unsigned int elements;
struct nft_hash_elem __rcu *buckets[]; struct nft_hash_elem __rcu *buckets[];
}; };
...@@ -82,6 +82,11 @@ static void nft_hash_tbl_free(const struct nft_hash_table *tbl) ...@@ -82,6 +82,11 @@ static void nft_hash_tbl_free(const struct nft_hash_table *tbl)
kfree(tbl); kfree(tbl);
} }
static unsigned int nft_hash_tbl_size(unsigned int nelem)
{
return max(roundup_pow_of_two(nelem * 4 / 3), NFT_HASH_MIN_SIZE);
}
static struct nft_hash_table *nft_hash_tbl_alloc(unsigned int nbuckets) static struct nft_hash_table *nft_hash_tbl_alloc(unsigned int nbuckets)
{ {
struct nft_hash_table *tbl; struct nft_hash_table *tbl;
...@@ -161,7 +166,6 @@ static int nft_hash_tbl_expand(const struct nft_set *set, struct nft_hash *priv) ...@@ -161,7 +166,6 @@ static int nft_hash_tbl_expand(const struct nft_set *set, struct nft_hash *priv)
break; break;
} }
} }
ntbl->elements = tbl->elements;
/* Publish new table */ /* Publish new table */
rcu_assign_pointer(priv->tbl, ntbl); rcu_assign_pointer(priv->tbl, ntbl);
...@@ -201,7 +205,6 @@ static int nft_hash_tbl_shrink(const struct nft_set *set, struct nft_hash *priv) ...@@ -201,7 +205,6 @@ static int nft_hash_tbl_shrink(const struct nft_set *set, struct nft_hash *priv)
; ;
RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]); RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]);
} }
ntbl->elements = tbl->elements;
/* Publish new table */ /* Publish new table */
rcu_assign_pointer(priv->tbl, ntbl); rcu_assign_pointer(priv->tbl, ntbl);
...@@ -237,10 +240,9 @@ static int nft_hash_insert(const struct nft_set *set, ...@@ -237,10 +240,9 @@ static int nft_hash_insert(const struct nft_set *set,
h = nft_hash_data(&he->key, tbl->size, set->klen); h = nft_hash_data(&he->key, tbl->size, set->klen);
RCU_INIT_POINTER(he->next, tbl->buckets[h]); RCU_INIT_POINTER(he->next, tbl->buckets[h]);
rcu_assign_pointer(tbl->buckets[h], he); rcu_assign_pointer(tbl->buckets[h], he);
tbl->elements++;
/* Expand table when exceeding 75% load */ /* Expand table when exceeding 75% load */
if (tbl->elements > tbl->size / 4 * 3) if (set->nelems + 1 > tbl->size / 4 * 3)
nft_hash_tbl_expand(set, priv); nft_hash_tbl_expand(set, priv);
return 0; return 0;
...@@ -268,10 +270,9 @@ static void nft_hash_remove(const struct nft_set *set, ...@@ -268,10 +270,9 @@ static void nft_hash_remove(const struct nft_set *set,
RCU_INIT_POINTER(*pprev, he->next); RCU_INIT_POINTER(*pprev, he->next);
synchronize_rcu(); synchronize_rcu();
kfree(he); kfree(he);
tbl->elements--;
/* Shrink table beneath 30% load */ /* Shrink table beneath 30% load */
if (tbl->elements < tbl->size * 3 / 10 && if (set->nelems - 1 < tbl->size * 3 / 10 &&
tbl->size > NFT_HASH_MIN_SIZE) tbl->size > NFT_HASH_MIN_SIZE)
nft_hash_tbl_shrink(set, priv); nft_hash_tbl_shrink(set, priv);
} }
...@@ -335,17 +336,23 @@ static unsigned int nft_hash_privsize(const struct nlattr * const nla[]) ...@@ -335,17 +336,23 @@ static unsigned int nft_hash_privsize(const struct nlattr * const nla[])
} }
static int nft_hash_init(const struct nft_set *set, static int nft_hash_init(const struct nft_set *set,
const struct nft_set_desc *desc,
const struct nlattr * const tb[]) const struct nlattr * const tb[])
{ {
struct nft_hash *priv = nft_set_priv(set); struct nft_hash *priv = nft_set_priv(set);
struct nft_hash_table *tbl; struct nft_hash_table *tbl;
unsigned int size;
if (unlikely(!nft_hash_rnd_initted)) { if (unlikely(!nft_hash_rnd_initted)) {
get_random_bytes(&nft_hash_rnd, 4); get_random_bytes(&nft_hash_rnd, 4);
nft_hash_rnd_initted = true; nft_hash_rnd_initted = true;
} }
tbl = nft_hash_tbl_alloc(NFT_HASH_MIN_SIZE); size = NFT_HASH_MIN_SIZE;
if (desc->size)
size = nft_hash_tbl_size(desc->size);
tbl = nft_hash_tbl_alloc(size);
if (tbl == NULL) if (tbl == NULL)
return -ENOMEM; return -ENOMEM;
RCU_INIT_POINTER(priv->tbl, tbl); RCU_INIT_POINTER(priv->tbl, tbl);
...@@ -369,8 +376,37 @@ static void nft_hash_destroy(const struct nft_set *set) ...@@ -369,8 +376,37 @@ static void nft_hash_destroy(const struct nft_set *set)
kfree(tbl); kfree(tbl);
} }
static bool nft_hash_estimate(const struct nft_set_desc *desc, u32 features,
struct nft_set_estimate *est)
{
unsigned int esize;
esize = sizeof(struct nft_hash_elem);
if (features & NFT_SET_MAP)
esize += FIELD_SIZEOF(struct nft_hash_elem, data[0]);
if (desc->size) {
est->size = sizeof(struct nft_hash) +
nft_hash_tbl_size(desc->size) *
sizeof(struct nft_hash_elem *) +
desc->size * esize;
} else {
/* Resizing happens when the load drops below 30% or goes
* above 75%. The average of 52.5% load (approximated by 50%)
* is used for the size estimation of the hash buckets,
* meaning we calculate two buckets per element.
*/
est->size = esize + 2 * sizeof(struct nft_hash_elem *);
}
est->class = NFT_SET_CLASS_O_1;
return true;
}
static struct nft_set_ops nft_hash_ops __read_mostly = { static struct nft_set_ops nft_hash_ops __read_mostly = {
.privsize = nft_hash_privsize, .privsize = nft_hash_privsize,
.estimate = nft_hash_estimate,
.init = nft_hash_init, .init = nft_hash_init,
.destroy = nft_hash_destroy, .destroy = nft_hash_destroy,
.get = nft_hash_get, .get = nft_hash_get,
......
...@@ -56,8 +56,14 @@ static int nft_lookup_init(const struct nft_ctx *ctx, ...@@ -56,8 +56,14 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
return -EINVAL; return -EINVAL;
set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]); set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]);
if (IS_ERR(set)) if (IS_ERR(set)) {
return PTR_ERR(set); if (tb[NFTA_LOOKUP_SET_ID]) {
set = nf_tables_set_lookup_byid(ctx->net,
tb[NFTA_LOOKUP_SET_ID]);
}
if (IS_ERR(set))
return PTR_ERR(set);
}
priv->sreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_SREG])); priv->sreg = ntohl(nla_get_be32(tb[NFTA_LOOKUP_SREG]));
err = nft_validate_input_register(priv->sreg); err = nft_validate_input_register(priv->sreg);
......
...@@ -18,18 +18,11 @@ ...@@ -18,18 +18,11 @@
#include <net/sock.h> #include <net/sock.h>
#include <net/tcp_states.h> /* for TCP_TIME_WAIT */ #include <net/tcp_states.h> /* for TCP_TIME_WAIT */
#include <net/netfilter/nf_tables.h> #include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_meta.h>
struct nft_meta { void nft_meta_get_eval(const struct nft_expr *expr,
enum nft_meta_keys key:8; struct nft_data data[NFT_REG_MAX + 1],
union { const struct nft_pktinfo *pkt)
enum nft_registers dreg:8;
enum nft_registers sreg:8;
};
};
static void nft_meta_get_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt)
{ {
const struct nft_meta *priv = nft_expr_priv(expr); const struct nft_meta *priv = nft_expr_priv(expr);
const struct sk_buff *skb = pkt->skb; const struct sk_buff *skb = pkt->skb;
...@@ -140,10 +133,11 @@ static void nft_meta_get_eval(const struct nft_expr *expr, ...@@ -140,10 +133,11 @@ static void nft_meta_get_eval(const struct nft_expr *expr,
err: err:
data[NFT_REG_VERDICT].verdict = NFT_BREAK; data[NFT_REG_VERDICT].verdict = NFT_BREAK;
} }
EXPORT_SYMBOL_GPL(nft_meta_get_eval);
static void nft_meta_set_eval(const struct nft_expr *expr, void nft_meta_set_eval(const struct nft_expr *expr,
struct nft_data data[NFT_REG_MAX + 1], struct nft_data data[NFT_REG_MAX + 1],
const struct nft_pktinfo *pkt) const struct nft_pktinfo *pkt)
{ {
const struct nft_meta *meta = nft_expr_priv(expr); const struct nft_meta *meta = nft_expr_priv(expr);
struct sk_buff *skb = pkt->skb; struct sk_buff *skb = pkt->skb;
...@@ -163,28 +157,24 @@ static void nft_meta_set_eval(const struct nft_expr *expr, ...@@ -163,28 +157,24 @@ static void nft_meta_set_eval(const struct nft_expr *expr,
WARN_ON(1); WARN_ON(1);
} }
} }
EXPORT_SYMBOL_GPL(nft_meta_set_eval);
static const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = { const struct nla_policy nft_meta_policy[NFTA_META_MAX + 1] = {
[NFTA_META_DREG] = { .type = NLA_U32 }, [NFTA_META_DREG] = { .type = NLA_U32 },
[NFTA_META_KEY] = { .type = NLA_U32 }, [NFTA_META_KEY] = { .type = NLA_U32 },
[NFTA_META_SREG] = { .type = NLA_U32 }, [NFTA_META_SREG] = { .type = NLA_U32 },
}; };
EXPORT_SYMBOL_GPL(nft_meta_policy);
static int nft_meta_init_validate_set(uint32_t key) int nft_meta_get_init(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nlattr * const tb[])
{ {
switch (key) { struct nft_meta *priv = nft_expr_priv(expr);
case NFT_META_MARK: int err;
case NFT_META_PRIORITY:
case NFT_META_NFTRACE:
return 0;
default:
return -EOPNOTSUPP;
}
}
static int nft_meta_init_validate_get(uint32_t key) priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
{ switch (priv->key) {
switch (key) {
case NFT_META_LEN: case NFT_META_LEN:
case NFT_META_PROTOCOL: case NFT_META_PROTOCOL:
case NFT_META_NFPROTO: case NFT_META_NFPROTO:
...@@ -205,39 +195,41 @@ static int nft_meta_init_validate_get(uint32_t key) ...@@ -205,39 +195,41 @@ static int nft_meta_init_validate_get(uint32_t key)
#ifdef CONFIG_NETWORK_SECMARK #ifdef CONFIG_NETWORK_SECMARK
case NFT_META_SECMARK: case NFT_META_SECMARK:
#endif #endif
return 0; break;
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG]));
err = nft_validate_output_register(priv->dreg);
if (err < 0)
return err;
err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
if (err < 0)
return err;
return 0;
} }
EXPORT_SYMBOL_GPL(nft_meta_get_init);
static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr, int nft_meta_set_init(const struct nft_ctx *ctx,
const struct nlattr * const tb[]) const struct nft_expr *expr,
const struct nlattr * const tb[])
{ {
struct nft_meta *priv = nft_expr_priv(expr); struct nft_meta *priv = nft_expr_priv(expr);
int err; int err;
priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY])); priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
switch (priv->key) {
if (tb[NFTA_META_DREG]) { case NFT_META_MARK:
err = nft_meta_init_validate_get(priv->key); case NFT_META_PRIORITY:
if (err < 0) case NFT_META_NFTRACE:
return err; break;
default:
priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG])); return -EOPNOTSUPP;
err = nft_validate_output_register(priv->dreg);
if (err < 0)
return err;
return nft_validate_data_load(ctx, priv->dreg, NULL,
NFT_DATA_VALUE);
} }
err = nft_meta_init_validate_set(priv->key);
if (err < 0)
return err;
priv->sreg = ntohl(nla_get_be32(tb[NFTA_META_SREG])); priv->sreg = ntohl(nla_get_be32(tb[NFTA_META_SREG]));
err = nft_validate_input_register(priv->sreg); err = nft_validate_input_register(priv->sreg);
if (err < 0) if (err < 0)
...@@ -245,9 +237,10 @@ static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr, ...@@ -245,9 +237,10 @@ static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(nft_meta_set_init);
static int nft_meta_get_dump(struct sk_buff *skb, int nft_meta_get_dump(struct sk_buff *skb,
const struct nft_expr *expr) const struct nft_expr *expr)
{ {
const struct nft_meta *priv = nft_expr_priv(expr); const struct nft_meta *priv = nft_expr_priv(expr);
...@@ -260,9 +253,10 @@ static int nft_meta_get_dump(struct sk_buff *skb, ...@@ -260,9 +253,10 @@ static int nft_meta_get_dump(struct sk_buff *skb,
nla_put_failure: nla_put_failure:
return -1; return -1;
} }
EXPORT_SYMBOL_GPL(nft_meta_get_dump);
static int nft_meta_set_dump(struct sk_buff *skb, int nft_meta_set_dump(struct sk_buff *skb,
const struct nft_expr *expr) const struct nft_expr *expr)
{ {
const struct nft_meta *priv = nft_expr_priv(expr); const struct nft_meta *priv = nft_expr_priv(expr);
...@@ -276,13 +270,14 @@ static int nft_meta_set_dump(struct sk_buff *skb, ...@@ -276,13 +270,14 @@ static int nft_meta_set_dump(struct sk_buff *skb,
nla_put_failure: nla_put_failure:
return -1; return -1;
} }
EXPORT_SYMBOL_GPL(nft_meta_set_dump);
static struct nft_expr_type nft_meta_type; static struct nft_expr_type nft_meta_type;
static const struct nft_expr_ops nft_meta_get_ops = { static const struct nft_expr_ops nft_meta_get_ops = {
.type = &nft_meta_type, .type = &nft_meta_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_get_eval, .eval = nft_meta_get_eval,
.init = nft_meta_init, .init = nft_meta_get_init,
.dump = nft_meta_get_dump, .dump = nft_meta_get_dump,
}; };
...@@ -290,7 +285,7 @@ static const struct nft_expr_ops nft_meta_set_ops = { ...@@ -290,7 +285,7 @@ static const struct nft_expr_ops nft_meta_set_ops = {
.type = &nft_meta_type, .type = &nft_meta_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)), .size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_set_eval, .eval = nft_meta_set_eval,
.init = nft_meta_init, .init = nft_meta_set_init,
.dump = nft_meta_set_dump, .dump = nft_meta_set_dump,
}; };
......
...@@ -201,6 +201,7 @@ static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[]) ...@@ -201,6 +201,7 @@ static unsigned int nft_rbtree_privsize(const struct nlattr * const nla[])
} }
static int nft_rbtree_init(const struct nft_set *set, static int nft_rbtree_init(const struct nft_set *set,
const struct nft_set_desc *desc,
const struct nlattr * const nla[]) const struct nlattr * const nla[])
{ {
struct nft_rbtree *priv = nft_set_priv(set); struct nft_rbtree *priv = nft_set_priv(set);
...@@ -222,8 +223,28 @@ static void nft_rbtree_destroy(const struct nft_set *set) ...@@ -222,8 +223,28 @@ static void nft_rbtree_destroy(const struct nft_set *set)
} }
} }
static bool nft_rbtree_estimate(const struct nft_set_desc *desc, u32 features,
struct nft_set_estimate *est)
{
unsigned int nsize;
nsize = sizeof(struct nft_rbtree_elem);
if (features & NFT_SET_MAP)
nsize += FIELD_SIZEOF(struct nft_rbtree_elem, data[0]);
if (desc->size)
est->size = sizeof(struct nft_rbtree) + desc->size * nsize;
else
est->size = nsize;
est->class = NFT_SET_CLASS_O_LOG_N;
return true;
}
static struct nft_set_ops nft_rbtree_ops __read_mostly = { static struct nft_set_ops nft_rbtree_ops __read_mostly = {
.privsize = nft_rbtree_privsize, .privsize = nft_rbtree_privsize,
.estimate = nft_rbtree_estimate,
.init = nft_rbtree_init, .init = nft_rbtree_init,
.destroy = nft_rbtree_destroy, .destroy = nft_rbtree_destroy,
.insert = nft_rbtree_insert, .insert = nft_rbtree_insert,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment