From: Jan Engelhardt <jengelh@medozas.de>
To: netfilter-devel@vger.kernel.org
Cc: pablo@netfilter.org
Subject: [PATCH 3/7] netfilter: xtables2: chain creation and deletion
Date: Thu, 19 Jan 2012 17:26:17 +0100 [thread overview]
Message-ID: <1326990381-14534-4-git-send-email-jengelh@medozas.de> (raw)
In-Reply-To: <1326990381-14534-1-git-send-email-jengelh@medozas.de>
Notes: xt2_core will have two independent users: xt2_nfnetlink and the
xt1 translator, and they shall be separate modules, thereby
necessiting the use of EXPORT_SYMBOLs.
Signed-off-by: Jan Engelhardt <jengelh@medozas.de>
---
include/linux/netfilter/nfnetlink_xtables.h | 18 ++++
include/net/netfilter/x_tables2.h | 31 ++++++-
net/netfilter/xt2_core.c | 81 +++++++++++++++-
net/netfilter/xt2_nfnetlink.c | 146 ++++++++++++++++++++++++++-
4 files changed, 273 insertions(+), 3 deletions(-)
diff --git a/include/linux/netfilter/nfnetlink_xtables.h b/include/linux/netfilter/nfnetlink_xtables.h
index 4c53042..fe1b6ce 100644
--- a/include/linux/netfilter/nfnetlink_xtables.h
+++ b/include/linux/netfilter/nfnetlink_xtables.h
@@ -3,18 +3,36 @@
enum nfxt_msg_type {
NFXTM_IDENTIFY = 1,
+ NFXTM_CHAIN_NEW,
+ NFXTM_CHAIN_DEL,
};
/**
* %NFXTA_NAME: name of the object being operated on
+ * %NFXTA_ERRNO: system error code (%Exxx)
+ * %NFXTA_XTERRNO: NFXT-specific error code (cf. enum nfxt_errno)
*/
enum nfxt_attr_type {
NFXTA_UNSPEC = 0,
NFXTA_NAME,
+ NFXTA_ERRNO,
+ NFXTA_XTERRNO,
};
+/**
+ * %NFXTE_ATTRSET_INCOMPLETE: Not all required attributes are present in nlmsg
+ * %NFXTE_CHAIN_INVALID_NAME: Chain name is not acceptable
+ * %NFXTE_CHAIN_EXIST: Chain already exists
+ * %NFXTE_CHAIN_NOENT: Chain does not exist
+ * %NFXTE_CHAIN_NAMETOOLONG: New chain name is too long
+ */
enum nfxt_errno {
NFXTE_SUCCESS = 0,
+ NFXTE_ATTRSET_INCOMPLETE,
+ NFXTE_CHAIN_INVALID_NAME,
+ NFXTE_CHAIN_EXISTS,
+ NFXTE_CHAIN_NOENT,
+ NFXTE_CHAIN_NAMETOOLONG,
};
#endif /* _LINUX_NFNETLINK_XTABLES_H */
diff --git a/include/net/netfilter/x_tables2.h b/include/net/netfilter/x_tables2.h
index a219952..b13eab7 100644
--- a/include/net/netfilter/x_tables2.h
+++ b/include/net/netfilter/x_tables2.h
@@ -1,17 +1,46 @@
#ifndef _NET_NETFILTER_XTABLES2_H
#define _NET_NETFILTER_XTABLES2_H 1
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/rcupdate.h>
+
#define XTABLES2_VTAG "Xtables2 A8"
/**
* @master: the master table
+ * @master_lock: protecting changes to @master
*/
struct xt2_pernet_data {
struct xt2_table __rcu *master;
+ struct mutex master_lock;
};
+/**
+ * @chain_list: list of chains (struct xt2_chain)
+ * @lock: protecting changes to @chain_list
+ */
struct xt2_table {
- int _dummy;
+ struct list_head chain_list;
+ struct mutex lock;
};
+/**
+ * @anchor: list anchor for parent (struct xt2_table.chain_list)
+ * @name: name of chain; its large size is for the xt1 translator
+ * @rcu: rcu head for delayed deletion
+ */
+struct xt2_chain {
+ struct list_head anchor;
+ char name[48];
+ struct rcu_head rcu;
+};
+
+struct net;
+
+extern struct xt2_pernet_data *xtables2_pernet(struct net *);
+extern struct xt2_chain *xt2_chain_new(struct xt2_table *, const char *);
+extern struct xt2_chain *xt2_chain_lookup(struct xt2_table *, const char *);
+extern void xt2_chain_free(struct xt2_chain *);
+
#endif /* _NET_NETFILTER_XTABLES2_H */
diff --git a/net/netfilter/xt2_core.c b/net/netfilter/xt2_core.c
index ab73c4d..5e7426d 100644
--- a/net/netfilter/xt2_core.c
+++ b/net/netfilter/xt2_core.c
@@ -9,9 +9,12 @@
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/err.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/rculist.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/netfilter/x_tables2.h>
@@ -22,10 +25,79 @@ MODULE_LICENSE("GPL");
static int xtables2_net_id __read_mostly;
-static inline struct xt2_pernet_data *xtables2_pernet(struct net *net)
+struct xt2_pernet_data *xtables2_pernet(struct net *net)
{
return net_generic(net, xtables2_net_id);
}
+EXPORT_SYMBOL_GPL(xtables2_pernet);
+
+/**
+ * @table: table to add the new chain to
+ * @name: name for the chain; may be %NULL
+ *
+ * Creates a new chain for @table using the given @name. @name may be NULL or
+ * the empty string, in which case an anonymous chain is created.
+ *
+ * Caller should hold @table->lock and verify chain uniqueness.
+ */
+struct xt2_chain *xt2_chain_new(struct xt2_table *table, const char *name)
+{
+ struct xt2_chain *chain;
+
+ if (strlen(name) >= ARRAY_SIZE(chain->name))
+ return ERR_PTR(-ENAMETOOLONG);
+ chain = kmalloc(sizeof(*chain), GFP_KERNEL);
+ if (chain == NULL)
+ return ERR_PTR(-ENOMEM);
+ INIT_LIST_HEAD(&chain->anchor);
+ if (name != NULL)
+ strncpy(chain->name, name, sizeof(chain->name));
+ else
+ *chain->name = '\0';
+ chain->name[sizeof(chain->name)-1] = '\0';
+ list_add_tail_rcu(&chain->anchor, &table->chain_list);
+ return chain;
+}
+EXPORT_SYMBOL_GPL(xt2_chain_new);
+
+/**
+ * @table: table to search chain in
+ * @name: name of desired chain
+ *
+ * Looks for a chain by its name in the given table.
+ * Caller should hold RCU if the chain is supposed to not go away.
+ * (= Caller can ignore RCU if it just wants an existence test.)
+ */
+struct xt2_chain *xt2_chain_lookup(struct xt2_table *table, const char *name)
+{
+ /* Future patch: Use better-suited data structure. */
+ struct xt2_chain *chain;
+
+ list_for_each_entry_rcu(chain, &table->chain_list, anchor)
+ if (strcmp(chain->name, name) == 0)
+ return chain;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(xt2_chain_lookup);
+
+/**
+ * Frees the chain and all its associated memory.
+ */
+static void xt2_chain_free_rcu(struct rcu_head *rcu)
+{
+ kfree(container_of(rcu, struct xt2_chain, rcu));
+}
+
+void xt2_chain_free(struct xt2_chain *chain)
+{
+ /*
+ * Not using kfree_rcu here, as we may want to free more,
+ * in xt2_chain_free_rcu, soon.
+ */
+ list_del_rcu(&chain->anchor);
+ call_rcu(&chain->rcu, xt2_chain_free_rcu);
+}
+EXPORT_SYMBOL_GPL(xt2_chain_free);
/**
* Create a new table with no chains and no rules.
@@ -38,11 +110,17 @@ static struct xt2_table *xt2_table_new(void)
if (table == NULL)
return NULL;
+ mutex_init(&table->lock);
+ INIT_LIST_HEAD(&table->chain_list);
return table;
}
static void xt2_table_free(struct xt2_table *table)
{
+ struct xt2_chain *chain, *next;
+
+ list_for_each_entry_safe(chain, next, &table->chain_list, anchor)
+ xt2_chain_free(chain);
kfree(table);
}
@@ -50,6 +128,7 @@ static int __net_init xtables2_net_init(struct net *net)
{
struct xt2_pernet_data *pnet = xtables2_pernet(net);
+ mutex_init(&pnet->master_lock);
pnet->master = xt2_table_new();
if (IS_ERR(pnet->master))
return PTR_ERR(pnet->master);
diff --git a/net/netfilter/xt2_nfnetlink.c b/net/netfilter/xt2_nfnetlink.c
index 3dc241f..b50e468d 100644
--- a/net/netfilter/xt2_nfnetlink.c
+++ b/net/netfilter/xt2_nfnetlink.c
@@ -17,6 +17,7 @@
#include <linux/netfilter/nfnetlink.h>
#include <linux/netfilter/nfnetlink_xtables.h>
#include <net/netlink.h>
+#include <net/sock.h>
#include <net/netfilter/x_tables2.h>
MODULE_DESCRIPTION("Xtables2 nfnetlink interface");
@@ -69,6 +70,66 @@ xtnetlink_fill(struct sk_buff *skb, const struct xtnetlink_pktref *old,
}
/**
+ * @ref: skb/nl pointers that will be filled in (secondary return values)
+ * @old: pointers to the original incoming skb/nl headers
+ *
+ * xtnetlink_fill can be used when the outgoing skb already exists (e.g. in
+ * case of a dump operation), but for non-dump responses, we have to create it
+ * ourselves.
+ */
+static int
+xtnetlink_new_fill(struct xtnetlink_pktref *ref,
+ const struct xtnetlink_pktref *old)
+{
+ ref->skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+ if (ref->skb == NULL)
+ return -ENOMEM;
+ ref->msg = xtnetlink_fill(ref->skb, old, 0);
+ if (IS_ERR(ref->msg)) {
+ kfree_skb(ref->skb);
+ return PTR_ERR(ref->msg);
+ }
+ return 0;
+}
+
+/**
+ * @xtnl: socket to send the error packet out on
+ * @old: pointers to the original incoming skb/nl headers
+ * @errcode: last error code
+ *
+ * Create and send out an NFXT error packet. If @errcode is < 0, it indicates
+ * a system-level error (such as %-ENOMEM), reported back using %NFXTA_ERRNO.
+ * If @errcode is >= 0, it indicates an NFXT-specific error codes (%NFXTE_*),
+ * which is more fine grained than the dreaded %EINVAL, and which is reported
+ * back using %NFXTA_XTERRNO.
+ */
+static int
+xtnetlink_error(struct sock *xtnl, const struct xtnetlink_pktref *old,
+ int errcode)
+{
+ struct xtnetlink_pktref ref;
+ int ret;
+
+ ret = xtnetlink_new_fill(&ref, old);
+ if (ret < 0)
+ return ret;
+ if (errcode < 0)
+ /* Prefer positive numbers on the wire */
+ NLA_PUT_U32(ref.skb, NFXTA_ERRNO, -errcode);
+ else
+ NLA_PUT_U32(ref.skb, NFXTA_XTERRNO, errcode);
+ nlmsg_end(ref.skb, ref.msg);
+ ret = netlink_unicast(xtnl, ref.skb, NETLINK_CB(old->skb).pid,
+ MSG_DONTWAIT);
+ if (ret < 0)
+ return ret;
+ /* ret is skb->len, but values >0 mean error to the caller -.- */
+ return 0;
+ nla_put_failure:
+ return -ENOBUFS;
+}
+
+/**
* Ran too often into NULL derefs. Now there is a dummy function for unused
* message type 0.
*/
@@ -113,14 +174,95 @@ xtnetlink_identify(struct sock *xtnl, struct sk_buff *iskb,
NULL, 0);
}
+static int
+xtnetlink_chain_new(struct sock *xtnl, struct sk_buff *iskb,
+ const struct nlmsghdr *imsg, const struct nlattr *const *ad)
+{
+ struct xt2_pernet_data *pnet = xtables2_pernet(sock_net(xtnl));
+ struct xtnetlink_pktref ref = {.c_skb = iskb, .c_msg = imsg};
+ const struct nlattr *attr;
+ struct xt2_table *table;
+ struct xt2_chain *chain;
+ const char *name;
+ int ret = 0;
+
+ attr = nlmsg_find_attr(imsg, sizeof(struct nfgenmsg), NFXTA_NAME);
+ if (attr == NULL)
+ return xtnetlink_error(xtnl, &ref, NFXTE_ATTRSET_INCOMPLETE);
+ name = nla_data(attr);
+ if (*name == '\0')
+ /* Anonymous chains are internal. */
+ return xtnetlink_error(xtnl, &ref, NFXTE_CHAIN_INVALID_NAME);
+ /*
+ * The table needs to stay, but note that rcu_read_lock cannot be used,
+ * since we might sleep.
+ */
+ mutex_lock(&pnet->master_lock);
+ table = pnet->master;
+ mutex_lock(&table->lock);
+ if (xt2_chain_lookup(table, name) != NULL) {
+ ret = xtnetlink_error(xtnl, &ref, NFXTE_CHAIN_EXISTS);
+ } else {
+ chain = xt2_chain_new(table, name);
+ if (IS_ERR(chain))
+ ret = PTR_ERR(chain);
+ /* Use NFXTE error codes whenever possible. */
+ if (ret == -ENAMETOOLONG)
+ ret = NFXTE_CHAIN_NAMETOOLONG;
+ ret = xtnetlink_error(xtnl, &ref, ret);
+ }
+ mutex_unlock(&table->lock);
+ mutex_unlock(&pnet->master_lock);
+ return ret;
+}
+
+/**
+ * Act on a %NFXTM_CHAIN_DEL message.
+ */
+static int
+xtnetlink_chain_del(struct sock *xtnl, struct sk_buff *iskb,
+ const struct nlmsghdr *imsg,
+ const struct nlattr *const *ad)
+{
+ struct xt2_pernet_data *pnet = xtables2_pernet(sock_net(xtnl));
+ struct xtnetlink_pktref ref = {.c_skb = iskb, .c_msg = imsg};
+ const struct nlattr *name_attr;
+ struct xt2_table *table;
+ struct xt2_chain *chain;
+ const char *name;
+ int ret = 0;
+
+ name_attr = nlmsg_find_attr(imsg, sizeof(struct nfgenmsg), NFXTA_NAME);
+ if (name_attr == NULL)
+ return xtnetlink_error(xtnl, &ref, NFXTE_ATTRSET_INCOMPLETE);
+ name = nla_data(name_attr);
+ if (*name == '\0')
+ return xtnetlink_error(xtnl, &ref, NFXTE_CHAIN_NOENT);
+
+ mutex_lock(&pnet->master_lock);
+ table = pnet->master;
+ mutex_lock(&table->lock);
+ chain = xt2_chain_lookup(table, name);
+ if (chain != NULL)
+ xt2_chain_free(chain);
+ else
+ ret = NFXTE_CHAIN_NOENT;
+ ret = xtnetlink_error(xtnl, &ref, ret);
+ mutex_unlock(&table->lock);
+ mutex_unlock(&pnet->master_lock);
+ return ret;
+}
+
static const struct nla_policy xtnetlink_policy[] = {
[NFXTA_NAME] = {.type = NLA_NUL_STRING},
+ [NFXTA_ERRNO] = {.type = NLA_U32},
+ [NFXTA_XTERRNO] = {.type = NLA_U32},
};
/*
* Use the same policy for all messages. I do not want to see EINVAL anytime
* soon again just because I forgot sending an attribute from userspace.
- * (If such occurs, it will be dealt with %NFXTE_ATTRSET_INCOMPLETE, tbd.)
+ * (If such occurs, it will be dealt with %NFXTE_ATTRSET_INCOMPLETE.)
*/
#define pol \
.policy = xtnetlink_policy, \
@@ -128,6 +270,8 @@ static const struct nla_policy xtnetlink_policy[] = {
static const struct nfnl_callback xtnetlink_callback[] = {
[0] = {.call = xtnetlink_ignore},
[NFXTM_IDENTIFY] = {.call = xtnetlink_identify, pol},
+ [NFXTM_CHAIN_NEW] = {.call = xtnetlink_chain_new, pol},
+ [NFXTM_CHAIN_DEL] = {.call = xtnetlink_chain_del, pol},
};
#undef pol
--
1.7.7
next prev parent reply other threads:[~2012-01-19 16:26 UTC|newest]
Thread overview: 23+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-01-19 16:26 xtables2 a8, netlink interface Jan Engelhardt
2012-01-19 16:26 ` [PATCH 1/7] netfilter: xtables2: initial table skeletal functions Jan Engelhardt
2012-01-20 0:23 ` Pablo Neira Ayuso
2012-01-20 9:23 ` Jan Engelhardt
2012-01-19 16:26 ` [PATCH 2/7] netfilter: xtables2: initial Netlink interface Jan Engelhardt
2012-02-14 10:47 ` Pablo Neira Ayuso
2012-02-14 15:56 ` Jan Engelhardt
2012-02-14 19:53 ` Pablo Neira Ayuso
2012-01-19 16:26 ` Jan Engelhardt [this message]
2012-02-14 11:07 ` [PATCH 3/7] netfilter: xtables2: chain creation and deletion Pablo Neira Ayuso
2012-01-19 16:26 ` [PATCH 4/7] netfilter: xtables2: chain renaming support Jan Engelhardt
2012-01-19 16:26 ` [PATCH 5/7] netfilter: xtables2: initial table replace support Jan Engelhardt
2012-01-19 16:26 ` [PATCH 6/7] netfilter: xtables2: transaction abort support Jan Engelhardt
2012-01-19 16:26 ` [PATCH 7/7] netfilter: xtables2: redirect writes into transaction buffer Jan Engelhardt
2012-01-20 0:56 ` xtables2 a8, netlink interface Stephen Hemminger
2012-01-20 8:33 ` Jan Engelhardt
2012-01-20 9:23 ` Dave Taht
2012-01-20 16:50 ` Stephen Hemminger
2012-01-21 14:10 ` Jozsef Kadlecsik
2012-01-21 15:53 ` Jan Engelhardt
2012-01-21 20:21 ` Jozsef Kadlecsik
2012-01-23 15:42 ` Jan Engelhardt
2012-01-23 19:48 ` Jozsef Kadlecsik
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1326990381-14534-4-git-send-email-jengelh@medozas.de \
--to=jengelh@medozas.de \
--cc=netfilter-devel@vger.kernel.org \
--cc=pablo@netfilter.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).