Patchwork [08/11] netfilter: xtables2: redirect writes into transaction buffer

login
register
mail settings
Submitter Jan Engelhardt
Date Nov. 2, 2012, 3:38 a.m.
Message ID <1351827523-10629-9-git-send-email-jengelh@inai.de>
Download mbox | patch
Permalink /patch/196481/
State Not Applicable
Headers show

Comments

Jan Engelhardt - Nov. 2, 2012, 3:38 a.m.
While a transaction is active, operations like chain add/del/move
should be done against the temporary table, of course.

This commit adds convenience functions to grab the desired table
object and contain all the locking details.

Signed-off-by: Jan Engelhardt <jengelh@inai.de>
---
 net/netfilter/xt_nfnetlink.c |   76 ++++++++++++++++++++++++++++++------------
 1 file changed, 54 insertions(+), 22 deletions(-)

Patch

diff --git a/net/netfilter/xt_nfnetlink.c b/net/netfilter/xt_nfnetlink.c
index 133a4a8..c53902e 100644
--- a/net/netfilter/xt_nfnetlink.c
+++ b/net/netfilter/xt_nfnetlink.c
@@ -294,6 +294,47 @@  xtnetlink_error(const struct xtnetlink_pktref *old, int errcode)
 }
 
 /**
+ * @xa:		place for returning transaction, if any
+ * @net:	network namespace of the client
+ * @nladdr:	Netlink address of the same
+ *
+ * Return a pointer to either the live table, or to the transaction's temporary
+ * buffer (if there is a transaction open by the client identified by the
+ * {@net,@nladdr} tuple). All appropriate data structures are locked for write.
+ * A symmetric call to xtnetlink_table_wput is required afterwards.
+ */
+static struct xt2_table *
+xtnetlink_table_wget(struct xtnetlink_transact **xa, struct net *net,
+		     uint32_t nladdr)
+{
+	struct xt2_pernet_data *pnet;
+	struct xt2_table *table;
+
+	*xa = xtnetlink_transact_get(net, nladdr);
+	if (*xa == NULL) {
+		pnet = xtables2_pernet(net);
+		mutex_lock(&pnet->master_lock);
+		table = pnet->master;
+	} else {
+		table = (*xa)->table;
+	}
+	mutex_lock(&table->lock);
+	return table;
+}
+
+static void
+xtnetlink_table_wput(struct xtnetlink_transact *xa, struct net *net,
+		     struct xt2_table *table)
+{
+	mutex_unlock(&table->lock);
+	if (xa == NULL)
+		/* Note to self: if @xa is NULL, there is nothing to "put". */
+		mutex_unlock(&xtables2_pernet(net)->master_lock);
+	else
+		xtnetlink_transact_put(xa);
+}
+
+/**
  * Ran too often into NULL derefs. Now there is a dummy function for unused
  * message type 0.
  */
@@ -346,9 +387,9 @@  static int xtnetlink_chain_new(struct sock *xtnl, struct sk_buff *iskb,
 			       const struct nlmsghdr *imsg,
 			       const struct nlattr *const *attr)
 {
-	struct xt2_pernet_data *pnet = xtables2_pernet(sock_net(xtnl));
 	struct xtnetlink_pktref ref =
 		{.c_skb = iskb, .c_msg = imsg, .sock = xtnl};
+	struct xtnetlink_transact *xa;
 	struct xt2_table *table;
 	struct xt2_chain *chain;
 	const char *name;
@@ -360,13 +401,9 @@  static int xtnetlink_chain_new(struct sock *xtnl, struct sk_buff *iskb,
 	if (*name == '\0')
 		/* Anonymous chains are internal. */
 		return xtnetlink_error(&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);
+
+	table = xtnetlink_table_wget(&xa, sock_net(xtnl),
+				     NETLINK_CB(iskb).portid);
 	if (xt2_chain_lookup(table, name) != NULL) {
 		ret = NFXTE_CHAIN_EXISTS;
 	} else {
@@ -377,8 +414,7 @@  static int xtnetlink_chain_new(struct sock *xtnl, struct sk_buff *iskb,
 		if (ret == -ENAMETOOLONG)
 			ret = NFXTE_CHAIN_NAMETOOLONG;
 	}
-	mutex_unlock(&table->lock);
-	mutex_unlock(&pnet->master_lock);
+	xtnetlink_table_wput(xa, sock_net(xtnl), table);
 	return xtnetlink_error(&ref, ret);
 }
 
@@ -390,9 +426,9 @@  xtnetlink_chain_del(struct sock *xtnl, struct sk_buff *iskb,
 		    const struct nlmsghdr *imsg,
 		    const struct nlattr *const *attr)
 {
-	struct xt2_pernet_data *pnet = xtables2_pernet(sock_net(xtnl));
 	struct xtnetlink_pktref ref =
 		{.c_skb = iskb, .c_msg = imsg, .sock = xtnl};
+	struct xtnetlink_transact *xa;
 	struct xt2_table *table;
 	struct xt2_chain *chain;
 	const char *name;
@@ -404,16 +440,14 @@  xtnetlink_chain_del(struct sock *xtnl, struct sk_buff *iskb,
 	if (*name == '\0')
 		return xtnetlink_error(&ref, NFXTE_CHAIN_NOENT);
 
-	mutex_lock(&pnet->master_lock);
-	table = pnet->master;
-	mutex_lock(&table->lock);
+	table = xtnetlink_table_wget(&xa, sock_net(xtnl),
+				     NETLINK_CB(iskb).portid);
 	chain = xt2_chain_lookup(table, name);
 	if (chain != NULL)
 		xt2_chain_free(chain);
 	else
 		ret = NFXTE_CHAIN_NOENT;
-	mutex_unlock(&table->lock);
-	mutex_unlock(&pnet->master_lock);
+	xtnetlink_table_wput(xa, sock_net(xtnl), table);
 	return xtnetlink_error(&ref, ret);
 }
 
@@ -422,9 +456,9 @@  xtnetlink_chain_move(struct sock *xtnl, struct sk_buff *iskb,
 		     const struct nlmsghdr *imsg,
 		     const struct nlattr *const *attr)
 {
-	struct xt2_pernet_data *pnet = xtables2_pernet(sock_net(xtnl));
 	struct xtnetlink_pktref ref =
 		{.c_skb = iskb, .c_msg = imsg, .sock = xtnl};
+	struct xtnetlink_transact *xa;
 	const char *old_name, *new_name;
 	const struct xt2_chain *chain;
 	struct xt2_table *table;
@@ -441,13 +475,11 @@  xtnetlink_chain_move(struct sock *xtnl, struct sk_buff *iskb,
 	if (*new_name == '\0')
 		return xtnetlink_error(&ref, NFXTE_CHAIN_INVALID_NAME);
 
-	mutex_lock(&pnet->master_lock);
-	table = pnet->master;
-	mutex_lock(&table->lock);
+	table = xtnetlink_table_wget(&xa, sock_net(xtnl),
+				     NETLINK_CB(iskb).portid);
 	chain = xt2_chain_move(table, old_name, new_name);
 	ret   = IS_ERR(chain) ? PTR_ERR(chain) : 0;
-	mutex_unlock(&table->lock);
-	mutex_unlock(&pnet->master_lock);
+	xtnetlink_table_wput(xa, sock_net(xtnl), table);
 	switch (ret) {
 	case -ENAMETOOLONG:
 		return xtnetlink_error(&ref, NFXTE_CHAIN_NAMETOOLONG);