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)
* struct nft_ctx - nf_tables rule/set context
*
* @net: net namespace
* @skb: netlink skb
* @nlh: netlink message header
* @afi: address family info
* @table: the table the chain is contained in
* @chain: the chain the rule is contained in
* @nla: netlink attributes
* @portid: netlink portID of the original message
* @seq: netlink sequence number
* @report: notify via unicast netlink message
*/
struct nft_ctx {
struct net *net;
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;
struct nft_af_info *afi;
struct nft_table *table;
struct nft_chain *chain;
const struct nlattr * const *nla;
u32 portid;
u32 seq;
bool report;
};
struct nft_data_desc {
......@@ -145,6 +147,44 @@ struct nft_set_iter {
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
*
......@@ -174,7 +214,11 @@ struct nft_set_ops {
struct nft_set_iter *iter);
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,
const struct nft_set_desc *desc,
const struct nlattr * const nla[]);
void (*destroy)(const struct nft_set *set);
......@@ -194,6 +238,8 @@ void nft_unregister_set(struct nft_set_ops *ops);
* @name: name of the set
* @ktype: key type (numeric type defined by userspace, not used in the kernel)
* @dtype: data type (verdict or numeric type defined by userspace)
* @size: maximum set size
* @nelems: number of elements
* @ops: set ops
* @flags: set flags
* @klen: key length
......@@ -206,6 +252,8 @@ struct nft_set {
char name[IFNAMSIZ];
u32 ktype;
u32 dtype;
u32 size;
u32 nelems;
/* runtime data below here */
const struct nft_set_ops *ops ____cacheline_aligned;
u16 flags;
......@@ -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,
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
......@@ -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
* @ctx: rule context
* @rule: rule that needs to be updated
* @msg_type: message type
* @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;
int msg_type;
struct nft_ctx ctx;
char data[0];
};
struct nft_trans_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)
{
return (struct nft_expr *)&rule->data[0];
......@@ -385,6 +492,7 @@ static inline void *nft_userdata(const struct nft_rule *rule)
enum nft_chain_flags {
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 {
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
*
......@@ -221,6 +244,9 @@ enum nft_set_flags {
* @NFTA_SET_KEY_LEN: key data length (NLA_U32)
* @NFTA_SET_DATA_TYPE: mapping data type (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 {
NFTA_SET_UNSPEC,
......@@ -231,6 +257,9 @@ enum nft_set_attributes {
NFTA_SET_KEY_LEN,
NFTA_SET_DATA_TYPE,
NFTA_SET_DATA_LEN,
NFTA_SET_POLICY,
NFTA_SET_DESC,
NFTA_SET_ID,
__NFTA_SET_MAX
};
#define NFTA_SET_MAX (__NFTA_SET_MAX - 1)
......@@ -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_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_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
*/
enum nft_set_elem_list_attributes {
NFTA_SET_ELEM_LIST_UNSPEC,
NFTA_SET_ELEM_LIST_TABLE,
NFTA_SET_ELEM_LIST_SET,
NFTA_SET_ELEM_LIST_ELEMENTS,
NFTA_SET_ELEM_LIST_SET_ID,
__NFTA_SET_ELEM_LIST_MAX
};
#define NFTA_SET_ELEM_LIST_MAX (__NFTA_SET_ELEM_LIST_MAX - 1)
......@@ -457,12 +488,14 @@ enum nft_cmp_attributes {
* @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_DREG: destination register (NLA_U32: nft_registers)
* @NFTA_LOOKUP_SET_ID: uniquely identifies a set in a transaction (NLA_U32)
*/
enum nft_lookup_attributes {
NFTA_LOOKUP_UNSPEC,
NFTA_LOOKUP_SET,
NFTA_LOOKUP_SREG,
NFTA_LOOKUP_DREG,
NFTA_LOOKUP_SET_ID,
__NFTA_LOOKUP_MAX
};
#define NFTA_LOOKUP_MAX (__NFTA_LOOKUP_MAX - 1)
......@@ -536,6 +569,8 @@ enum nft_exthdr_attributes {
* @NFT_META_SECMARK: packet secmark (skb->secmark)
* @NFT_META_NFPROTO: netfilter protocol
* @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 {
NFT_META_LEN,
......@@ -555,6 +590,8 @@ enum nft_meta_keys {
NFT_META_SECMARK,
NFT_META_NFPROTO,
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
bridge-$(CONFIG_BRIDGE_VLAN_FILTERING) += br_vlan.o
obj-$(CONFIG_BRIDGE_NF_EBTABLES) += netfilter/
obj-$(CONFIG_BRIDGE_NETFILTER) += netfilter/
......@@ -2,13 +2,25 @@
# Bridge netfilter configuration
#
#
config NF_TABLES_BRIDGE
menuconfig NF_TABLES_BRIDGE
depends on NF_TABLES
select BRIDGE_NETFILTER
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
tristate "Ethernet Bridge tables (ebtables) support"
depends on BRIDGE && NETFILTER
select BRIDGE_NETFILTER
select NETFILTER_XTABLES
help
ebtables is a general, extensible frame/packet identification
......
......@@ -3,6 +3,7 @@
#
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
......
/*
* 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");
This diff is collapsed.
......@@ -215,22 +215,14 @@ static void nft_ct_l3proto_module_put(uint8_t 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 nft_expr *expr,
const struct nlattr * const tb[])
{
struct nft_ct *priv = nft_expr_priv(expr);
int err;
if (tb[NFTA_CT_DIRECTION] != NULL) {
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;
}
}
priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
switch (priv->key) {
case NFT_CT_STATE:
case NFT_CT_DIRECTION:
......@@ -262,22 +254,34 @@ static int nft_ct_init_validate_get(const struct nft_expr *expr,
return -EOPNOTSUPP;
}
return 0;
}
static int nft_ct_init_validate_set(uint32_t key)
{
switch (key) {
case NFT_CT_MARK:
if (tb[NFTA_CT_DIRECTION] != NULL) {
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 -EOPNOTSUPP;
return -EINVAL;
}
}
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;
}
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 nlattr * const tb[])
{
......@@ -285,31 +289,19 @@ static int nft_ct_init(const struct nft_ctx *ctx,
int err;
priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
if (tb[NFTA_CT_DREG]) {
err = nft_ct_init_validate_get(expr, tb);
if (err < 0)
return err;
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;
} else {
err = nft_ct_init_validate_set(priv->key);
if (err < 0)
return err;
switch (priv->key) {
#ifdef CONFIG_NF_CONNTRACK_MARK
case NFT_CT_MARK:
break;
#endif
default:
return -EOPNOTSUPP;
}
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);
if (err < 0)
......@@ -370,7 +362,7 @@ static const struct nft_expr_ops nft_ct_get_ops = {
.type = &nft_ct_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
.eval = nft_ct_get_eval,
.init = nft_ct_init,
.init = nft_ct_get_init,
.destroy = nft_ct_destroy,
.dump = nft_ct_get_dump,
};
......@@ -379,7 +371,7 @@ static const struct nft_expr_ops nft_ct_set_ops = {
.type = &nft_ct_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
.eval = nft_ct_set_eval,
.init = nft_ct_init,
.init = nft_ct_set_init,
.destroy = nft_ct_destroy,
.dump = nft_ct_set_dump,
};
......
......@@ -12,6 +12,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/list.h>
#include <linux/log2.h>
#include <linux/jhash.h>
#include <linux/netlink.h>
#include <linux/vmalloc.h>
......@@ -19,7 +20,7 @@
#include <linux/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_table __rcu *tbl;
......@@ -27,7 +28,6 @@ struct nft_hash {
struct nft_hash_table {
unsigned int size;
unsigned int elements;
struct nft_hash_elem __rcu *buckets[];
};
......@@ -82,6 +82,11 @@ static void nft_hash_tbl_free(const struct nft_hash_table *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)
{
struct nft_hash_table *tbl;
......@@ -161,7 +166,6 @@ static int nft_hash_tbl_expand(const struct nft_set *set, struct nft_hash *priv)
break;
}
}
ntbl->elements = tbl->elements;
/* Publish new table */
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)
;
RCU_INIT_POINTER(*pprev, tbl->buckets[i + ntbl->size]);
}
ntbl->elements = tbl->elements;
/* Publish new table */
rcu_assign_pointer(priv->tbl, ntbl);
......@@ -237,10 +240,9 @@ static int nft_hash_insert(const struct nft_set *set,
h = nft_hash_data(&he->key, tbl->size, set->klen);
RCU_INIT_POINTER(he->next, tbl->buckets[h]);
rcu_assign_pointer(tbl->buckets[h], he);
tbl->elements++;
/* 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);
return 0;
......@@ -268,10 +270,9 @@ static void nft_hash_remove(const struct nft_set *set,
RCU_INIT_POINTER(*pprev, he->next);
synchronize_rcu();
kfree(he);
tbl->elements--;
/* 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)
nft_hash_tbl_shrink(set, priv);
}
......@@ -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,
const struct nft_set_desc *desc,
const struct nlattr * const tb[])
{
struct nft_hash *priv = nft_set_priv(set);
struct nft_hash_table *tbl;
unsigned int size;
if (unlikely(!nft_hash_rnd_initted)) {
get_random_bytes(&nft_hash_rnd, 4);
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)
return -ENOMEM;
RCU_INIT_POINTER(priv->tbl, tbl);
......@@ -369,8 +376,37 @@ static void nft_hash_destroy(const struct nft_set *set)
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 = {
.privsize = nft_hash_privsize,
.estimate = nft_hash_estimate,
.init = nft_hash_init,
.destroy = nft_hash_destroy,
.get = nft_hash_get,
......
......@@ -56,8 +56,14 @@ static int nft_lookup_init(const struct nft_ctx *ctx,
return -EINVAL;
set = nf_tables_set_lookup(ctx->table, tb[NFTA_LOOKUP_SET]);
if (IS_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]));
err = nft_validate_input_register(priv->sreg);
......
......@@ -18,16 +18,9 @@
#include <net/sock.h>
#include <net/tcp_states.h> /* for TCP_TIME_WAIT */
#include <net/netfilter/nf_tables.h>
#include <net/netfilter/nft_meta.h>
struct nft_meta {
enum nft_meta_keys key:8;
union {
enum nft_registers dreg:8;
enum nft_registers sreg:8;
};
};
static void nft_meta_get_eval(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)
{
......@@ -140,8 +133,9 @@ static void nft_meta_get_eval(const struct nft_expr *expr,
err:
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],
const struct nft_pktinfo *pkt)
{
......@@ -163,28 +157,24 @@ static void nft_meta_set_eval(const struct nft_expr *expr,
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_KEY] = { .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) {
case NFT_META_MARK:
case NFT_META_PRIORITY:
case NFT_META_NFTRACE:
return 0;
default:
return -EOPNOTSUPP;
}
}
struct nft_meta *priv = nft_expr_priv(expr);
int err;
static int nft_meta_init_validate_get(uint32_t key)
{
switch (key) {
priv->key = ntohl(nla_get_be32(tb[NFTA_META_KEY]));
switch (priv->key) {
case NFT_META_LEN:
case NFT_META_PROTOCOL:
case NFT_META_NFPROTO:
......@@ -205,39 +195,41 @@ static int nft_meta_init_validate_get(uint32_t key)
#ifdef CONFIG_NETWORK_SECMARK
case NFT_META_SECMARK:
#endif
return 0;
break;
default:
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 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]));
if (tb[NFTA_META_DREG]) {
err = nft_meta_init_validate_get(priv->key);
if (err < 0)
return err;
priv->dreg = ntohl(nla_get_be32(tb[NFTA_META_DREG]));
err = nft_validate_output_register(priv->dreg);
if (err < 0)
return err;
return nft_validate_data_load(ctx, priv->dreg, NULL,
NFT_DATA_VALUE);
switch (priv->key) {
case NFT_META_MARK:
case NFT_META_PRIORITY:
case NFT_META_NFTRACE:
break;
default:
return -EOPNOTSUPP;
}
err = nft_meta_init_validate_set(priv->key);
if (err < 0)
return err;
priv->sreg = ntohl(nla_get_be32(tb[NFTA_META_SREG]));
err = nft_validate_input_register(priv->sreg);
if (err < 0)
......@@ -245,8 +237,9 @@ static int nft_meta_init(const struct nft_ctx *ctx, const struct nft_expr *expr,
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_meta *priv = nft_expr_priv(expr);
......@@ -260,8 +253,9 @@ static int nft_meta_get_dump(struct sk_buff *skb,
nla_put_failure:
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_meta *priv = nft_expr_priv(expr);
......@@ -276,13 +270,14 @@ static int nft_meta_set_dump(struct sk_buff *skb,
nla_put_failure:
return -1;
}
EXPORT_SYMBOL_GPL(nft_meta_set_dump);
static struct nft_expr_type nft_meta_type;
static const struct nft_expr_ops nft_meta_get_ops = {
.type = &nft_meta_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_get_eval,
.init = nft_meta_init,
.init = nft_meta_get_init,
.dump = nft_meta_get_dump,
};
......@@ -290,7 +285,7 @@ static const struct nft_expr_ops nft_meta_set_ops = {
.type = &nft_meta_type,
.size = NFT_EXPR_SIZE(sizeof(struct nft_meta)),
.eval = nft_meta_set_eval,
.init = nft_meta_init,
.init = nft_meta_set_init,
.dump = nft_meta_set_dump,
};
......
......@@ -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,
const struct nft_set_desc *desc,
const struct nlattr * const nla[])
{
struct nft_rbtree *priv = nft_set_priv(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 = {
.privsize = nft_rbtree_privsize,
.estimate = nft_rbtree_estimate,
.init = nft_rbtree_init,
.destroy = nft_rbtree_destroy,
.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