* [PATCH RFC nf-next 0/3] named expressions for nf_tables
@ 2016-04-06 16:51 Pablo Neira Ayuso
2016-04-06 16:51 ` [PATCH RFC nf-next 1/3] netfilter: nf_tables: add stateful named expressions Pablo Neira Ayuso
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Pablo Neira Ayuso @ 2016-04-06 16:51 UTC (permalink / raw)
To: netfilter-devel
Hi,
This patchset introduces the named stateful expressions for nf_tables,
that allows userspace to set a name for the stateful expression for
several reasons:
* Provide a unique identifier to fetch and reset it internal state.
* Allow to update of their parameters and internal state.
* Allow to fetch and reset its internal state.
* Refer to the same stateful expression from one or more rules.
nf_tables currently supports to stateful expressions: limit and counter,
therefore you can create named instances of this expressions through
this new infrastructure.
This batch is composed of three patches:
1) Extend the nf_tables netlink interface to add, delete, dump and reset
named expressions.
2) Add a new expression to dereference named expressions.
3) Support atomic dump and reset of named expressions.
Note that this provides a native replacement for the iptables' nfacct
infrastructure, the use of nfacct is out of question due to the lack of
integration with nftables netlink interface and its 2-phase commit
protocol.
Several examples on how this would look from userspace:
* Add the 'tcp-counter' counter to the 'filter' table:
# nft add counter filter tcp-counter
* Delete this counter (only possible if not dereferenced from a rule):
# nft delete counter filter tcp-counter
* List existing named counters:
# nft lists counters
table ip filter {
counter tcp-counter {
packets 6086 bytes 6278052
}
counter udp-counter {
packets 272 bytes 64690
}
counter icmp-counter {
packets 10 bytes 840
}
}
* Atomically fetch and reset counters:
# nft reset counters
table ip filter {
counter tcp-counter {
packets 6086 bytes 6278052
}
counter udp-counter {
packets 272 bytes 64690
}
counter icmp-counter {
packets 10 bytes 840
}
}
This retrieves the existing counter values and reset them, a follow up
listing confirms this:
# nft list counters
table ip filter {
counter tcp-counter {
packets 0 bytes 0
}
counter udp-counter {
packets 0 bytes 0
}
counter icmp-counter {
packets 0 bytes 0
}
}
The snippet below shows a simplistic configuration to account tcp, udp
and icmp traffic through the named counter:
-o-
table ip filter {
counter tcp-counter {
packets 6086 bytes 6278052
}
counter udp-counter {
packets 272 bytes 64690
}
counter icmp-counter {
packets 10 bytes 840
}
chain input {
type filter hook input priority 0; policy accept;
ip protocol vmap { tcp : jump tcp-chain, icmp : jump icmp-chain, udp : jump udp-chain}
}
chain output {
type filter hook output priority 0; policy accept;
}
chain tcp-chain {
counter name tcp-counter
}
chain udp-chain {
counter name udp-counter
}
chain icmp-chain {
counter name icmp-counter
}
}
-o-
So far, only counters are supported, but it should be possible to
support named limits. I have another (imcomplete) patch that allows to
update the named expressions parameters, this can be useful to
dynamically update the ratelimiting policies, the command line should
look like:
# nft update limit name user01234 rate 250 mbytes/day
This example above updates the existing ratelimit 'user01234' to
250 mbytes/day.
Comments welcome,
Thanks.
Pablo Neira Ayuso (3):
netfilter: nf_tables: add stateful named expressions
netfilter: nf_tables: support for named expression reference
netfilter: nf_tables: support dump and reset for named expressions
include/net/netfilter/nf_tables.h | 32 +++
include/uapi/linux/netfilter/nf_tables.h | 41 +++
net/netfilter/Kconfig | 6 +
net/netfilter/Makefile | 1 +
net/netfilter/nf_tables_api.c | 453 ++++++++++++++++++++++++++++++-
net/netfilter/nft_counter.c | 36 ++-
net/netfilter/nft_nexpr.c | 112 ++++++++
7 files changed, 667 insertions(+), 14 deletions(-)
create mode 100644 net/netfilter/nft_nexpr.c
--
2.1.4
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH RFC nf-next 1/3] netfilter: nf_tables: add stateful named expressions
2016-04-06 16:51 [PATCH RFC nf-next 0/3] named expressions for nf_tables Pablo Neira Ayuso
@ 2016-04-06 16:51 ` Pablo Neira Ayuso
2016-04-06 16:51 ` [PATCH RFC nf-next 2/3] netfilter: nf_tables: support for named expression reference Pablo Neira Ayuso
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Pablo Neira Ayuso @ 2016-04-06 16:51 UTC (permalink / raw)
To: netfilter-devel
Users can define named counters in iptables through the nfacct
infrastructure. This extended accounting infrastructure provides a
netlink interface to create counters, that are uniquely identified by a
name, to fetch them from userspace; and to (atomically) fetch and reset
them.
In nf_tables, the use of the existing nfacct infrastructure would not
integrate nicely with its netlink interface and its 2-phase commit
protocol. For that reason, the use of nfacct is out of question.
This patch extends the nf_tables netlink interface to allow the
creation, deletion and dump of these stateful named expression from
userspace.
This patch introduces a generic infrastructure for stateful named
expressions, that allows userspace to set a name for the stateful
expression for several reason: 1) to provide a unique identifier to
fetch and reset it internal state, 2) to update of their parameters
and internal state, 3) to fetch and reset its internal state, and
4) to refer to the same stateful expression from one or more rules.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
include/net/netfilter/nf_tables.h | 25 ++
include/uapi/linux/netfilter/nf_tables.h | 25 ++
net/netfilter/nf_tables_api.c | 393 +++++++++++++++++++++++++++++++
3 files changed, 443 insertions(+)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index f6b1daf..0ba91ac 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -653,6 +653,22 @@ struct nft_expr {
unsigned char data[];
};
+/**
+ * struct nft_nexpr - nf_tables named expression
+ *
+ * @list: table named expression list node
+ * @name: name of this expression
+ * @use: number of references to this named expression
+ * @expr: pointer to expression object
+ */
+struct nft_nexpr {
+ struct list_head list;
+ char name[NFT_NEXPR_MAXNAMELEN];
+ u32 flags;
+ u32 use;
+ struct nft_expr *expr;
+};
+
static inline void *nft_expr_priv(const struct nft_expr *expr)
{
return (void *)expr->data;
@@ -835,6 +851,7 @@ unsigned int nft_do_chain(struct nft_pktinfo *pkt, void *priv);
* @list: used internally
* @chains: chains in the table
* @sets: sets in the table
+ * @nexprs: named expression in the table
* @hgenerator: handle generator state
* @use: number of chain references to this table
* @flags: table flag (see enum nft_table_flags)
@@ -844,6 +861,7 @@ struct nft_table {
struct list_head list;
struct list_head chains;
struct list_head sets;
+ struct list_head nexprs;
u64 hgenerator;
u32 use;
u16 flags;
@@ -1088,4 +1106,11 @@ struct nft_trans_elem {
#define nft_trans_elem(trans) \
(((struct nft_trans_elem *)trans->data)->elem)
+struct nft_trans_nexpr {
+ struct nft_nexpr *nexpr;
+};
+
+#define nft_trans_nexpr(trans) \
+ (((struct nft_trans_nexpr *)trans->data)->nexpr)
+
#endif /* _NET_NF_TABLES_H */
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index be41ffc..c1e19c3 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -3,6 +3,7 @@
#define NFT_TABLE_MAXNAMELEN 32
#define NFT_CHAIN_MAXNAMELEN 32
+#define NFT_NEXPR_MAXNAMELEN 32
#define NFT_USERDATA_MAXLEN 256
/**
@@ -84,6 +85,9 @@ enum nft_verdicts {
* @NFT_MSG_NEWGEN: announce a new generation, only for events (enum nft_gen_attributes)
* @NFT_MSG_GETGEN: get the rule-set generation (enum nft_gen_attributes)
* @NFT_MSG_TRACE: trace event (enum nft_trace_attributes)
+ * @NFT_MSG_NEWNEXPR: create a new named expression (enum nft_nexpr_attributes)
+ * @NFT_MSG_GETNEXPR: get a named expression (enum nft_nexpr_attributes)
+ * @NFT_MSG_DELNEXPR: delete a named expression (enum nft_nexpr_attributes)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -104,6 +108,9 @@ enum nf_tables_msg_types {
NFT_MSG_NEWGEN,
NFT_MSG_GETGEN,
NFT_MSG_TRACE,
+ NFT_MSG_NEWNEXPR,
+ NFT_MSG_GETNEXPR,
+ NFT_MSG_DELNEXPR,
NFT_MSG_MAX,
};
@@ -416,6 +423,24 @@ enum nft_verdict_attributes {
#define NFTA_VERDICT_MAX (__NFTA_VERDICT_MAX - 1)
/**
+ * enum nft_nexpr_attributes - nf_tables named expression netlink attributes
+ *
+ * @NFTA_NEXPR_TABLE: name of the table containing the expression (NLA_STRING)
+ * @NFTA_NEXPR_NAME: name of this expression type (NLA_STRING)
+ * @NFTA_NEXPR_EXPR: expression data (NLA_NESTED: nft_expr_attributes)
+ * @NFTA_NEXPR_USE: number of references to this expression (NLA_U32)
+ */
+enum nft_nexpr_attributes {
+ NFTA_NEXPR_UNSPEC,
+ NFTA_NEXPR_TABLE,
+ NFTA_NEXPR_NAME,
+ NFTA_NEXPR_EXPR,
+ NFTA_NEXPR_USE,
+ __NFTA_NEXPR_MAX
+};
+#define NFTA_NEXPR_MAX (__NFTA_NEXPR_MAX - 1)
+
+/**
* enum nft_expr_attributes - nf_tables expression netlink attributes
*
* @NFTA_EXPR_NAME: name of the expression type (NLA_STRING)
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index 2011977..b542d20 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -359,6 +359,39 @@ static int nft_delset(struct nft_ctx *ctx, struct nft_set *set)
return err;
}
+/* Internal set flag */
+#define NFT_NEXPR_INACTIVE (1 << 7)
+
+static int nft_trans_nexpr_add(struct nft_ctx *ctx, int msg_type,
+ struct nft_nexpr *nexpr)
+{
+ struct nft_trans *trans;
+
+ trans = nft_trans_alloc(ctx, msg_type, sizeof(struct nft_trans_nexpr));
+ if (trans == NULL)
+ return -ENOMEM;
+
+ nexpr->flags |= NFT_NEXPR_INACTIVE;
+ nft_trans_nexpr(trans) = nexpr;
+ list_add_tail(&trans->list, &ctx->net->nft.commit_list);
+
+ return 0;
+}
+
+static int nft_delnexpr(struct nft_ctx *ctx, struct nft_nexpr *nexpr)
+{
+ int err;
+
+ err = nft_trans_nexpr_add(ctx, NFT_MSG_DELNEXPR, nexpr);
+ if (err < 0)
+ return err;
+
+ list_del_rcu(&nexpr->list);
+ ctx->table->use--;
+
+ return err;
+}
+
/*
* Tables
*/
@@ -728,6 +761,7 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk,
nla_strlcpy(table->name, name, NFT_TABLE_MAXNAMELEN);
INIT_LIST_HEAD(&table->chains);
INIT_LIST_HEAD(&table->sets);
+ INIT_LIST_HEAD(&table->nexprs);
table->flags = flags;
nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
@@ -749,6 +783,7 @@ static int nft_flush_table(struct nft_ctx *ctx)
{
int err;
struct nft_chain *chain, *nc;
+ struct nft_nexpr *nexpr, *ne;
struct nft_set *set, *ns;
list_for_each_entry(chain, &ctx->table->chains, list) {
@@ -769,6 +804,12 @@ static int nft_flush_table(struct nft_ctx *ctx)
goto out;
}
+ list_for_each_entry_safe(nexpr, ne, &ctx->table->nexprs, list) {
+ err = nft_delnexpr(ctx, nexpr);
+ if (err < 0)
+ goto out;
+ }
+
list_for_each_entry_safe(chain, nc, &ctx->table->chains, list) {
ctx->chain = chain;
@@ -3759,6 +3800,315 @@ err:
return err;
}
+static struct nft_nexpr *nf_tables_nexpr_lookup(const struct nft_table *table,
+ const struct nlattr * const nla[])
+{
+ struct nlattr *tb[NFTA_EXPR_MAX + 1];
+ struct nft_nexpr *nexpr;
+ int err;
+
+ if (!nla[NFTA_NEXPR_NAME] ||
+ !nla[NFTA_NEXPR_EXPR])
+ return ERR_PTR(-EINVAL);
+
+ err = nla_parse_nested(tb, NFTA_EXPR_MAX, nla[NFTA_NEXPR_EXPR],
+ nft_expr_policy);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ if (!tb[NFTA_EXPR_NAME])
+ return ERR_PTR(-EINVAL);
+
+ list_for_each_entry(nexpr, &table->nexprs, list) {
+ if (!nla_strcmp(nla[NFTA_NEXPR_NAME], nexpr->name) &&
+ !nla_strcmp(tb[NFTA_EXPR_NAME], nexpr->expr->ops->type->name))
+ return nexpr;
+ }
+ return ERR_PTR(-ENOENT);
+}
+
+static const struct nla_policy nft_nexpr_policy[NFTA_NEXPR_MAX + 1] = {
+ [NFTA_NEXPR_TABLE] = { .type = NLA_STRING },
+ [NFTA_NEXPR_NAME] = { .type = NLA_STRING },
+ [NFTA_NEXPR_EXPR] = { .type = NLA_NESTED },
+};
+
+static int nf_tables_newnexpr(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nla[])
+{
+ const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+ int family = nfmsg->nfgen_family;
+ struct nft_nexpr *nexpr;
+ struct nft_af_info *afi;
+ struct nft_table *table;
+ struct nft_expr *expr;
+ struct nft_ctx ctx;
+ int err;
+
+ if (!nla[NFTA_NEXPR_EXPR])
+ return -EINVAL;
+
+ afi = nf_tables_afinfo_lookup(net, family, true);
+ if (IS_ERR(afi))
+ return PTR_ERR(afi);
+
+ table = nf_tables_table_lookup(afi, nla[NFTA_NEXPR_TABLE]);
+ if (IS_ERR(table))
+ return PTR_ERR(table);
+
+ nexpr = nf_tables_nexpr_lookup(table, nla);
+ if (IS_ERR(nexpr)) {
+ err = PTR_ERR(nexpr);
+ if (err != -ENOENT)
+ return err;
+
+ nexpr = NULL;
+ }
+
+ if (nexpr != NULL) {
+ if (nlh->nlmsg_flags & NLM_F_EXCL)
+ return -EEXIST;
+ else
+ return -EBUSY;
+ }
+
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
+
+ expr = nft_expr_init(&ctx, nla[NFTA_NEXPR_EXPR]);
+ if (IS_ERR(expr)) {
+ err = PTR_ERR(expr);
+ goto err1;
+ }
+
+ if (!(expr->ops->type->flags & NFT_EXPR_STATEFUL)) {
+ err = -EOPNOTSUPP;
+ goto err2;
+ }
+
+ nexpr = kzalloc(sizeof(struct nft_nexpr), GFP_KERNEL);
+ if (nexpr == NULL) {
+ err = -ENOMEM;
+ goto err2;
+ }
+
+ nla_strlcpy(nexpr->name, nla[NFTA_NEXPR_NAME], NFT_NEXPR_MAXNAMELEN);
+ nexpr->expr = expr;
+
+ err = nft_trans_nexpr_add(&ctx, NFT_MSG_NEWNEXPR, nexpr);
+ if (err < 0)
+ goto err3;
+
+ list_add_tail_rcu(&nexpr->list, &table->nexprs);
+ table->use++;
+ return 0;
+err3:
+ kfree(nexpr);
+err2:
+ nft_expr_destroy(&ctx, expr);
+err1:
+ kfree(nexpr);
+ return err;
+}
+
+static int nf_tables_fill_nexpr_info(struct sk_buff *skb, struct net *net,
+ u32 portid, u32 seq, int event, u32 flags,
+ int family, const struct nft_table *table,
+ const struct nft_nexpr *nexpr)
+{
+ struct nlmsghdr *nlh;
+ struct nfgenmsg *nfmsg;
+
+ 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 = family;
+ nfmsg->version = NFNETLINK_V0;
+ nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
+
+ if (nla_put_string(skb, NFTA_NEXPR_TABLE, table->name) ||
+ nla_put_string(skb, NFTA_NEXPR_NAME, nexpr->name) ||
+ nla_put_be32(skb, NFTA_NEXPR_USE, htonl(nexpr->use)) ||
+ nft_expr_dump(skb, NFTA_NEXPR_EXPR, nexpr->expr))
+ goto nla_put_failure;
+
+ nlmsg_end(skb, nlh);
+ return 0;
+
+nla_put_failure:
+ nlmsg_trim(skb, nlh);
+ return -1;
+}
+
+static int nf_tables_dump_nexpr(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ const struct nfgenmsg *nfmsg = nlmsg_data(cb->nlh);
+ const struct nft_af_info *afi;
+ const struct nft_table *table;
+ const struct nft_nexpr *nexpr;
+ unsigned int idx = 0, s_idx = cb->args[0];
+ struct net *net = sock_net(skb->sk);
+ int family = nfmsg->nfgen_family;
+
+ rcu_read_lock();
+ cb->seq = net->nft.base_seq;
+
+ list_for_each_entry_rcu(afi, &net->nft.af_info, list) {
+ if (family != NFPROTO_UNSPEC && family != afi->family)
+ continue;
+
+ list_for_each_entry_rcu(table, &afi->tables, list) {
+ list_for_each_entry_rcu(nexpr, &table->nexprs, list) {
+ if (idx < s_idx)
+ goto cont;
+ if (idx > s_idx)
+ memset(&cb->args[1], 0,
+ sizeof(cb->args) - sizeof(cb->args[0]));
+ if (nf_tables_fill_nexpr_info(skb, net, NETLINK_CB(cb->skb).portid,
+ cb->nlh->nlmsg_seq,
+ NFT_MSG_NEWNEXPR,
+ NLM_F_MULTI | NLM_F_APPEND,
+ afi->family, table, nexpr) < 0)
+ goto done;
+
+ nl_dump_check_consistent(cb, nlmsg_hdr(skb));
+cont:
+ idx++;
+ }
+ }
+ }
+done:
+ rcu_read_unlock();
+
+ cb->args[0] = idx;
+ return skb->len;
+}
+
+static int nf_tables_getnexpr(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nla[])
+{
+ const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+ int family = nfmsg->nfgen_family;
+ const struct nft_af_info *afi;
+ const struct nft_table *table;
+ struct nft_nexpr *nexpr;
+ struct sk_buff *skb2;
+ int err;
+
+ if (nlh->nlmsg_flags & NLM_F_DUMP) {
+ struct netlink_dump_control c = {
+ .dump = nf_tables_dump_nexpr,
+ };
+ return netlink_dump_start(nlsk, skb, nlh, &c);
+ }
+
+ afi = nf_tables_afinfo_lookup(net, family, false);
+ if (IS_ERR(afi))
+ return PTR_ERR(afi);
+
+ table = nf_tables_table_lookup(afi, nla[NFTA_NEXPR_TABLE]);
+ if (IS_ERR(table))
+ return PTR_ERR(table);
+ if (table->flags & NFT_TABLE_INACTIVE)
+ return -ENOENT;
+
+ nexpr = nf_tables_nexpr_lookup(table, nla);
+ if (IS_ERR(nexpr))
+ return PTR_ERR(nexpr);
+
+ skb2 = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (!skb2)
+ return -ENOMEM;
+
+ err = nf_tables_fill_nexpr_info(skb2, net, NETLINK_CB(skb).portid,
+ nlh->nlmsg_seq, NFT_MSG_NEWNEXPR, 0,
+ family, table, nexpr);
+ if (err < 0)
+ goto err;
+
+ return nlmsg_unicast(nlsk, skb2, NETLINK_CB(skb).portid);
+
+err:
+ kfree_skb(skb2);
+ return err;
+
+ return 0;
+}
+
+static void nft_nexpr_destroy(struct nft_ctx *ctx, struct nft_nexpr *nexpr)
+{
+ nft_expr_destroy(ctx, nexpr->expr);
+ kfree(nexpr);
+}
+
+static int nf_tables_delnexpr(struct net *net, struct sock *nlsk,
+ struct sk_buff *skb, const struct nlmsghdr *nlh,
+ const struct nlattr * const nla[])
+{
+ const struct nfgenmsg *nfmsg = nlmsg_data(nlh);
+ int family = nfmsg->nfgen_family;
+ struct nft_nexpr *nexpr;
+ struct nft_af_info *afi;
+ struct nft_table *table;
+ struct nft_ctx ctx;
+
+ afi = nf_tables_afinfo_lookup(net, family, true);
+ if (IS_ERR(afi))
+ return PTR_ERR(afi);
+
+ table = nf_tables_table_lookup(afi, nla[NFTA_NEXPR_TABLE]);
+ if (IS_ERR(table))
+ return PTR_ERR(table);
+
+ nexpr = nf_tables_nexpr_lookup(table, nla);
+ if (IS_ERR(nexpr))
+ return PTR_ERR(nexpr);
+ if (nexpr->use > 0)
+ return -EBUSY;
+
+ nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla);
+
+ return nft_delnexpr(&ctx, nexpr);
+}
+
+static int nf_tables_nexpr_notify(const struct nft_ctx *ctx,
+ struct nft_nexpr *nexpr, int event)
+{
+ struct sk_buff *skb;
+ int err;
+
+ if (!ctx->report &&
+ !nfnetlink_has_listeners(ctx->net, NFNLGRP_NFTABLES))
+ return 0;
+
+ err = -ENOBUFS;
+ skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
+ if (skb == NULL)
+ goto err;
+
+ err = nf_tables_fill_nexpr_info(skb, ctx->net, ctx->portid, ctx->seq,
+ event, 0, ctx->afi->family, ctx->table,
+ nexpr);
+ if (err < 0) {
+ kfree_skb(skb);
+ goto err;
+ }
+
+ err = nfnetlink_send(skb, ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+ ctx->report, GFP_KERNEL);
+err:
+ if (err < 0) {
+ nfnetlink_set_err(ctx->net, ctx->portid, NFNLGRP_NFTABLES,
+ err);
+ }
+ return err;
+}
+
static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
[NFT_MSG_NEWTABLE] = {
.call_batch = nf_tables_newtable,
@@ -3838,6 +4188,21 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
[NFT_MSG_GETGEN] = {
.call = nf_tables_getgen,
},
+ [NFT_MSG_NEWNEXPR] = {
+ .call_batch = nf_tables_newnexpr,
+ .attr_count = NFTA_NEXPR_MAX,
+ .policy = nft_nexpr_policy,
+ },
+ [NFT_MSG_GETNEXPR] = {
+ .call = nf_tables_getnexpr,
+ .attr_count = NFTA_NEXPR_MAX,
+ .policy = nft_nexpr_policy,
+ },
+ [NFT_MSG_DELNEXPR] = {
+ .call_batch = nf_tables_delnexpr,
+ .attr_count = NFTA_NEXPR_MAX,
+ .policy = nft_nexpr_policy,
+ },
};
static void nft_chain_commit_update(struct nft_trans *trans)
@@ -3880,6 +4245,9 @@ static void nf_tables_commit_release(struct nft_trans *trans)
nft_set_elem_destroy(nft_trans_elem_set(trans),
nft_trans_elem(trans).priv);
break;
+ case NFT_MSG_NEWNEXPR:
+ nft_nexpr_destroy(&trans->ctx, nft_trans_nexpr(trans));
+ break;
}
kfree(trans);
}
@@ -3982,6 +4350,18 @@ static int nf_tables_commit(struct net *net, struct sk_buff *skb)
atomic_dec(&te->set->nelems);
te->set->ndeact--;
break;
+ case NFT_MSG_NEWNEXPR:
+ nft_trans_nexpr(trans)->flags &= ~NFT_NEXPR_INACTIVE;
+ nf_tables_nexpr_notify(&trans->ctx,
+ nft_trans_nexpr(trans),
+ NFT_MSG_NEWNEXPR);
+ nft_trans_destroy(trans);
+ break;
+ case NFT_MSG_DELNEXPR:
+ nf_tables_nexpr_notify(&trans->ctx,
+ nft_trans_nexpr(trans),
+ NFT_MSG_DELNEXPR);
+ break;
}
}
@@ -4016,6 +4396,9 @@ static void nf_tables_abort_release(struct nft_trans *trans)
nft_set_elem_destroy(nft_trans_elem_set(trans),
nft_trans_elem(trans).priv);
break;
+ case NFT_MSG_NEWNEXPR:
+ nft_nexpr_destroy(&trans->ctx, nft_trans_nexpr(trans));
+ break;
}
kfree(trans);
}
@@ -4097,6 +4480,16 @@ static int nf_tables_abort(struct net *net, struct sk_buff *skb)
nft_trans_destroy(trans);
break;
+ case NFT_MSG_NEWNEXPR:
+ trans->ctx.table->use--;
+ list_del_rcu(&nft_trans_nexpr(trans)->list);
+ break;
+ case NFT_MSG_DELNEXPR:
+ trans->ctx.table->use++;
+ list_add_tail_rcu(&nft_trans_nexpr(trans)->list,
+ &trans->ctx.table->nexprs);
+ nft_trans_destroy(trans);
+ break;
}
}
--
2.1.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH RFC nf-next 2/3] netfilter: nf_tables: support for named expression reference
2016-04-06 16:51 [PATCH RFC nf-next 0/3] named expressions for nf_tables Pablo Neira Ayuso
2016-04-06 16:51 ` [PATCH RFC nf-next 1/3] netfilter: nf_tables: add stateful named expressions Pablo Neira Ayuso
@ 2016-04-06 16:51 ` Pablo Neira Ayuso
2016-04-06 16:51 ` [PATCH RFC nf-next 3/3] netfilter: nf_tables: support dump and reset for named expressions Pablo Neira Ayuso
` (3 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Pablo Neira Ayuso @ 2016-04-06 16:51 UTC (permalink / raw)
To: netfilter-devel
This patch adds the 'nexpr' expression, this expression allows us to
refer to existing named expressions. This generic expression can be used
from rules and set elements.
This patch also adds nft_nexpr_lookup() to the core, as this new
expression requires this function.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
include/net/netfilter/nf_tables.h | 4 ++
include/uapi/linux/netfilter/nf_tables.h | 14 ++++
net/netfilter/Kconfig | 6 ++
net/netfilter/Makefile | 1 +
net/netfilter/nf_tables_api.c | 24 +++++--
net/netfilter/nft_nexpr.c | 112 +++++++++++++++++++++++++++++++
6 files changed, 154 insertions(+), 7 deletions(-)
create mode 100644 net/netfilter/nft_nexpr.c
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index 0ba91ac..a84f787 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -669,6 +669,10 @@ struct nft_nexpr {
struct nft_expr *expr;
};
+struct nft_nexpr *nft_nexpr_lookup(const struct nft_table *table,
+ const struct nlattr * const nla_name,
+ const struct nlattr * const nla_type);
+
static inline void *nft_expr_priv(const struct nft_expr *expr)
{
return (void *)expr->data;
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index c1e19c3..f07b384 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -1025,6 +1025,20 @@ enum nft_fwd_attributes {
#define NFTA_FWD_MAX (__NFTA_FWD_MAX - 1)
/**
+ * enum nft_nexpr_ref_attributes - nf_tables named expression reference netlink attributes
+ *
+ * @NFTA_NEXPR_REF_NAME: name of this named expression (NLA_STRING)
+ * @NFTA_NEXPR_REF_TYPE: type of this named expression (NLA_STRING)
+ */
+enum nft_nexpr_ref_attributes {
+ NFTA_NEXPR_REF_UNSPEC,
+ NFTA_NEXPR_REF_NAME,
+ NFTA_NEXPR_REF_TYPE,
+ __NFTA_NEXPR_REF_MAX
+};
+#define NFTA_NEXPR_REF_MAX (__NFTA_NEXPR_REF_MAX - 1)
+
+/**
* enum nft_gen_attributes - nf_tables ruleset generation attributes
*
* @NFTA_GEN_ID: Ruleset generation ID (NLA_U32)
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 95e757c..251f9d2 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -535,6 +535,12 @@ config NFT_NAT
This option adds the "nat" expression that you can use to perform
typical Network Address Translation (NAT) packet transformations.
+config NFT_NEXPR
+ tristate "Netfilter nf_tables named expression module"
+ help
+ This option adds the "named" expression that you can use to
+ refer an existing named expression.
+
config NFT_QUEUE
depends on NETFILTER_NETLINK_QUEUE
tristate "Netfilter nf_tables queue module"
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 6913454..2ba1388 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -83,6 +83,7 @@ obj-$(CONFIG_NFT_META) += nft_meta.o
obj-$(CONFIG_NFT_CT) += nft_ct.o
obj-$(CONFIG_NFT_LIMIT) += nft_limit.o
obj-$(CONFIG_NFT_NAT) += nft_nat.o
+obj-$(CONFIG_NFT_NEXPR) += nft_nexpr.o
obj-$(CONFIG_NFT_QUEUE) += nft_queue.o
obj-$(CONFIG_NFT_REJECT) += nft_reject.o
obj-$(CONFIG_NFT_REJECT_INET) += nft_reject_inet.o
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index b542d20..e506dca 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -3800,11 +3800,25 @@ err:
return err;
}
+struct nft_nexpr *nft_nexpr_lookup(const struct nft_table *table,
+ const struct nlattr * const nla_name,
+ const struct nlattr * const nla_type)
+{
+ struct nft_nexpr *nexpr;
+
+ list_for_each_entry(nexpr, &table->nexprs, list) {
+ if (!nla_strcmp(nla_name, nexpr->name) &&
+ !nla_strcmp(nla_type, nexpr->expr->ops->type->name))
+ return nexpr;
+ }
+ return ERR_PTR(-ENOENT);
+}
+EXPORT_SYMBOL_GPL(nft_nexpr_lookup);
+
static struct nft_nexpr *nf_tables_nexpr_lookup(const struct nft_table *table,
const struct nlattr * const nla[])
{
struct nlattr *tb[NFTA_EXPR_MAX + 1];
- struct nft_nexpr *nexpr;
int err;
if (!nla[NFTA_NEXPR_NAME] ||
@@ -3819,12 +3833,8 @@ static struct nft_nexpr *nf_tables_nexpr_lookup(const struct nft_table *table,
if (!tb[NFTA_EXPR_NAME])
return ERR_PTR(-EINVAL);
- list_for_each_entry(nexpr, &table->nexprs, list) {
- if (!nla_strcmp(nla[NFTA_NEXPR_NAME], nexpr->name) &&
- !nla_strcmp(tb[NFTA_EXPR_NAME], nexpr->expr->ops->type->name))
- return nexpr;
- }
- return ERR_PTR(-ENOENT);
+ return nft_nexpr_lookup(table, nla[NFTA_NEXPR_NAME],
+ tb[NFTA_EXPR_NAME]);
}
static const struct nla_policy nft_nexpr_policy[NFTA_NEXPR_MAX + 1] = {
diff --git a/net/netfilter/nft_nexpr.c b/net/netfilter/nft_nexpr.c
new file mode 100644
index 0000000..1fe0bee
--- /dev/null
+++ b/net/netfilter/nft_nexpr.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016 Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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/seqlock.h>
+#include <linux/netlink.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nf_tables.h>
+#include <net/netfilter/nf_tables.h>
+
+struct nft_nexpr_priv {
+ struct nft_nexpr *nexpr;
+};
+
+static void nft_nexpr_eval(const struct nft_expr *expr,
+ struct nft_regs *regs,
+ const struct nft_pktinfo *pkt)
+{
+ struct nft_nexpr_priv *priv = nft_expr_priv(expr);
+
+ priv->nexpr->expr->ops->eval(priv->nexpr->expr, regs, pkt);
+}
+
+static int nft_nexpr_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ struct nft_nexpr_priv *priv = nft_expr_priv(expr);
+ const char *type = priv->nexpr->expr->ops->type->name;
+
+ if (nla_put_string(skb, NFTA_NEXPR_REF_NAME, priv->nexpr->name) ||
+ nla_put_string(skb, NFTA_NEXPR_REF_TYPE, type))
+ goto nla_put_failure;
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
+static const struct nla_policy nft_nexpr_policy[NFTA_NEXPR_REF_MAX + 1] = {
+ [NFTA_NEXPR_REF_NAME] = { .type = NLA_STRING },
+ [NFTA_NEXPR_REF_TYPE] = { .type = NLA_STRING },
+};
+
+static int nft_nexpr_init(const struct nft_ctx *ctx,
+ const struct nft_expr *expr,
+ const struct nlattr * const tb[])
+{
+ struct nft_nexpr_priv *priv = nft_expr_priv(expr);
+ struct nft_nexpr *nexpr;
+
+ if (!tb[NFTA_NEXPR_REF_NAME] ||
+ !tb[NFTA_NEXPR_REF_TYPE])
+ return -EINVAL;
+
+ nexpr = nft_nexpr_lookup(ctx->table, tb[NFTA_NEXPR_REF_NAME],
+ tb[NFTA_NEXPR_REF_TYPE]);
+ if (IS_ERR(nexpr))
+ return PTR_ERR(nexpr);
+
+ nexpr->use++;
+ priv->nexpr = nexpr;
+ return 0;
+}
+
+static void nft_nexpr_destroy(const struct nft_ctx *ctx,
+ const struct nft_expr *expr)
+{
+ struct nft_nexpr_priv *priv = nft_expr_priv(expr);
+
+ priv->nexpr->use--;
+}
+
+static struct nft_expr_type nft_nexpr_type;
+static const struct nft_expr_ops nft_nexpr_ops = {
+ .type = &nft_nexpr_type,
+ .size = NFT_EXPR_SIZE(sizeof(struct nft_nexpr_priv)),
+ .eval = nft_nexpr_eval,
+ .init = nft_nexpr_init,
+ .destroy = nft_nexpr_destroy,
+ .dump = nft_nexpr_dump,
+};
+
+static struct nft_expr_type nft_nexpr_type __read_mostly = {
+ .name = "nexpr",
+ .ops = &nft_nexpr_ops,
+ .policy = nft_nexpr_policy,
+ .maxattr = NFTA_NEXPR_REF_MAX,
+ .owner = THIS_MODULE,
+};
+
+static int __init nft_nexpr_module_init(void)
+{
+ return nft_register_expr(&nft_nexpr_type);
+}
+
+static void __exit nft_nexpr_module_exit(void)
+{
+ nft_unregister_expr(&nft_nexpr_type);
+}
+
+module_init(nft_nexpr_module_init);
+module_exit(nft_nexpr_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org");
+MODULE_ALIAS_NFT_EXPR("nexpr");
--
2.1.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH RFC nf-next 3/3] netfilter: nf_tables: support dump and reset for named expressions
2016-04-06 16:51 [PATCH RFC nf-next 0/3] named expressions for nf_tables Pablo Neira Ayuso
2016-04-06 16:51 ` [PATCH RFC nf-next 1/3] netfilter: nf_tables: add stateful named expressions Pablo Neira Ayuso
2016-04-06 16:51 ` [PATCH RFC nf-next 2/3] netfilter: nf_tables: support for named expression reference Pablo Neira Ayuso
@ 2016-04-06 16:51 ` Pablo Neira Ayuso
2016-04-07 21:49 ` [PATCH RFC nf-next 0/3] named expressions for nf_tables Florian Westphal
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Pablo Neira Ayuso @ 2016-04-06 16:51 UTC (permalink / raw)
To: netfilter-devel
This patch adds a new NFT_MSG_GETNEXPR_RESET command to dump and to
atomically reset the internal state of the named expression.
Stateful expressions may implement the new reset() interface to allow
the reset of this named expressions.
This patch comes with the first client of it: the nft_counter
expression.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
include/net/netfilter/nf_tables.h | 3 ++
include/uapi/linux/netfilter/nf_tables.h | 2 +
net/netfilter/nf_tables_api.c | 66 +++++++++++++++++++++++++-------
net/netfilter/nft_counter.c | 36 +++++++++++++----
4 files changed, 85 insertions(+), 22 deletions(-)
diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h
index a84f787..8c39268 100644
--- a/include/net/netfilter/nf_tables.h
+++ b/include/net/netfilter/nf_tables.h
@@ -611,6 +611,7 @@ struct nft_expr_type {
* @init: initialization function
* @destroy: destruction function
* @dump: function to dump parameters
+ * @reset: function to dump parameters and to reset internal state
* @type: expression type
* @validate: validate expression, called during loop detection
* @data: extra data to attach to this expression operation
@@ -631,6 +632,8 @@ struct nft_expr_ops {
const struct nft_expr *expr);
int (*dump)(struct sk_buff *skb,
const struct nft_expr *expr);
+ int (*reset)(struct sk_buff *skb,
+ const struct nft_expr *expr);
int (*validate)(const struct nft_ctx *ctx,
const struct nft_expr *expr,
const struct nft_data **data);
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h
index f07b384..644c1dd 100644
--- a/include/uapi/linux/netfilter/nf_tables.h
+++ b/include/uapi/linux/netfilter/nf_tables.h
@@ -88,6 +88,7 @@ enum nft_verdicts {
* @NFT_MSG_NEWNEXPR: create a new named expression (enum nft_nexpr_attributes)
* @NFT_MSG_GETNEXPR: get a named expression (enum nft_nexpr_attributes)
* @NFT_MSG_DELNEXPR: delete a named expression (enum nft_nexpr_attributes)
+ * @NFT_MSG_GETNEXPR_RESET: get and reset a named expression (enum nft_nexpr_attributes)
*/
enum nf_tables_msg_types {
NFT_MSG_NEWTABLE,
@@ -111,6 +112,7 @@ enum nf_tables_msg_types {
NFT_MSG_NEWNEXPR,
NFT_MSG_GETNEXPR,
NFT_MSG_DELNEXPR,
+ NFT_MSG_GETNEXPR_RESET,
NFT_MSG_MAX,
};
diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c
index e506dca..94d51cd 100644
--- a/net/netfilter/nf_tables_api.c
+++ b/net/netfilter/nf_tables_api.c
@@ -1630,7 +1630,7 @@ static const struct nla_policy nft_expr_policy[NFTA_EXPR_MAX + 1] = {
};
static int nf_tables_fill_expr_info(struct sk_buff *skb,
- const struct nft_expr *expr)
+ const struct nft_expr *expr, bool reset)
{
if (nla_put_string(skb, NFTA_EXPR_NAME, expr->ops->type->name))
goto nla_put_failure;
@@ -1639,8 +1639,13 @@ static int nf_tables_fill_expr_info(struct sk_buff *skb,
struct nlattr *data = nla_nest_start(skb, NFTA_EXPR_DATA);
if (data == NULL)
goto nla_put_failure;
- if (expr->ops->dump(skb, expr) < 0)
- goto nla_put_failure;
+ if (reset) {
+ if (expr->ops->reset(skb, expr) < 0)
+ goto nla_put_failure;
+ } else {
+ if (expr->ops->dump(skb, expr) < 0)
+ goto nla_put_failure;
+ }
nla_nest_end(skb, data);
}
@@ -1650,15 +1655,15 @@ nla_put_failure:
return -1;
};
-int nft_expr_dump(struct sk_buff *skb, unsigned int attr,
- const struct nft_expr *expr)
+int __nft_expr_dump(struct sk_buff *skb, unsigned int attr,
+ const struct nft_expr *expr, bool reset)
{
struct nlattr *nest;
nest = nla_nest_start(skb, attr);
if (!nest)
goto nla_put_failure;
- if (nf_tables_fill_expr_info(skb, expr) < 0)
+ if (nf_tables_fill_expr_info(skb, expr, reset) < 0)
goto nla_put_failure;
nla_nest_end(skb, nest);
return 0;
@@ -1667,6 +1672,12 @@ nla_put_failure:
return -1;
}
+int nft_expr_dump(struct sk_buff *skb, unsigned int attr,
+ const struct nft_expr *expr)
+{
+ return __nft_expr_dump(skb, attr, expr, false);
+}
+
struct nft_expr_info {
const struct nft_expr_ops *ops;
struct nlattr *tb[NFT_EXPR_MAXATTR + 1];
@@ -3921,10 +3932,27 @@ err1:
return err;
}
+static int nft_nexpr_dump(struct sk_buff *skb, const struct nft_table *table,
+ const struct nft_nexpr *nexpr, bool reset)
+{
+ if (nla_put_string(skb, NFTA_NEXPR_TABLE, table->name) ||
+ nla_put_string(skb, NFTA_NEXPR_NAME, nexpr->name) ||
+ nla_put_be32(skb, NFTA_NEXPR_USE, htonl(nexpr->use)))
+ goto nla_put_failure;
+
+ if (__nft_expr_dump(skb, NFTA_NEXPR_EXPR, nexpr->expr, reset))
+ goto nla_put_failure;
+
+ return 0;
+
+nla_put_failure:
+ return -1;
+}
+
static int nf_tables_fill_nexpr_info(struct sk_buff *skb, struct net *net,
u32 portid, u32 seq, int event, u32 flags,
int family, const struct nft_table *table,
- const struct nft_nexpr *nexpr)
+ const struct nft_nexpr *nexpr, bool reset)
{
struct nlmsghdr *nlh;
struct nfgenmsg *nfmsg;
@@ -3939,10 +3967,7 @@ static int nf_tables_fill_nexpr_info(struct sk_buff *skb, struct net *net,
nfmsg->version = NFNETLINK_V0;
nfmsg->res_id = htons(net->nft.base_seq & 0xffff);
- if (nla_put_string(skb, NFTA_NEXPR_TABLE, table->name) ||
- nla_put_string(skb, NFTA_NEXPR_NAME, nexpr->name) ||
- nla_put_be32(skb, NFTA_NEXPR_USE, htonl(nexpr->use)) ||
- nft_expr_dump(skb, NFTA_NEXPR_EXPR, nexpr->expr))
+ if (nft_nexpr_dump(skb, table, nexpr, reset))
goto nla_put_failure;
nlmsg_end(skb, nlh);
@@ -3963,6 +3988,10 @@ static int nf_tables_dump_nexpr(struct sk_buff *skb,
unsigned int idx = 0, s_idx = cb->args[0];
struct net *net = sock_net(skb->sk);
int family = nfmsg->nfgen_family;
+ bool reset = false;
+
+ if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) == NFT_MSG_GETNEXPR_RESET)
+ reset = true;
rcu_read_lock();
cb->seq = net->nft.base_seq;
@@ -3982,7 +4011,7 @@ static int nf_tables_dump_nexpr(struct sk_buff *skb,
cb->nlh->nlmsg_seq,
NFT_MSG_NEWNEXPR,
NLM_F_MULTI | NLM_F_APPEND,
- afi->family, table, nexpr) < 0)
+ afi->family, table, nexpr, reset) < 0)
goto done;
nl_dump_check_consistent(cb, nlmsg_hdr(skb));
@@ -4008,6 +4037,7 @@ static int nf_tables_getnexpr(struct net *net, struct sock *nlsk,
const struct nft_table *table;
struct nft_nexpr *nexpr;
struct sk_buff *skb2;
+ bool reset = false;
int err;
if (nlh->nlmsg_flags & NLM_F_DUMP) {
@@ -4035,9 +4065,12 @@ static int nf_tables_getnexpr(struct net *net, struct sock *nlsk,
if (!skb2)
return -ENOMEM;
+ if (NFNL_MSG_TYPE(nlh->nlmsg_type) == NFT_MSG_GETNEXPR_RESET)
+ reset = true;
+
err = nf_tables_fill_nexpr_info(skb2, net, NETLINK_CB(skb).portid,
nlh->nlmsg_seq, NFT_MSG_NEWNEXPR, 0,
- family, table, nexpr);
+ family, table, nexpr, reset);
if (err < 0)
goto err;
@@ -4103,7 +4136,7 @@ static int nf_tables_nexpr_notify(const struct nft_ctx *ctx,
err = nf_tables_fill_nexpr_info(skb, ctx->net, ctx->portid, ctx->seq,
event, 0, ctx->afi->family, ctx->table,
- nexpr);
+ nexpr, false);
if (err < 0) {
kfree_skb(skb);
goto err;
@@ -4213,6 +4246,11 @@ static const struct nfnl_callback nf_tables_cb[NFT_MSG_MAX] = {
.attr_count = NFTA_NEXPR_MAX,
.policy = nft_nexpr_policy,
},
+ [NFT_MSG_GETNEXPR_RESET] = {
+ .call = nf_tables_getnexpr,
+ .attr_count = NFTA_NEXPR_MAX,
+ .policy = nft_nexpr_policy,
+ },
};
static void nft_chain_commit_update(struct nft_trans *trans)
diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c
index c9743f7..eec5d63 100644
--- a/net/netfilter/nft_counter.c
+++ b/net/netfilter/nft_counter.c
@@ -47,21 +47,29 @@ static void nft_counter_eval(const struct nft_expr *expr,
local_bh_enable();
}
-static void nft_counter_fetch(const struct nft_counter_percpu __percpu *counter,
- struct nft_counter *total)
+static void nft_counter_fetch(struct nft_counter_percpu __percpu *counter,
+ struct nft_counter *total, bool reset)
{
- const struct nft_counter_percpu *cpu_stats;
+ struct nft_counter_percpu *cpu_stats;
u64 bytes, packets;
unsigned int seq;
int cpu;
memset(total, 0, sizeof(*total));
for_each_possible_cpu(cpu) {
+ if (reset)
+ bytes = packets = 0;
+
cpu_stats = per_cpu_ptr(counter, cpu);
do {
seq = u64_stats_fetch_begin_irq(&cpu_stats->syncp);
- bytes = cpu_stats->counter.bytes;
- packets = cpu_stats->counter.packets;
+ if (reset) {
+ packets += xchg(&cpu_stats->counter.packets, 0);
+ bytes += xchg(&cpu_stats->counter.bytes, 0);
+ } else {
+ bytes = cpu_stats->counter.bytes;
+ packets = cpu_stats->counter.packets;
+ }
} while (u64_stats_fetch_retry_irq(&cpu_stats->syncp, seq));
total->packets += packets;
@@ -69,12 +77,13 @@ static void nft_counter_fetch(const struct nft_counter_percpu __percpu *counter,
}
}
-static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr)
+static int __nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr,
+ bool reset)
{
struct nft_counter_percpu_priv *priv = nft_expr_priv(expr);
struct nft_counter total;
- nft_counter_fetch(priv->counter, &total);
+ nft_counter_fetch(priv->counter, &total, reset);
if (nla_put_be64(skb, NFTA_COUNTER_BYTES, cpu_to_be64(total.bytes)) ||
nla_put_be64(skb, NFTA_COUNTER_PACKETS, cpu_to_be64(total.packets)))
@@ -85,6 +94,16 @@ nla_put_failure:
return -1;
}
+static int nft_counter_dump(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ return __nft_counter_dump(skb, expr, false);
+}
+
+static int nft_counter_reset(struct sk_buff *skb, const struct nft_expr *expr)
+{
+ return __nft_counter_dump(skb, expr, true);
+}
+
static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = {
[NFTA_COUNTER_PACKETS] = { .type = NLA_U64 },
[NFTA_COUNTER_BYTES] = { .type = NLA_U64 },
@@ -133,7 +152,7 @@ static int nft_counter_clone(struct nft_expr *dst, const struct nft_expr *src)
struct nft_counter_percpu *this_cpu;
struct nft_counter total;
- nft_counter_fetch(priv->counter, &total);
+ nft_counter_fetch(priv->counter, &total, false);
cpu_stats = __netdev_alloc_pcpu_stats(struct nft_counter_percpu,
GFP_ATOMIC);
@@ -158,6 +177,7 @@ static const struct nft_expr_ops nft_counter_ops = {
.init = nft_counter_init,
.destroy = nft_counter_destroy,
.dump = nft_counter_dump,
+ .reset = nft_counter_reset,
.clone = nft_counter_clone,
};
--
2.1.4
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH RFC nf-next 0/3] named expressions for nf_tables
2016-04-06 16:51 [PATCH RFC nf-next 0/3] named expressions for nf_tables Pablo Neira Ayuso
` (2 preceding siblings ...)
2016-04-06 16:51 ` [PATCH RFC nf-next 3/3] netfilter: nf_tables: support dump and reset for named expressions Pablo Neira Ayuso
@ 2016-04-07 21:49 ` Florian Westphal
2016-04-08 11:43 ` Pablo Neira Ayuso
2016-04-08 12:12 ` Florian Westphal
2016-04-11 15:27 ` Andreas Schultz
5 siblings, 1 reply; 11+ messages in thread
From: Florian Westphal @ 2016-04-07 21:49 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel
Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> This patchset introduces the named stateful expressions for nf_tables,
> that allows userspace to set a name for the stateful expression for
> several reasons:
>
> * Provide a unique identifier to fetch and reset it internal state.
> * Allow to update of their parameters and internal state.
> * Allow to fetch and reset its internal state.
> * Refer to the same stateful expression from one or more rules.
>
Can't all of this be achieved via custom chains already...?
> nf_tables currently supports to stateful expressions: limit and counter,
> therefore you can create named instances of this expressions through
> this new infrastructure.
> Several examples on how this would look from userspace:
>
> * Add the 'tcp-counter' counter to the 'filter' table:
>
> # nft add counter filter tcp-counter
>
> * Delete this counter (only possible if not dereferenced from a rule):
>
> # nft delete counter filter tcp-counter
>
> * List existing named counters:
>
> # nft lists counters
> table ip filter {
> counter tcp-counter {
> packets 6086 bytes 6278052
> }
> counter udp-counter {
> packets 272 bytes 64690
> }
> counter icmp-counter {
> packets 10 bytes 840
> }
> }
Can't you already create a 'named counter' via
chain tcp-counter {
counter
}
?
Seems to me we only would have to teach nft list to
allow filtering output on chain names, e.g.
nft list table filter \*-counter
to have something similar using exsiting feature set.
> So far, only counters are supported, but it should be possible to
> support named limits. I have another (imcomplete) patch that allows to
> update the named expressions parameters, this can be useful to
> dynamically update the ratelimiting policies, the command line should
> look like:
>
> # nft update limit name user01234 rate 250 mbytes/day
Similar comment here, you could create
chain user01234 {
limit rate 100 mbytes/day
}
This feature seems strange to me, AFAIU it adds a container for single
expressions, so it just seems to be a subset of what we already have
(namely chains as containers of rules).
What am I missing? :)
Is there something that is too cumbersome to achive with a chain-based
approach?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC nf-next 0/3] named expressions for nf_tables
2016-04-07 21:49 ` [PATCH RFC nf-next 0/3] named expressions for nf_tables Florian Westphal
@ 2016-04-08 11:43 ` Pablo Neira Ayuso
2016-04-08 12:04 ` Florian Westphal
0 siblings, 1 reply; 11+ messages in thread
From: Pablo Neira Ayuso @ 2016-04-08 11:43 UTC (permalink / raw)
To: Florian Westphal; +Cc: netfilter-devel
On Thu, Apr 07, 2016 at 11:49:42PM +0200, Florian Westphal wrote:
> Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> > Several examples on how this would look from userspace:
> >
> > * Add the 'tcp-counter' counter to the 'filter' table:
> >
> > # nft add counter filter tcp-counter
> >
> > * Delete this counter (only possible if not dereferenced from a rule):
> >
> > # nft delete counter filter tcp-counter
> >
> > * List existing named counters:
> >
> > # nft lists counters
> > table ip filter {
> > counter tcp-counter {
> > packets 6086 bytes 6278052
> > }
> > counter udp-counter {
> > packets 272 bytes 64690
> > }
> > counter icmp-counter {
> > packets 10 bytes 840
> > }
> > }
>
> Can't you already create a 'named counter' via
>
> chain tcp-counter {
> counter
> }
>
> ?
>
> Seems to me we only would have to teach nft list to
> allow filtering output on chain names, e.g.
>
> nft list table filter \*-counter
>
> to have something similar using exsiting feature set.
Right, but we still need a way to uniquely identify this stateful
expression for atomic dump'n'reset.
> > So far, only counters are supported, but it should be possible to
> > support named limits. I have another (imcomplete) patch that allows to
> > update the named expressions parameters, this can be useful to
> > dynamically update the ratelimiting policies, the command line should
> > look like:
> >
> > # nft update limit name user01234 rate 250 mbytes/day
>
> Similar comment here, you could create
>
> chain user01234 {
> limit rate 100 mbytes/day
> }
Right, we can replace this rule via the handle, but I see two problems
with this:
1) The user needs to track the rule handle, not so much of a problem
since userspace can do this at the cost of a bit of more
complexity.
2) When replacing the rule, the new expression starts from no history
as it will be a new expression, we'll be basically reseting it.
Using 'nft update limit...' I think we should be capable of upgrading
the ratelimit by keeping around the quota that the user has already
consumed.
The idea is, if we know the previous cost of every byte/packets in
terms of tokens, given that tokens are generated per nanosecond and
that know how many tokens we have already consumed, then we can
recalculate the quota that has been already consumed and add this to
the limit update, eg. if limit rate is 100 mbytes/day but the user
already consumed 50 mbytes, when calling:
nft update limit rate 200 mbytes/day
we can keep the 50 mbytes that were already consumed around.
Let me know, thanks.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC nf-next 0/3] named expressions for nf_tables
2016-04-08 11:43 ` Pablo Neira Ayuso
@ 2016-04-08 12:04 ` Florian Westphal
0 siblings, 0 replies; 11+ messages in thread
From: Florian Westphal @ 2016-04-08 12:04 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: Florian Westphal, netfilter-devel
Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> 2) When replacing the rule, the new expression starts from no history
> as it will be a new expression, we'll be basically reseting it.
You are right, I was only thinking of counter (where userspace can
set packets/bytes), but limit would lose its state as most of that
is internal only.
So I think #2 makes sense.
I have more concerns (sorry!) wrt user representation, I'll reply
to this in a new email.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC nf-next 0/3] named expressions for nf_tables
2016-04-06 16:51 [PATCH RFC nf-next 0/3] named expressions for nf_tables Pablo Neira Ayuso
` (3 preceding siblings ...)
2016-04-07 21:49 ` [PATCH RFC nf-next 0/3] named expressions for nf_tables Florian Westphal
@ 2016-04-08 12:12 ` Florian Westphal
2016-04-11 15:27 ` Andreas Schultz
5 siblings, 0 replies; 11+ messages in thread
From: Florian Westphal @ 2016-04-08 12:12 UTC (permalink / raw)
To: Pablo Neira Ayuso; +Cc: netfilter-devel
Pablo Neira Ayuso <pablo@netfilter.org> wrote:
> * List existing named counters:
>
> # nft lists counters
> table ip filter {
> counter tcp-counter {
> packets 6086 bytes 6278052
> }
> counter udp-counter {
> packets 272 bytes 64690
> }
> counter icmp-counter {
> packets 10 bytes 840
> }
> }
So if we extend this scheme to all (stateful) expressions we'll have to
add 'nft list limits' (or whatever).
Do you think it makes sense to represent this in a more generic fashion?
nft list tables
nft list chains
Maybe add
nft list expressions
?
> The snippet below shows a simplistic configuration to account tcp, udp
> and icmp traffic through the named counter:
>
> -o-
> table ip filter {
> counter tcp-counter {
> packets 6086 bytes 6278052
> }
So this could f.e. look like
expression tcp-counter {
counter packets 6086 bytes 6278052
}
(and tcp-counter is just some identifier).
> support named limits. I have another (imcomplete) patch that allows to
> update the named expressions parameters, this can be useful to
> dynamically update the ratelimiting policies, the command line should
> look like:
>
> # nft update limit name user01234 rate 250 mbytes/day
nft update expression user01234 rate 250 mbytes/day
(or perhaps
nft update expression user01234 limit rate 250 mbytes/day
not sure if we want to allow replacing the (internal) expression
stored in a named expression with a different one....)
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC nf-next 0/3] named expressions for nf_tables
2016-04-06 16:51 [PATCH RFC nf-next 0/3] named expressions for nf_tables Pablo Neira Ayuso
` (4 preceding siblings ...)
2016-04-08 12:12 ` Florian Westphal
@ 2016-04-11 15:27 ` Andreas Schultz
2016-04-19 19:46 ` Pablo Neira Ayuso
5 siblings, 1 reply; 11+ messages in thread
From: Andreas Schultz @ 2016-04-11 15:27 UTC (permalink / raw)
To: Pablo Neira Ayuso, netfilter-devel
Hi,
On 04/06/2016 06:51 PM, Pablo Neira Ayuso wrote:
> Hi,
>
> This patchset introduces the named stateful expressions for nf_tables,
> that allows userspace to set a name for the stateful expression for
> several reasons:
>
> * Provide a unique identifier to fetch and reset it internal state.
> * Allow to update of their parameters and internal state.
> * Allow to fetch and reset its internal state.
> * Refer to the same stateful expression from one or more rules.
Would this support to have rules based on the value of a counter and/or
the current rate (like quotas in nfacct)?
something like this:
nft add rule filter tcp-chain counter name tcp-counter bytes > 10000 jump deny
A more realistic setup might more look like:
table ip filter {
counter counter-user1234 {
packets 6086 bytes 6278052
}
chain chain-user1234 {
counter name counter-user1234
counter name counter-user1234 > 10000000 goto chain-user1234-overlimit
counter name counter-user1234 > 500000 goto rate-limit
accept
}
chain chain-user1234-overlimit {
do-once notify userspace somehow
reject
}
}
As far as I know there is currently no mechanism in nft that could do
the "do-once notify userspace somehow", or is there???
The other issue I have with such a scheme that it requires lots of chains per
client and might limit the number of clients that could be supported.
Regards
Andreas
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC nf-next 0/3] named expressions for nf_tables
2016-04-11 15:27 ` Andreas Schultz
@ 2016-04-19 19:46 ` Pablo Neira Ayuso
2016-04-20 6:41 ` Pablo Neira Ayuso
0 siblings, 1 reply; 11+ messages in thread
From: Pablo Neira Ayuso @ 2016-04-19 19:46 UTC (permalink / raw)
To: Andreas Schultz; +Cc: netfilter-devel
Hi Andreas,
On Mon, Apr 11, 2016 at 05:27:59PM +0200, Andreas Schultz wrote:
> Would this support to have rules based on the value of a counter and/or
> the current rate (like quotas in nfacct)?
>
> something like this:
>
> nft add rule filter tcp-chain counter name tcp-counter bytes > 10000 jump deny
>
> A more realistic setup might more look like:
>
> table ip filter {
> counter counter-user1234 {
> packets 6086 bytes 6278052
> }
>
> chain chain-user1234 {
> counter name counter-user1234
> counter name counter-user1234 > 10000000 goto chain-user1234-overlimit
> counter name counter-user1234 > 500000 goto rate-limit
> accept
> }
>
> chain chain-user1234-overlimit {
> do-once notify userspace somehow
> reject
> }
I think we can express this with:
nft add limit counter-user1234 rate over 100 mbytes/day
nft add rule filter input \
limit name counter-user1234 \
log prefix "user1234" group 10 \
reject
The idea is to create a 'counter-user1234' limit. Then refer to this
from the rule.
BTW, currently the 'reject' statement will rely on icmp unreach to
reject this. Probably you want a plain 'drop' here.
> As far as I know there is currently no mechanism in nft that could do
> the "do-once notify userspace somehow", or is there???
You can do this through the log statement, and then use
libnetfilter_log for your application.
> The other issue I have with such a scheme that it requires lots of chains per
> client and might limit the number of clients that could be supported.
I think we can skip the extra non-base chains in the example above.
The question is if you would be OK by specifying the quota using
ratelimit, opposed to counter.
The idea is that you can reset limits with something like:
nft reset limit name user1234
so this basically resets the quota. Or even upgrade via:
nft update limit name user1234 over 1000 mbytes/day
(this would keep what it's been consumed already, so statefulness is
preserved).
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH RFC nf-next 0/3] named expressions for nf_tables
2016-04-19 19:46 ` Pablo Neira Ayuso
@ 2016-04-20 6:41 ` Pablo Neira Ayuso
0 siblings, 0 replies; 11+ messages in thread
From: Pablo Neira Ayuso @ 2016-04-20 6:41 UTC (permalink / raw)
To: Andreas Schultz; +Cc: netfilter-devel
On Tue, Apr 19, 2016 at 09:46:38PM +0200, Pablo Neira Ayuso wrote:
> On Mon, Apr 11, 2016 at 05:27:59PM +0200, Andreas Schultz wrote:
> > Would this support to have rules based on the value of a counter and/or
> > the current rate (like quotas in nfacct)?
> >
> > something like this:
> >
> > nft add rule filter tcp-chain counter name tcp-counter bytes > 10000 jump deny
> >
> > A more realistic setup might more look like:
> >
> > table ip filter {
> > counter counter-user1234 {
> > packets 6086 bytes 6278052
> > }
> >
> > chain chain-user1234 {
> > counter name counter-user1234
> > counter name counter-user1234 > 10000000 goto chain-user1234-overlimit
> > counter name counter-user1234 > 500000 goto rate-limit
> > accept
> > }
> >
> > chain chain-user1234-overlimit {
> > do-once notify userspace somehow
> > reject
> > }
>
> I think we can express this with:
>
> nft add limit counter-user1234 rate over 100 mbytes/day
> nft add rule filter input \
> limit name counter-user1234 \
> log prefix "user1234" group 10 \
> reject
Actually using the limit rate policer will not work for quota-like
stuff since the budget gets refilled for each packet that is received.
> The idea is to create a 'counter-user1234' limit. Then refer to this
> from the rule.
>
> BTW, currently the 'reject' statement will rely on icmp unreach to
> reject this. Probably you want a plain 'drop' here.
>
> > As far as I know there is currently no mechanism in nft that could do
> > the "do-once notify userspace somehow", or is there???
>
> You can do this through the log statement, and then use
> libnetfilter_log for your application.
libnetfilter_log will keep spamming userspace after going overlimit.
You most likely want a single report event notification to userspace.
Anyway, I understand your use case, will come back with an update on
this.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2016-04-20 6:42 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-04-06 16:51 [PATCH RFC nf-next 0/3] named expressions for nf_tables Pablo Neira Ayuso
2016-04-06 16:51 ` [PATCH RFC nf-next 1/3] netfilter: nf_tables: add stateful named expressions Pablo Neira Ayuso
2016-04-06 16:51 ` [PATCH RFC nf-next 2/3] netfilter: nf_tables: support for named expression reference Pablo Neira Ayuso
2016-04-06 16:51 ` [PATCH RFC nf-next 3/3] netfilter: nf_tables: support dump and reset for named expressions Pablo Neira Ayuso
2016-04-07 21:49 ` [PATCH RFC nf-next 0/3] named expressions for nf_tables Florian Westphal
2016-04-08 11:43 ` Pablo Neira Ayuso
2016-04-08 12:04 ` Florian Westphal
2016-04-08 12:12 ` Florian Westphal
2016-04-11 15:27 ` Andreas Schultz
2016-04-19 19:46 ` Pablo Neira Ayuso
2016-04-20 6:41 ` Pablo Neira Ayuso
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).