[PATCH 7/7] netfilter: xtables2: redirect writes into transaction buffer

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



While a transaction is active, operations like chain add/del/move
should be done against the temporary table of course.

Signed-off-by: Jan Engelhardt <jengelh@xxxxxxxxxx>
---
 net/netfilter/xt2_nfnetlink.c |   77 +++++++++++++++++++++++++++++------------
 1 files changed, 55 insertions(+), 22 deletions(-)

diff --git a/net/netfilter/xt2_nfnetlink.c b/net/netfilter/xt2_nfnetlink.c
index 08bcc5e..3138f8c 100644
--- a/net/netfilter/xt2_nfnetlink.c
+++ b/net/netfilter/xt2_nfnetlink.c
@@ -206,6 +206,51 @@ xtnetlink_error(struct sock *xtnl, const struct xtnetlink_pktref *old,
 }
 
 /**
+ * @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 against
+ * modification by other instances. A symmetric call to xtnetlink_table_put is
+ * required afterwards.
+ */
+static struct xt2_table *
+xtnetlink_table_get(struct xtnetlink_transact **xa, struct net *net,
+		    int nladdr)
+{
+	struct xt2_pernet_data *pnet = xtables2_pernet(net);
+	struct xt2_table *table;
+
+	mutex_lock(&xtnetlink_transact_lock);
+	*xa = xtnetlink_transact_lookup(net, nladdr);
+	if (*xa == NULL) {
+		mutex_unlock(&xtnetlink_transact_lock);
+		mutex_lock(&pnet->master_lock);
+		table = pnet->master;
+	} else {
+		table = (*xa)->table;
+	}
+	mutex_lock(&table->lock);
+	return table;
+}
+
+static void
+xtnetlink_table_put(struct xtnetlink_transact *xa, struct net *net,
+		    struct xt2_table *table)
+{
+	struct xt2_pernet_data *pnet = xtables2_pernet(net);
+
+	mutex_unlock(&table->lock);
+	if (xa == NULL) {
+		mutex_unlock(&pnet->master_lock);
+		return;
+	}
+	mutex_unlock(&xtnetlink_transact_lock);
+}
+
+/**
  * Ran too often into NULL derefs. Now there is a dummy function for unused
  * message type 0.
  */
@@ -254,8 +299,8 @@ 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};
+	struct xtnetlink_transact *xa;
 	const struct nlattr *attr;
 	struct xt2_table *table;
 	struct xt2_chain *chain;
@@ -269,13 +314,8 @@ xtnetlink_chain_new(struct sock *xtnl, struct sk_buff *iskb,
 	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);
+
+	table = xtnetlink_table_get(&xa, sock_net(xtnl), NETLINK_CB(iskb).pid);
 	if (xt2_chain_lookup(table, name) != NULL) {
 		ret = xtnetlink_error(xtnl, &ref, NFXTE_CHAIN_EXISTS);
 	} else {
@@ -287,8 +327,7 @@ xtnetlink_chain_new(struct sock *xtnl, struct sk_buff *iskb,
 			ret = NFXTE_CHAIN_NAMETOOLONG;
 		ret = xtnetlink_error(xtnl, &ref, ret);
 	}
-	mutex_unlock(&table->lock);
-	mutex_unlock(&pnet->master_lock);
+	xtnetlink_table_put(xa, sock_net(xtnl), table);
 	return ret;
 }
 
@@ -300,8 +339,8 @@ 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};
+	struct xtnetlink_transact *xa;
 	const struct nlattr *name_attr;
 	struct xt2_table *table;
 	struct xt2_chain *chain;
@@ -315,17 +354,14 @@ xtnetlink_chain_del(struct sock *xtnl, struct sk_buff *iskb,
 	if (*name == '\0')
 		return xtnetlink_error(xtnl, &ref, NFXTE_CHAIN_NOENT);
 
-	mutex_lock(&pnet->master_lock);
-	table = pnet->master;
-	mutex_lock(&table->lock);
+	table = xtnetlink_table_get(&xa, sock_net(xtnl), NETLINK_CB(iskb).pid);
 	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);
+	xtnetlink_table_put(xa, sock_net(xtnl), table);
 	return ret;
 }
 
@@ -334,8 +370,8 @@ xtnetlink_chain_move(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};
+	struct xtnetlink_transact *xa;
 	const char *old_name, *new_name;
 	const struct xt2_chain *chain;
 	const struct nlattr *attr;
@@ -355,13 +391,10 @@ xtnetlink_chain_move(struct sock *xtnl, struct sk_buff *iskb,
 	if (*new_name == '\0')
 		return xtnetlink_error(xtnl, &ref, NFXTE_CHAIN_INVALID_NAME);
 
-	mutex_lock(&pnet->master_lock);
-	table = pnet->master;
-	mutex_lock(&table->lock);
+	table = xtnetlink_table_get(&xa, sock_net(xtnl), NETLINK_CB(iskb).pid);
 	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_put(xa, sock_net(xtnl), table);
 	switch (ret) {
 	case -ENAMETOOLONG:
 		return xtnetlink_error(xtnl, &ref, NFXTE_CHAIN_NAMETOOLONG);
-- 
1.7.7

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Netfitler Users]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux