netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] We all need more expectations
@ 2010-09-21  9:34 Pablo Neira Ayuso
  2010-09-21  9:34 ` [PATCH 1/4] netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers Pablo Neira Ayuso
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Pablo Neira Ayuso @ 2010-09-21  9:34 UTC (permalink / raw)
  To: netfilter-devel; +Cc: kaber

Hi Patrick,

The following patches are focuses on conntrack expectations. The first
one is an improvement for the situation in which the expectation table
is full for conntrack NAT helpers. Then, another quite simple to include
a missing attribute validation. To conclude, a couple of patches oriented
to support user-space conntrack helpers.

Hope that you like them.

---

Pablo Neira Ayuso (4):
      netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers
      netfilter: ctnetlink: missing validation of CTA_EXPECT_ZONE attribute
      netfilter: ctnetlink: allow to specify the expectation flags
      netfilter: ctnetlink: add support for user-space expectation helpers


 include/linux/netfilter/nf_conntrack_common.h |    5 ++
 include/linux/netfilter/nfnetlink_conntrack.h |    1 
 include/net/netfilter/nf_conntrack_expect.h   |    3 -
 net/ipv4/netfilter/nf_nat_amanda.c            |    9 ++++
 net/ipv4/netfilter/nf_nat_ftp.c               |    9 ++++
 net/ipv4/netfilter/nf_nat_h323.c              |   53 ++++++++++++++++++++++---
 net/ipv4/netfilter/nf_nat_irc.c               |    9 ++++
 net/ipv4/netfilter/nf_nat_sip.c               |   27 +++++++++++--
 net/netfilter/nf_conntrack_expect.c           |   40 ++++++++++++-------
 net/netfilter/nf_conntrack_netlink.c          |   38 ++++++++++++------
 10 files changed, 149 insertions(+), 45 deletions(-)

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/4] netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers
  2010-09-21  9:34 [PATCH 0/4] We all need more expectations Pablo Neira Ayuso
@ 2010-09-21  9:34 ` Pablo Neira Ayuso
  2010-09-21 15:07   ` Patrick McHardy
  2010-09-22  6:35   ` Patrick McHardy
  2010-09-21  9:34 ` [PATCH 2/4] netfilter: ctnetlink: missing validation of CTA_EXPECT_ZONE attribute Pablo Neira Ayuso
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 16+ messages in thread
From: Pablo Neira Ayuso @ 2010-09-21  9:34 UTC (permalink / raw)
  To: netfilter-devel; +Cc: kaber

This patch improves the situation in which the expectation table is
full for conntrack NAT helpers. Basically, we give up if we don't
find a place in the table instead of looping over nf_ct_expect_related()
with a different port (we should only do this if it returns -EBUSY, for
-EMFILE or -ESHUTDOWN I think that it's better to skip this).

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/ipv4/netfilter/nf_nat_amanda.c |    9 +++++-
 net/ipv4/netfilter/nf_nat_ftp.c    |    9 +++++-
 net/ipv4/netfilter/nf_nat_h323.c   |   53 +++++++++++++++++++++++++++++++-----
 net/ipv4/netfilter/nf_nat_irc.c    |    9 +++++-
 net/ipv4/netfilter/nf_nat_sip.c    |   27 ++++++++++++++++--
 5 files changed, 93 insertions(+), 14 deletions(-)

diff --git a/net/ipv4/netfilter/nf_nat_amanda.c b/net/ipv4/netfilter/nf_nat_amanda.c
index c31b876..0f23b3f 100644
--- a/net/ipv4/netfilter/nf_nat_amanda.c
+++ b/net/ipv4/netfilter/nf_nat_amanda.c
@@ -44,9 +44,16 @@ static unsigned int help(struct sk_buff *skb,
 
 	/* Try to get same port: if not, try to change it. */
 	for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
+		int ret;
+
 		exp->tuple.dst.u.tcp.port = htons(port);
-		if (nf_ct_expect_related(exp) == 0)
+		ret = nf_ct_expect_related(exp);
+		if (ret == 0)
+			break;
+		else if (ret != -EBUSY) {
+			port = 0;
 			break;
+		}
 	}
 
 	if (port == 0)
diff --git a/net/ipv4/netfilter/nf_nat_ftp.c b/net/ipv4/netfilter/nf_nat_ftp.c
index 86e0e84..dc73abb 100644
--- a/net/ipv4/netfilter/nf_nat_ftp.c
+++ b/net/ipv4/netfilter/nf_nat_ftp.c
@@ -79,9 +79,16 @@ static unsigned int nf_nat_ftp(struct sk_buff *skb,
 
 	/* Try to get same port: if not, try to change it. */
 	for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
+		int ret;
+
 		exp->tuple.dst.u.tcp.port = htons(port);
-		if (nf_ct_expect_related(exp) == 0)
+		ret = nf_ct_expect_related(exp);
+		if (ret == 0)
+			break;
+		else if (ret != -EBUSY) {
+			port = 0;
 			break;
+		}
 	}
 
 	if (port == 0)
diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
index 5045196..790f316 100644
--- a/net/ipv4/netfilter/nf_nat_h323.c
+++ b/net/ipv4/netfilter/nf_nat_h323.c
@@ -222,13 +222,24 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct,
 	/* Try to get a pair of ports. */
 	for (nated_port = ntohs(rtp_exp->tuple.dst.u.udp.port);
 	     nated_port != 0; nated_port += 2) {
+		int ret;
+
 		rtp_exp->tuple.dst.u.udp.port = htons(nated_port);
-		if (nf_ct_expect_related(rtp_exp) == 0) {
+		ret = nf_ct_expect_related(rtp_exp);
+		if (ret == 0) {
 			rtcp_exp->tuple.dst.u.udp.port =
 			    htons(nated_port + 1);
-			if (nf_ct_expect_related(rtcp_exp) == 0)
+			ret = nf_ct_expect_related(rtcp_exp);
+			if (ret == 0)
+				break;
+			else if (ret != -EBUSY) {
+				nf_ct_unexpect_related(rtp_exp);
+				nated_port = 0;
 				break;
-			nf_ct_unexpect_related(rtp_exp);
+			}
+		} else if (ret != -EBUSY) {
+			nated_port = 0;
+			break;
 		}
 	}
 
@@ -284,9 +295,16 @@ static int nat_t120(struct sk_buff *skb, struct nf_conn *ct,
 
 	/* Try to get same port: if not, try to change it. */
 	for (; nated_port != 0; nated_port++) {
+		int ret;
+
 		exp->tuple.dst.u.tcp.port = htons(nated_port);
-		if (nf_ct_expect_related(exp) == 0)
+		ret = nf_ct_expect_related(exp);
+		if (ret == 0)
+			break;
+		else if (ret != -EBUSY) {
+			nated_port = 0;
 			break;
+		}
 	}
 
 	if (nated_port == 0) {	/* No port available */
@@ -334,9 +352,16 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct,
 
 	/* Try to get same port: if not, try to change it. */
 	for (; nated_port != 0; nated_port++) {
+		int ret;
+
 		exp->tuple.dst.u.tcp.port = htons(nated_port);
-		if (nf_ct_expect_related(exp) == 0)
+		ret = nf_ct_expect_related(exp);
+		if (ret == 0)
 			break;
+		else if (ret != -EBUSY) {
+			nated_port = 0;
+			break;
+		}
 	}
 
 	if (nated_port == 0) {	/* No port available */
@@ -418,9 +443,16 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct,
 
 	/* Try to get same port: if not, try to change it. */
 	for (; nated_port != 0; nated_port++) {
+		int ret;
+
 		exp->tuple.dst.u.tcp.port = htons(nated_port);
-		if (nf_ct_expect_related(exp) == 0)
+		ret = nf_ct_expect_related(exp);
+		if (ret == 0)
+			break;
+		else if (ret != -EBUSY) {
+			nated_port = 0;
 			break;
+		}
 	}
 
 	if (nated_port == 0) {	/* No port available */
@@ -500,9 +532,16 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct,
 
 	/* Try to get same port: if not, try to change it. */
 	for (nated_port = ntohs(port); nated_port != 0; nated_port++) {
+		int ret;
+
 		exp->tuple.dst.u.tcp.port = htons(nated_port);
-		if (nf_ct_expect_related(exp) == 0)
+		ret = nf_ct_expect_related(exp);
+		if (ret == 0)
 			break;
+		else if (ret != -EBUSY) {
+			nated_port = 0;
+			break;
+		}
 	}
 
 	if (nated_port == 0) {	/* No port available */
diff --git a/net/ipv4/netfilter/nf_nat_irc.c b/net/ipv4/netfilter/nf_nat_irc.c
index ea83a88..535e1a8 100644
--- a/net/ipv4/netfilter/nf_nat_irc.c
+++ b/net/ipv4/netfilter/nf_nat_irc.c
@@ -45,9 +45,16 @@ static unsigned int help(struct sk_buff *skb,
 
 	/* Try to get same port: if not, try to change it. */
 	for (port = ntohs(exp->saved_proto.tcp.port); port != 0; port++) {
+		int ret;
+
 		exp->tuple.dst.u.tcp.port = htons(port);
-		if (nf_ct_expect_related(exp) == 0)
+		ret = nf_ct_expect_related(exp);
+		if (ret == 0)
+			break;
+		else if (ret != -EBUSY) {
+			port = 0;
 			break;
+		}
 	}
 
 	if (port == 0)
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c
index 11b538d..e40cf78 100644
--- a/net/ipv4/netfilter/nf_nat_sip.c
+++ b/net/ipv4/netfilter/nf_nat_sip.c
@@ -307,9 +307,16 @@ static unsigned int ip_nat_sip_expect(struct sk_buff *skb, unsigned int dataoff,
 	exp->expectfn = ip_nat_sip_expected;
 
 	for (; port != 0; port++) {
+		int ret;
+
 		exp->tuple.dst.u.udp.port = htons(port);
-		if (nf_ct_expect_related(exp) == 0)
+		ret = nf_ct_expect_related(exp);
+		if (ret == 0)
+			break;
+		else if (ret != -EBUSY) {
+			port = 0;
 			break;
+		}
 	}
 
 	if (port == 0)
@@ -480,13 +487,25 @@ static unsigned int ip_nat_sdp_media(struct sk_buff *skb, unsigned int dataoff,
 	/* Try to get same pair of ports: if not, try to change them. */
 	for (port = ntohs(rtp_exp->tuple.dst.u.udp.port);
 	     port != 0; port += 2) {
+		int ret;
+
 		rtp_exp->tuple.dst.u.udp.port = htons(port);
-		if (nf_ct_expect_related(rtp_exp) != 0)
+		ret = nf_ct_expect_related(rtp_exp);
+		if (ret == -EBUSY)
 			continue;
+		else if (ret < 0) {
+			port = 0;
+			break;
+		}
 		rtcp_exp->tuple.dst.u.udp.port = htons(port + 1);
-		if (nf_ct_expect_related(rtcp_exp) == 0)
+		ret = nf_ct_expect_related(rtcp_exp);
+		if (ret == 0)
 			break;
-		nf_ct_unexpect_related(rtp_exp);
+		else if (ret != -EBUSY) {
+			nf_ct_unexpect_related(rtp_exp);
+			port = 0;
+			break;
+		}
 	}
 
 	if (port == 0)


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/4] netfilter: ctnetlink: missing validation of CTA_EXPECT_ZONE attribute
  2010-09-21  9:34 [PATCH 0/4] We all need more expectations Pablo Neira Ayuso
  2010-09-21  9:34 ` [PATCH 1/4] netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers Pablo Neira Ayuso
@ 2010-09-21  9:34 ` Pablo Neira Ayuso
  2010-09-22  6:36   ` Patrick McHardy
  2010-09-21  9:35 ` [PATCH 3/4] netfilter: ctnetlink: allow to specify the expectation flags Pablo Neira Ayuso
  2010-09-21  9:35 ` [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers Pablo Neira Ayuso
  3 siblings, 1 reply; 16+ messages in thread
From: Pablo Neira Ayuso @ 2010-09-21  9:34 UTC (permalink / raw)
  To: netfilter-devel; +Cc: kaber

This patch adds the missing validation of the CTA_EXPECT_ZONE
attribute in the ctnetlink code.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/nf_conntrack_netlink.c |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 5bae1cd..37533a3 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1733,6 +1733,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
 	[CTA_EXPECT_TIMEOUT]	= { .type = NLA_U32 },
 	[CTA_EXPECT_ID]		= { .type = NLA_U32 },
 	[CTA_EXPECT_HELP_NAME]	= { .type = NLA_NUL_STRING },
+	[CTA_EXPECT_ZONE]	= { .type = NLA_U16 },
 };
 
 static int


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/4] netfilter: ctnetlink: allow to specify the expectation flags
  2010-09-21  9:34 [PATCH 0/4] We all need more expectations Pablo Neira Ayuso
  2010-09-21  9:34 ` [PATCH 1/4] netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers Pablo Neira Ayuso
  2010-09-21  9:34 ` [PATCH 2/4] netfilter: ctnetlink: missing validation of CTA_EXPECT_ZONE attribute Pablo Neira Ayuso
@ 2010-09-21  9:35 ` Pablo Neira Ayuso
  2010-09-21 15:18   ` Patrick McHardy
  2010-09-21  9:35 ` [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers Pablo Neira Ayuso
  3 siblings, 1 reply; 16+ messages in thread
From: Pablo Neira Ayuso @ 2010-09-21  9:35 UTC (permalink / raw)
  To: netfilter-devel; +Cc: kaber

With this patch, you can specify the expectation flags for user-space
created expectations.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/nf_conntrack_common.h |    4 ++++
 include/linux/netfilter/nfnetlink_conntrack.h |    1 +
 include/net/netfilter/nf_conntrack_expect.h   |    3 ---
 net/netfilter/nf_conntrack_netlink.c          |    7 ++++++-
 4 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
index 1afd18c..fdc50ca 100644
--- a/include/linux/netfilter/nf_conntrack_common.h
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -100,6 +100,10 @@ enum ip_conntrack_expect_events {
 	IPEXP_NEW,		/* new expectation */
 };
 
+/* expectation flags */
+#define NF_CT_EXPECT_PERMANENT		0x1
+#define NF_CT_EXPECT_INACTIVE		0x2
+
 #ifdef __KERNEL__
 struct ip_conntrack_stat {
 	unsigned int searched;
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h
index 9ed534c..455f0ce 100644
--- a/include/linux/netfilter/nfnetlink_conntrack.h
+++ b/include/linux/netfilter/nfnetlink_conntrack.h
@@ -161,6 +161,7 @@ enum ctattr_expect {
 	CTA_EXPECT_ID,
 	CTA_EXPECT_HELP_NAME,
 	CTA_EXPECT_ZONE,
+	CTA_EXPECT_FLAGS,
 	__CTA_EXPECT_MAX
 };
 #define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1)
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index 11e8150..96bb42a 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -67,9 +67,6 @@ struct nf_conntrack_expect_policy {
 
 #define NF_CT_EXPECT_CLASS_DEFAULT	0
 
-#define NF_CT_EXPECT_PERMANENT	0x1
-#define NF_CT_EXPECT_INACTIVE	0x2
-
 int nf_conntrack_expect_init(struct net *net);
 void nf_conntrack_expect_fini(struct net *net);
 
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 37533a3..82363f0 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1734,6 +1734,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
 	[CTA_EXPECT_ID]		= { .type = NLA_U32 },
 	[CTA_EXPECT_HELP_NAME]	= { .type = NLA_NUL_STRING },
 	[CTA_EXPECT_ZONE]	= { .type = NLA_U16 },
+	[CTA_EXPECT_FLAGS]	= { .type = NLA_U32 },
 };
 
 static int
@@ -1933,9 +1934,13 @@ ctnetlink_create_expect(struct net *net, u16 zone,
 		goto out;
 	}
 
+	if (cda[CTA_EXPECT_FLAGS])
+		exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS]));
+	else
+		exp->flags = 0;
+
 	exp->class = 0;
 	exp->expectfn = NULL;
-	exp->flags = 0;
 	exp->master = ct;
 	exp->helper = NULL;
 	memcpy(&exp->tuple, &tuple, sizeof(struct nf_conntrack_tuple));


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers
  2010-09-21  9:34 [PATCH 0/4] We all need more expectations Pablo Neira Ayuso
                   ` (2 preceding siblings ...)
  2010-09-21  9:35 ` [PATCH 3/4] netfilter: ctnetlink: allow to specify the expectation flags Pablo Neira Ayuso
@ 2010-09-21  9:35 ` Pablo Neira Ayuso
  2010-09-21 15:20   ` Patrick McHardy
  2010-09-28 19:08   ` Patrick McHardy
  3 siblings, 2 replies; 16+ messages in thread
From: Pablo Neira Ayuso @ 2010-09-21  9:35 UTC (permalink / raw)
  To: netfilter-devel; +Cc: kaber

This patch adds the basic infrastructure to support user-space
expectation helpers via ctnetlink and the netfilter queuing
infrastructure NFQUEUE. Basically, this patch:

* adds NF_CT_EXPECT_USERSPACE flag to identify user-space
created expectations. I have also added a sanity check in
__nf_ct_expect_check() to avoid that kernel-space helpers
may create an expectation if the master conntrack has no
helper assigned.
* adds some branches to check if the master conntrack helper
exists, otherwise we skip the code that refers to kernel-space
helper such as the local expectation list and the expectation
policy.
* allows to set the timeout for user-space expectations with
no helper assigned.

This patch also modifies ctnetlink to skip including the helper
name in the Netlink messages if no kernel-space helper is set
(since no user-space expectation has not kernel-space kernel
assigned).

You can access an example user-space FTP conntrack helper at:
http://people.netfilter.org/pablo/nf-ftp-helper-userspace-POC.tar.bz

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/nf_conntrack_common.h |    1 +
 net/netfilter/nf_conntrack_expect.c           |   40 ++++++++++++++++---------
 net/netfilter/nf_conntrack_netlink.c          |   38 ++++++++++++++----------
 3 files changed, 48 insertions(+), 31 deletions(-)

diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
index fdc50ca..23a1a08 100644
--- a/include/linux/netfilter/nf_conntrack_common.h
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -103,6 +103,7 @@ enum ip_conntrack_expect_events {
 /* expectation flags */
 #define NF_CT_EXPECT_PERMANENT		0x1
 #define NF_CT_EXPECT_INACTIVE		0x2
+#define NF_CT_EXPECT_USERSPACE		0x4
 
 #ifdef __KERNEL__
 struct ip_conntrack_stat {
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index acb29cc..b53e817 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -44,14 +44,15 @@ void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
 	struct nf_conn_help *master_help = nfct_help(exp->master);
 	struct net *net = nf_ct_exp_net(exp);
 
-	NF_CT_ASSERT(master_help);
 	NF_CT_ASSERT(!timer_pending(&exp->timeout));
 
 	hlist_del_rcu(&exp->hnode);
 	net->ct.expect_count--;
 
-	hlist_del(&exp->lnode);
-	master_help->expecting[exp->class]--;
+	if (master_help) {
+		hlist_del(&exp->lnode);
+		master_help->expecting[exp->class]--;
+	}
 	nf_ct_expect_put(exp);
 
 	NF_CT_STAT_INC(net, expect_delete);
@@ -320,16 +321,20 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
 
 	atomic_inc(&exp->use);
 
-	hlist_add_head(&exp->lnode, &master_help->expectations);
-	master_help->expecting[exp->class]++;
+	if (master_help) {
+		hlist_add_head(&exp->lnode, &master_help->expectations);
+		master_help->expecting[exp->class]++;
+	}
 
 	hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
 	net->ct.expect_count++;
 
 	setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
 		    (unsigned long)exp);
-	p = &master_help->helper->expect_policy[exp->class];
-	exp->timeout.expires = jiffies + p->timeout * HZ;
+	if (master_help) {
+		p = &master_help->helper->expect_policy[exp->class];
+		exp->timeout.expires = jiffies + p->timeout * HZ;
+	}
 	add_timer(&exp->timeout);
 
 	atomic_inc(&exp->use);
@@ -380,7 +385,9 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
 	unsigned int h;
 	int ret = 1;
 
-	if (!master_help->helper) {
+	/* Don't allow expectations created from kernel-space with no helper */
+	if (!(expect->flags & NF_CT_EXPECT_USERSPACE) &&
+	    (!master_help || (master_help && !master_help->helper))) {
 		ret = -ESHUTDOWN;
 		goto out;
 	}
@@ -398,13 +405,16 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
 		}
 	}
 	/* Will be over limit? */
-	p = &master_help->helper->expect_policy[expect->class];
-	if (p->max_expected &&
-	    master_help->expecting[expect->class] >= p->max_expected) {
-		evict_oldest_expect(master, expect);
-		if (master_help->expecting[expect->class] >= p->max_expected) {
-			ret = -EMFILE;
-			goto out;
+	if (master_help) {
+		p = &master_help->helper->expect_policy[expect->class];
+		if (p->max_expected &&
+		    master_help->expecting[expect->class] >= p->max_expected) {
+			evict_oldest_expect(master, expect);
+			if (master_help->expecting[expect->class]
+						>= p->max_expected) {
+				ret = -EMFILE;
+				goto out;
+			}
 		}
 	}
 
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 82363f0..cddf4b6 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1560,8 +1560,8 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
 			  const struct nf_conntrack_expect *exp)
 {
 	struct nf_conn *master = exp->master;
-	struct nf_conntrack_helper *helper;
 	long timeout = (exp->timeout.expires - jiffies) / HZ;
+	struct nf_conn_help *help;
 
 	if (timeout < 0)
 		timeout = 0;
@@ -1577,9 +1577,14 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
 
 	NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout));
 	NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp));
-	helper = rcu_dereference(nfct_help(master)->helper);
-	if (helper)
-		NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name);
+	help = nfct_help(master);
+	if (help) {
+		struct nf_conntrack_helper *helper;
+
+		helper = rcu_dereference(help->helper);
+		if (helper)
+			NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name);
+	}
 
 	return 0;
 
@@ -1920,24 +1925,25 @@ ctnetlink_create_expect(struct net *net, u16 zone,
 	if (!h)
 		return -ENOENT;
 	ct = nf_ct_tuplehash_to_ctrack(h);
-	help = nfct_help(ct);
-
-	if (!help || !help->helper) {
-		/* such conntrack hasn't got any helper, abort */
-		err = -EOPNOTSUPP;
-		goto out;
-	}
-
 	exp = nf_ct_expect_alloc(ct);
 	if (!exp) {
 		err = -ENOMEM;
 		goto out;
 	}
-
-	if (cda[CTA_EXPECT_FLAGS])
+	help = nfct_help(ct);
+	if (!help) {
+		if (!cda[CTA_EXPECT_TIMEOUT]) {
+			err = -EINVAL;
+			goto out;
+		}
+		exp->timeout.expires =
+		  jiffies + ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ;
+	}
+	if (cda[CTA_EXPECT_FLAGS]) {
 		exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS]));
-	else
-		exp->flags = 0;
+		exp->flags |= NF_CT_EXPECT_USERSPACE;
+	} else
+		exp->flags = NF_CT_EXPECT_USERSPACE;
 
 	exp->class = 0;
 	exp->expectfn = NULL;


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 1/4] netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers
  2010-09-21  9:34 ` [PATCH 1/4] netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers Pablo Neira Ayuso
@ 2010-09-21 15:07   ` Patrick McHardy
  2010-09-22  6:35   ` Patrick McHardy
  1 sibling, 0 replies; 16+ messages in thread
From: Patrick McHardy @ 2010-09-21 15:07 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Am 21.09.2010 11:34, schrieb Pablo Neira Ayuso:
> This patch improves the situation in which the expectation table is
> full for conntrack NAT helpers. Basically, we give up if we don't
> find a place in the table instead of looping over nf_ct_expect_related()
> with a different port (we should only do this if it returns -EBUSY, for
> -EMFILE or -ESHUTDOWN I think that it's better to skip this).

Indeed, this makes a lot more sense than our current behaviour.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/4] netfilter: ctnetlink: allow to specify the expectation flags
  2010-09-21  9:35 ` [PATCH 3/4] netfilter: ctnetlink: allow to specify the expectation flags Pablo Neira Ayuso
@ 2010-09-21 15:18   ` Patrick McHardy
  2010-09-21 22:38     ` Pablo Neira Ayuso
  0 siblings, 1 reply; 16+ messages in thread
From: Patrick McHardy @ 2010-09-21 15:18 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Am 21.09.2010 11:35, schrieb Pablo Neira Ayuso:
> With this patch, you can specify the expectation flags for user-space
> created expectations.

We don't seem to be dumping the flags to userspace. I think this
should be added for consistency.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers
  2010-09-21  9:35 ` [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers Pablo Neira Ayuso
@ 2010-09-21 15:20   ` Patrick McHardy
  2010-09-21 22:38     ` Pablo Neira Ayuso
  2010-09-28 19:08   ` Patrick McHardy
  1 sibling, 1 reply; 16+ messages in thread
From: Patrick McHardy @ 2010-09-21 15:20 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Am 21.09.2010 11:35, schrieb Pablo Neira Ayuso:
> This patch adds the basic infrastructure to support user-space
> expectation helpers via ctnetlink and the netfilter queuing
> infrastructure NFQUEUE. Basically, this patch:
> 
> * adds NF_CT_EXPECT_USERSPACE flag to identify user-space
> created expectations. I have also added a sanity check in
> __nf_ct_expect_check() to avoid that kernel-space helpers
> may create an expectation if the master conntrack has no
> helper assigned.
> * adds some branches to check if the master conntrack helper
> exists, otherwise we skip the code that refers to kernel-space
> helper such as the local expectation list and the expectation
> policy.
> * allows to set the timeout for user-space expectations with
> no helper assigned.
> 
> This patch also modifies ctnetlink to skip including the helper
> name in the Netlink messages if no kernel-space helper is set
> (since no user-space expectation has not kernel-space kernel
> assigned).

My main question is - what will be cleaning up these expectations
on module unload? Currently expectations are cleaned up on unload
of the corresponding helper module, which obviously doesn't
happen in this case.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/4] netfilter: ctnetlink: allow to specify the expectation flags
  2010-09-21 15:18   ` Patrick McHardy
@ 2010-09-21 22:38     ` Pablo Neira Ayuso
  2010-09-22  6:37       ` Patrick McHardy
  0 siblings, 1 reply; 16+ messages in thread
From: Pablo Neira Ayuso @ 2010-09-21 22:38 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netfilter-devel

[-- Attachment #1: Type: text/plain, Size: 366 bytes --]

On 21/09/10 17:18, Patrick McHardy wrote:
> Am 21.09.2010 11:35, schrieb Pablo Neira Ayuso:
>> With this patch, you can specify the expectation flags for user-space
>> created expectations.
> 
> We don't seem to be dumping the flags to userspace. I think this
> should be added for consistency.

I have included this in a new version of the patch, find it attached.

[-- Attachment #2: exp-flags.patch --]
[-- Type: text/x-patch, Size: 3198 bytes --]

netfilter: ctnetlink: allow to specify the expectation flags

With this patch, you can specify the expectation flags for user-space
created expectations.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/nf_conntrack_common.h |    4 ++++
 include/linux/netfilter/nfnetlink_conntrack.h |    1 +
 include/net/netfilter/nf_conntrack_expect.h   |    3 ---
 net/netfilter/nf_conntrack_netlink.c          |    8 +++++++-
 4 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
index 1afd18c..fdc50ca 100644
--- a/include/linux/netfilter/nf_conntrack_common.h
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -100,6 +100,10 @@ enum ip_conntrack_expect_events {
 	IPEXP_NEW,		/* new expectation */
 };
 
+/* expectation flags */
+#define NF_CT_EXPECT_PERMANENT		0x1
+#define NF_CT_EXPECT_INACTIVE		0x2
+
 #ifdef __KERNEL__
 struct ip_conntrack_stat {
 	unsigned int searched;
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h
index 9ed534c..455f0ce 100644
--- a/include/linux/netfilter/nfnetlink_conntrack.h
+++ b/include/linux/netfilter/nfnetlink_conntrack.h
@@ -161,6 +161,7 @@ enum ctattr_expect {
 	CTA_EXPECT_ID,
 	CTA_EXPECT_HELP_NAME,
 	CTA_EXPECT_ZONE,
+	CTA_EXPECT_FLAGS,
 	__CTA_EXPECT_MAX
 };
 #define CTA_EXPECT_MAX (__CTA_EXPECT_MAX - 1)
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index 11e8150..96bb42a 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -67,9 +67,6 @@ struct nf_conntrack_expect_policy {
 
 #define NF_CT_EXPECT_CLASS_DEFAULT	0
 
-#define NF_CT_EXPECT_PERMANENT	0x1
-#define NF_CT_EXPECT_INACTIVE	0x2
-
 int nf_conntrack_expect_init(struct net *net);
 void nf_conntrack_expect_fini(struct net *net);
 
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 37533a3..0804e0e 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1577,6 +1577,7 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
 
 	NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout));
 	NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp));
+	NLA_PUT_BE32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags));
 	helper = rcu_dereference(nfct_help(master)->helper);
 	if (helper)
 		NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name);
@@ -1734,6 +1735,7 @@ static const struct nla_policy exp_nla_policy[CTA_EXPECT_MAX+1] = {
 	[CTA_EXPECT_ID]		= { .type = NLA_U32 },
 	[CTA_EXPECT_HELP_NAME]	= { .type = NLA_NUL_STRING },
 	[CTA_EXPECT_ZONE]	= { .type = NLA_U16 },
+	[CTA_EXPECT_FLAGS]	= { .type = NLA_U32 },
 };
 
 static int
@@ -1933,9 +1935,13 @@ ctnetlink_create_expect(struct net *net, u16 zone,
 		goto out;
 	}
 
+	if (cda[CTA_EXPECT_FLAGS])
+		exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS]));
+	else
+		exp->flags = 0;
+
 	exp->class = 0;
 	exp->expectfn = NULL;
-	exp->flags = 0;
 	exp->master = ct;
 	exp->helper = NULL;
 	memcpy(&exp->tuple, &tuple, sizeof(struct nf_conntrack_tuple));

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers
  2010-09-21 15:20   ` Patrick McHardy
@ 2010-09-21 22:38     ` Pablo Neira Ayuso
  2010-09-22  6:45       ` Patrick McHardy
  0 siblings, 1 reply; 16+ messages in thread
From: Pablo Neira Ayuso @ 2010-09-21 22:38 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netfilter-devel

[-- Attachment #1: Type: text/plain, Size: 1430 bytes --]

On 21/09/10 17:20, Patrick McHardy wrote:
> Am 21.09.2010 11:35, schrieb Pablo Neira Ayuso:
>> This patch adds the basic infrastructure to support user-space
>> expectation helpers via ctnetlink and the netfilter queuing
>> infrastructure NFQUEUE. Basically, this patch:
>>
>> * adds NF_CT_EXPECT_USERSPACE flag to identify user-space
>> created expectations. I have also added a sanity check in
>> __nf_ct_expect_check() to avoid that kernel-space helpers
>> may create an expectation if the master conntrack has no
>> helper assigned.
>> * adds some branches to check if the master conntrack helper
>> exists, otherwise we skip the code that refers to kernel-space
>> helper such as the local expectation list and the expectation
>> policy.
>> * allows to set the timeout for user-space expectations with
>> no helper assigned.
>>
>> This patch also modifies ctnetlink to skip including the helper
>> name in the Netlink messages if no kernel-space helper is set
>> (since no user-space expectation has not kernel-space kernel
>> assigned).
> 
> My main question is - what will be cleaning up these expectations
> on module unload? Currently expectations are cleaned up on unload
> of the corresponding helper module, which obviously doesn't
> happen in this case.

Indeed. I have reworked the patch to add the nf_ct_userspace_expect_list
that is used to delete all the user-space created expectations if
ctnetlink is unloaded.

[-- Attachment #2: exp.patch --]
[-- Type: text/x-patch, Size: 8738 bytes --]

netfilter: ctnetlink: add support for user-space expectation helpers

This patch adds the basic infrastructure to support user-space
expectation helpers via ctnetlink and the netfilter queuing
infrastructure NFQUEUE. Basically, this patch:

* adds NF_CT_EXPECT_USERSPACE flag to identify user-space
created expectations. I have also added a sanity check in
__nf_ct_expect_check() to avoid that kernel-space helpers
may create an expectation if the master conntrack has no
helper assigned.
* adds some branches to check if the master conntrack helper
exists, otherwise we skip the code that refers to kernel-space
helper such as the local expectation list and the expectation
policy.
* allows to set the timeout for user-space expectations with
no helper assigned.
* a list of expectations created from user-space that depends
on ctnetlink (if this module is removed, they are deleted).

This patch also modifies ctnetlink to skip including the helper
name in the Netlink messages if no kernel-space helper is set
(since no user-space expectation has not kernel-space kernel
assigned).

You can access an example user-space FTP conntrack helper at:
http://people.netfilter.org/pablo/nf-ftp-helper-userspace-POC.tar.bz

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/nf_conntrack_common.h |    1 
 include/net/netfilter/nf_conntrack_expect.h   |    1 
 net/netfilter/nf_conntrack_expect.c           |   61 +++++++++++++++++++------
 net/netfilter/nf_conntrack_netlink.c          |   39 +++++++++-------
 4 files changed, 72 insertions(+), 30 deletions(-)

diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
index fdc50ca..23a1a08 100644
--- a/include/linux/netfilter/nf_conntrack_common.h
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -103,6 +103,7 @@ enum ip_conntrack_expect_events {
 /* expectation flags */
 #define NF_CT_EXPECT_PERMANENT		0x1
 #define NF_CT_EXPECT_INACTIVE		0x2
+#define NF_CT_EXPECT_USERSPACE		0x4
 
 #ifdef __KERNEL__
 struct ip_conntrack_stat {
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index 96bb42a..416b838 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -85,6 +85,7 @@ nf_ct_find_expectation(struct net *net, u16 zone,
 void nf_ct_unlink_expect(struct nf_conntrack_expect *exp);
 void nf_ct_remove_expectations(struct nf_conn *ct);
 void nf_ct_unexpect_related(struct nf_conntrack_expect *exp);
+void nf_ct_remove_userspace_expectations(void);
 
 /* Allocate space for an expectation: this is mandatory before calling
    nf_ct_expect_related.  You will have to call put afterwards. */
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index acb29cc..361a8ba 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -38,20 +38,26 @@ static int nf_ct_expect_hash_rnd_initted __read_mostly;
 
 static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
 
+static HLIST_HEAD(nf_ct_userspace_expect_list);
+static int nf_ct_userspace_expect_list_counter;
+
 /* nf_conntrack_expect helper functions */
 void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
 {
 	struct nf_conn_help *master_help = nfct_help(exp->master);
 	struct net *net = nf_ct_exp_net(exp);
 
-	NF_CT_ASSERT(master_help);
 	NF_CT_ASSERT(!timer_pending(&exp->timeout));
 
 	hlist_del_rcu(&exp->hnode);
 	net->ct.expect_count--;
 
 	hlist_del(&exp->lnode);
-	master_help->expecting[exp->class]--;
+	if (exp->flags & NF_CT_EXPECT_USERSPACE)
+		nf_ct_userspace_expect_list_counter--;
+	else
+		master_help->expecting[exp->class]--;
+
 	nf_ct_expect_put(exp);
 
 	NF_CT_STAT_INC(net, expect_delete);
@@ -320,16 +326,23 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
 
 	atomic_inc(&exp->use);
 
-	hlist_add_head(&exp->lnode, &master_help->expectations);
-	master_help->expecting[exp->class]++;
+	if (master_help) {
+		hlist_add_head(&exp->lnode, &master_help->expectations);
+		master_help->expecting[exp->class]++;
+	} else if (exp->flags & NF_CT_EXPECT_USERSPACE) {
+		hlist_add_head(&exp->lnode, &nf_ct_userspace_expect_list);
+		nf_ct_userspace_expect_list_counter++;
+	}
 
 	hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
 	net->ct.expect_count++;
 
 	setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
 		    (unsigned long)exp);
-	p = &master_help->helper->expect_policy[exp->class];
-	exp->timeout.expires = jiffies + p->timeout * HZ;
+	if (master_help) {
+		p = &master_help->helper->expect_policy[exp->class];
+		exp->timeout.expires = jiffies + p->timeout * HZ;
+	}
 	add_timer(&exp->timeout);
 
 	atomic_inc(&exp->use);
@@ -380,7 +393,9 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
 	unsigned int h;
 	int ret = 1;
 
-	if (!master_help->helper) {
+	/* Don't allow expectations created from kernel-space with no helper */
+	if (!(expect->flags & NF_CT_EXPECT_USERSPACE) &&
+	    (!master_help || (master_help && !master_help->helper))) {
 		ret = -ESHUTDOWN;
 		goto out;
 	}
@@ -398,13 +413,16 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
 		}
 	}
 	/* Will be over limit? */
-	p = &master_help->helper->expect_policy[expect->class];
-	if (p->max_expected &&
-	    master_help->expecting[expect->class] >= p->max_expected) {
-		evict_oldest_expect(master, expect);
-		if (master_help->expecting[expect->class] >= p->max_expected) {
-			ret = -EMFILE;
-			goto out;
+	if (master_help) {
+		p = &master_help->helper->expect_policy[expect->class];
+		if (p->max_expected &&
+		    master_help->expecting[expect->class] >= p->max_expected) {
+			evict_oldest_expect(master, expect);
+			if (master_help->expecting[expect->class]
+						>= p->max_expected) {
+				ret = -EMFILE;
+				goto out;
+			}
 		}
 	}
 
@@ -439,6 +457,21 @@ out:
 }
 EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
 
+void nf_ct_remove_userspace_expectations(void)
+{
+	struct nf_conntrack_expect *exp;
+	struct hlist_node *n, *next;
+
+	hlist_for_each_entry_safe(exp, n, next,
+				  &nf_ct_userspace_expect_list, lnode) {
+		if (del_timer(&exp->timeout)) {
+			nf_ct_unlink_expect(exp);
+			nf_ct_expect_put(exp);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(nf_ct_remove_userspace_expectations);
+
 #ifdef CONFIG_PROC_FS
 struct ct_expect_iter_state {
 	struct seq_net_private p;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 0804e0e..83b11e3 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1560,8 +1560,8 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
 			  const struct nf_conntrack_expect *exp)
 {
 	struct nf_conn *master = exp->master;
-	struct nf_conntrack_helper *helper;
 	long timeout = (exp->timeout.expires - jiffies) / HZ;
+	struct nf_conn_help *help;
 
 	if (timeout < 0)
 		timeout = 0;
@@ -1578,9 +1578,14 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
 	NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout));
 	NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp));
 	NLA_PUT_BE32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags));
-	helper = rcu_dereference(nfct_help(master)->helper);
-	if (helper)
-		NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name);
+	help = nfct_help(master);
+	if (help) {
+		struct nf_conntrack_helper *helper;
+
+		helper = rcu_dereference(help->helper);
+		if (helper)
+			NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name);
+	}
 
 	return 0;
 
@@ -1921,24 +1926,25 @@ ctnetlink_create_expect(struct net *net, u16 zone,
 	if (!h)
 		return -ENOENT;
 	ct = nf_ct_tuplehash_to_ctrack(h);
-	help = nfct_help(ct);
-
-	if (!help || !help->helper) {
-		/* such conntrack hasn't got any helper, abort */
-		err = -EOPNOTSUPP;
-		goto out;
-	}
-
 	exp = nf_ct_expect_alloc(ct);
 	if (!exp) {
 		err = -ENOMEM;
 		goto out;
 	}
-
-	if (cda[CTA_EXPECT_FLAGS])
+	help = nfct_help(ct);
+	if (!help) {
+		if (!cda[CTA_EXPECT_TIMEOUT]) {
+			err = -EINVAL;
+			goto out;
+		}
+		exp->timeout.expires =
+		  jiffies + ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ;
+	}
+	if (cda[CTA_EXPECT_FLAGS]) {
 		exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS]));
-	else
-		exp->flags = 0;
+		exp->flags |= NF_CT_EXPECT_USERSPACE;
+	} else
+		exp->flags = NF_CT_EXPECT_USERSPACE;
 
 	exp->class = 0;
 	exp->expectfn = NULL;
@@ -2109,6 +2115,7 @@ static void __exit ctnetlink_exit(void)
 {
 	pr_info("ctnetlink: unregistering from nfnetlink.\n");
 
+	nf_ct_remove_userspace_expectations();
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
 	nf_ct_expect_unregister_notifier(&ctnl_notifier_exp);
 	nf_conntrack_unregister_notifier(&ctnl_notifier);

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 1/4] netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers
  2010-09-21  9:34 ` [PATCH 1/4] netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers Pablo Neira Ayuso
  2010-09-21 15:07   ` Patrick McHardy
@ 2010-09-22  6:35   ` Patrick McHardy
  1 sibling, 0 replies; 16+ messages in thread
From: Patrick McHardy @ 2010-09-22  6:35 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Am 21.09.2010 11:34, schrieb Pablo Neira Ayuso:
> This patch improves the situation in which the expectation table is
> full for conntrack NAT helpers. Basically, we give up if we don't
> find a place in the table instead of looping over nf_ct_expect_related()
> with a different port (we should only do this if it returns -EBUSY, for
> -EMFILE or -ESHUTDOWN I think that it's better to skip this).

Applied, thanks Pablo.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/4] netfilter: ctnetlink: missing validation of CTA_EXPECT_ZONE attribute
  2010-09-21  9:34 ` [PATCH 2/4] netfilter: ctnetlink: missing validation of CTA_EXPECT_ZONE attribute Pablo Neira Ayuso
@ 2010-09-22  6:36   ` Patrick McHardy
  0 siblings, 0 replies; 16+ messages in thread
From: Patrick McHardy @ 2010-09-22  6:36 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Am 21.09.2010 11:34, schrieb Pablo Neira Ayuso:
> This patch adds the missing validation of the CTA_EXPECT_ZONE
> attribute in the ctnetlink code.

Applied, thanks.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 3/4] netfilter: ctnetlink: allow to specify the expectation flags
  2010-09-21 22:38     ` Pablo Neira Ayuso
@ 2010-09-22  6:37       ` Patrick McHardy
  0 siblings, 0 replies; 16+ messages in thread
From: Patrick McHardy @ 2010-09-22  6:37 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Am 22.09.2010 00:38, schrieb Pablo Neira Ayuso:
> On 21/09/10 17:18, Patrick McHardy wrote:
>> Am 21.09.2010 11:35, schrieb Pablo Neira Ayuso:
>>> With this patch, you can specify the expectation flags for user-space
>>> created expectations.
>>
>> We don't seem to be dumping the flags to userspace. I think this
>> should be added for consistency.
> 
> I have included this in a new version of the patch, find it attached.

Applied, thanks.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers
  2010-09-21 22:38     ` Pablo Neira Ayuso
@ 2010-09-22  6:45       ` Patrick McHardy
  2010-09-22 11:07         ` Pablo Neira Ayuso
  0 siblings, 1 reply; 16+ messages in thread
From: Patrick McHardy @ 2010-09-22  6:45 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Am 22.09.2010 00:38, schrieb Pablo Neira Ayuso:
>>
>> My main question is - what will be cleaning up these expectations
>> on module unload? Currently expectations are cleaned up on unload
>> of the corresponding helper module, which obviously doesn't
>> happen in this case.
> 
> Indeed. I have reworked the patch to add the nf_ct_userspace_expect_list
> that is used to delete all the user-space created expectations if
> ctnetlink is unloaded.

> diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
> index fdc50ca..23a1a08 100644
> --- a/include/linux/netfilter/nf_conntrack_common.h
> +++ b/include/linux/netfilter/nf_conntrack_common.h
> @@ -103,6 +103,7 @@ enum ip_conntrack_expect_events {
>  /* expectation flags */
>  #define NF_CT_EXPECT_PERMANENT		0x1
>  #define NF_CT_EXPECT_INACTIVE		0x2
> +#define NF_CT_EXPECT_USERSPACE		0x4

Does this flag need to be exposed to userspace? I also don't
see anything preventing userspace incorrectly setting it on
an expectation that actually does have a master, which will
probably cause problems later on.

> diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
> index acb29cc..361a8ba 100644
> --- a/net/netfilter/nf_conntrack_expect.c
> +++ b/net/netfilter/nf_conntrack_expect.c
> @@ -38,20 +38,26 @@ static int nf_ct_expect_hash_rnd_initted __read_mostly;
>  
>  static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
>  
> +static HLIST_HEAD(nf_ct_userspace_expect_list);
> +static int nf_ct_userspace_expect_list_counter;

This counter is write-only.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers
  2010-09-22  6:45       ` Patrick McHardy
@ 2010-09-22 11:07         ` Pablo Neira Ayuso
  0 siblings, 0 replies; 16+ messages in thread
From: Pablo Neira Ayuso @ 2010-09-22 11:07 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netfilter-devel

[-- Attachment #1: Type: text/plain, Size: 1510 bytes --]

Hi Patrick,

On 22/09/10 08:45, Patrick McHardy wrote:
>> diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
>> index fdc50ca..23a1a08 100644
>> --- a/include/linux/netfilter/nf_conntrack_common.h
>> +++ b/include/linux/netfilter/nf_conntrack_common.h
>> @@ -103,6 +103,7 @@ enum ip_conntrack_expect_events {
>>  /* expectation flags */
>>  #define NF_CT_EXPECT_PERMANENT		0x1
>>  #define NF_CT_EXPECT_INACTIVE		0x2
>> +#define NF_CT_EXPECT_USERSPACE		0x4
> 
> Does this flag need to be exposed to userspace? I also don't
> see anything preventing userspace incorrectly setting it on
> an expectation that actually does have a master, which will
> probably cause problems later on.

I have reworked the patch again: the flag is exposed to user-space but
you cannot set/unset it.

>> diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
>> index acb29cc..361a8ba 100644
>> --- a/net/netfilter/nf_conntrack_expect.c
>> +++ b/net/netfilter/nf_conntrack_expect.c
>> @@ -38,20 +38,26 @@ static int nf_ct_expect_hash_rnd_initted __read_mostly;
>>  
>>  static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
>>  
>> +static HLIST_HEAD(nf_ct_userspace_expect_list);
>> +static int nf_ct_userspace_expect_list_counter;
> 
> This counter is write-only.

Removed this counter!

I have also modified the /proc output to display USERSPACE for
expectation whose user-space flag is set (this was missing in the
previous patch).

[-- Attachment #2: exp.patch --]
[-- Type: text/x-patch, Size: 9346 bytes --]

netfilter: ctnetlink: add support for user-space expectation helpers

This patch adds the basic infrastructure to support user-space
expectation helpers via ctnetlink and the netfilter queuing
infrastructure NFQUEUE. Basically, this patch:

* adds NF_CT_EXPECT_USERSPACE flag to identify user-space
created expectations. I have also added a sanity check in
__nf_ct_expect_check() to avoid that kernel-space helpers
may create an expectation if the master conntrack has no
helper assigned.
* adds some branches to check if the master conntrack helper
exists, otherwise we skip the code that refers to kernel-space
helper such as the local expectation list and the expectation
policy.
* allows to set the timeout for user-space expectations with
no helper assigned.
* a list of expectations created from user-space that depends
on ctnetlink (if this module is removed, they are deleted).
* includes USERSPACE in the /proc output for expectations
that have been created by a user-space helper.

This patch also modifies ctnetlink to skip including the helper
name in the Netlink messages if no kernel-space helper is set
(since no user-space expectation has not kernel-space kernel
assigned).

You can access an example user-space FTP conntrack helper at:
http://people.netfilter.org/pablo/nf-ftp-helper-userspace-POC.tar.bz

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netfilter/nf_conntrack_common.h |    1 
 include/net/netfilter/nf_conntrack_expect.h   |    1 
 net/netfilter/nf_conntrack_expect.c           |   62 +++++++++++++++++++------
 net/netfilter/nf_conntrack_netlink.c          |   46 ++++++++++++-------
 4 files changed, 79 insertions(+), 31 deletions(-)

diff --git a/include/linux/netfilter/nf_conntrack_common.h b/include/linux/netfilter/nf_conntrack_common.h
index fdc50ca..23a1a08 100644
--- a/include/linux/netfilter/nf_conntrack_common.h
+++ b/include/linux/netfilter/nf_conntrack_common.h
@@ -103,6 +103,7 @@ enum ip_conntrack_expect_events {
 /* expectation flags */
 #define NF_CT_EXPECT_PERMANENT		0x1
 #define NF_CT_EXPECT_INACTIVE		0x2
+#define NF_CT_EXPECT_USERSPACE		0x4
 
 #ifdef __KERNEL__
 struct ip_conntrack_stat {
diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h
index 96bb42a..416b838 100644
--- a/include/net/netfilter/nf_conntrack_expect.h
+++ b/include/net/netfilter/nf_conntrack_expect.h
@@ -85,6 +85,7 @@ nf_ct_find_expectation(struct net *net, u16 zone,
 void nf_ct_unlink_expect(struct nf_conntrack_expect *exp);
 void nf_ct_remove_expectations(struct nf_conn *ct);
 void nf_ct_unexpect_related(struct nf_conntrack_expect *exp);
+void nf_ct_remove_userspace_expectations(void);
 
 /* Allocate space for an expectation: this is mandatory before calling
    nf_ct_expect_related.  You will have to call put afterwards. */
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index acb29cc..b30a1f2 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -38,20 +38,23 @@ static int nf_ct_expect_hash_rnd_initted __read_mostly;
 
 static struct kmem_cache *nf_ct_expect_cachep __read_mostly;
 
+static HLIST_HEAD(nf_ct_userspace_expect_list);
+
 /* nf_conntrack_expect helper functions */
 void nf_ct_unlink_expect(struct nf_conntrack_expect *exp)
 {
 	struct nf_conn_help *master_help = nfct_help(exp->master);
 	struct net *net = nf_ct_exp_net(exp);
 
-	NF_CT_ASSERT(master_help);
 	NF_CT_ASSERT(!timer_pending(&exp->timeout));
 
 	hlist_del_rcu(&exp->hnode);
 	net->ct.expect_count--;
 
 	hlist_del(&exp->lnode);
-	master_help->expecting[exp->class]--;
+	if (!(exp->flags & NF_CT_EXPECT_USERSPACE))
+		master_help->expecting[exp->class]--;
+
 	nf_ct_expect_put(exp);
 
 	NF_CT_STAT_INC(net, expect_delete);
@@ -320,16 +323,21 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp)
 
 	atomic_inc(&exp->use);
 
-	hlist_add_head(&exp->lnode, &master_help->expectations);
-	master_help->expecting[exp->class]++;
+	if (master_help) {
+		hlist_add_head(&exp->lnode, &master_help->expectations);
+		master_help->expecting[exp->class]++;
+	} else if (exp->flags & NF_CT_EXPECT_USERSPACE)
+		hlist_add_head(&exp->lnode, &nf_ct_userspace_expect_list);
 
 	hlist_add_head_rcu(&exp->hnode, &net->ct.expect_hash[h]);
 	net->ct.expect_count++;
 
 	setup_timer(&exp->timeout, nf_ct_expectation_timed_out,
 		    (unsigned long)exp);
-	p = &master_help->helper->expect_policy[exp->class];
-	exp->timeout.expires = jiffies + p->timeout * HZ;
+	if (master_help) {
+		p = &master_help->helper->expect_policy[exp->class];
+		exp->timeout.expires = jiffies + p->timeout * HZ;
+	}
 	add_timer(&exp->timeout);
 
 	atomic_inc(&exp->use);
@@ -380,7 +388,9 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
 	unsigned int h;
 	int ret = 1;
 
-	if (!master_help->helper) {
+	/* Don't allow expectations created from kernel-space with no helper */
+	if (!(expect->flags & NF_CT_EXPECT_USERSPACE) &&
+	    (!master_help || (master_help && !master_help->helper))) {
 		ret = -ESHUTDOWN;
 		goto out;
 	}
@@ -398,13 +408,16 @@ static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
 		}
 	}
 	/* Will be over limit? */
-	p = &master_help->helper->expect_policy[expect->class];
-	if (p->max_expected &&
-	    master_help->expecting[expect->class] >= p->max_expected) {
-		evict_oldest_expect(master, expect);
-		if (master_help->expecting[expect->class] >= p->max_expected) {
-			ret = -EMFILE;
-			goto out;
+	if (master_help) {
+		p = &master_help->helper->expect_policy[expect->class];
+		if (p->max_expected &&
+		    master_help->expecting[expect->class] >= p->max_expected) {
+			evict_oldest_expect(master, expect);
+			if (master_help->expecting[expect->class]
+						>= p->max_expected) {
+				ret = -EMFILE;
+				goto out;
+			}
 		}
 	}
 
@@ -439,6 +452,21 @@ out:
 }
 EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
 
+void nf_ct_remove_userspace_expectations(void)
+{
+	struct nf_conntrack_expect *exp;
+	struct hlist_node *n, *next;
+
+	hlist_for_each_entry_safe(exp, n, next,
+				  &nf_ct_userspace_expect_list, lnode) {
+		if (del_timer(&exp->timeout)) {
+			nf_ct_unlink_expect(exp);
+			nf_ct_expect_put(exp);
+		}
+	}
+}
+EXPORT_SYMBOL_GPL(nf_ct_remove_userspace_expectations);
+
 #ifdef CONFIG_PROC_FS
 struct ct_expect_iter_state {
 	struct seq_net_private p;
@@ -529,8 +557,12 @@ static int exp_seq_show(struct seq_file *s, void *v)
 		seq_printf(s, "PERMANENT");
 		delim = ",";
 	}
-	if (expect->flags & NF_CT_EXPECT_INACTIVE)
+	if (expect->flags & NF_CT_EXPECT_INACTIVE) {
 		seq_printf(s, "%sINACTIVE", delim);
+		delim = ",";
+	}
+	if (expect->flags & NF_CT_EXPECT_USERSPACE)
+		seq_printf(s, "%sUSERSPACE", delim);
 
 	helper = rcu_dereference(nfct_help(expect->master)->helper);
 	if (helper) {
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 0804e0e..b4077be 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1560,8 +1560,8 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
 			  const struct nf_conntrack_expect *exp)
 {
 	struct nf_conn *master = exp->master;
-	struct nf_conntrack_helper *helper;
 	long timeout = (exp->timeout.expires - jiffies) / HZ;
+	struct nf_conn_help *help;
 
 	if (timeout < 0)
 		timeout = 0;
@@ -1578,9 +1578,14 @@ ctnetlink_exp_dump_expect(struct sk_buff *skb,
 	NLA_PUT_BE32(skb, CTA_EXPECT_TIMEOUT, htonl(timeout));
 	NLA_PUT_BE32(skb, CTA_EXPECT_ID, htonl((unsigned long)exp));
 	NLA_PUT_BE32(skb, CTA_EXPECT_FLAGS, htonl(exp->flags));
-	helper = rcu_dereference(nfct_help(master)->helper);
-	if (helper)
-		NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name);
+	help = nfct_help(master);
+	if (help) {
+		struct nf_conntrack_helper *helper;
+
+		helper = rcu_dereference(help->helper);
+		if (helper)
+			NLA_PUT_STRING(skb, CTA_EXPECT_HELP_NAME, helper->name);
+	}
 
 	return 0;
 
@@ -1921,24 +1926,32 @@ ctnetlink_create_expect(struct net *net, u16 zone,
 	if (!h)
 		return -ENOENT;
 	ct = nf_ct_tuplehash_to_ctrack(h);
-	help = nfct_help(ct);
-
-	if (!help || !help->helper) {
-		/* such conntrack hasn't got any helper, abort */
-		err = -EOPNOTSUPP;
-		goto out;
-	}
-
 	exp = nf_ct_expect_alloc(ct);
 	if (!exp) {
 		err = -ENOMEM;
 		goto out;
 	}
+	help = nfct_help(ct);
+	if (!help) {
+		if (!cda[CTA_EXPECT_TIMEOUT]) {
+			err = -EINVAL;
+			goto out;
+		}
+		exp->timeout.expires =
+		  jiffies + ntohl(nla_get_be32(cda[CTA_EXPECT_TIMEOUT])) * HZ;
 
-	if (cda[CTA_EXPECT_FLAGS])
-		exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS]));
-	else
-		exp->flags = 0;
+		exp->flags = NF_CT_EXPECT_USERSPACE;
+		if (cda[CTA_EXPECT_FLAGS]) {
+			exp->flags |=
+				ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS]));
+		}
+	} else {
+		if (cda[CTA_EXPECT_FLAGS]) {
+			exp->flags = ntohl(nla_get_be32(cda[CTA_EXPECT_FLAGS]));
+			exp->flags &= ~NF_CT_EXPECT_USERSPACE;
+		} else
+			exp->flags = 0;
+	}
 
 	exp->class = 0;
 	exp->expectfn = NULL;
@@ -2109,6 +2122,7 @@ static void __exit ctnetlink_exit(void)
 {
 	pr_info("ctnetlink: unregistering from nfnetlink.\n");
 
+	nf_ct_remove_userspace_expectations();
 #ifdef CONFIG_NF_CONNTRACK_EVENTS
 	nf_ct_expect_unregister_notifier(&ctnl_notifier_exp);
 	nf_conntrack_unregister_notifier(&ctnl_notifier);

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers
  2010-09-21  9:35 ` [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers Pablo Neira Ayuso
  2010-09-21 15:20   ` Patrick McHardy
@ 2010-09-28 19:08   ` Patrick McHardy
  1 sibling, 0 replies; 16+ messages in thread
From: Patrick McHardy @ 2010-09-28 19:08 UTC (permalink / raw)
  To: Pablo Neira Ayuso; +Cc: netfilter-devel

Am 21.09.2010 11:35, schrieb Pablo Neira Ayuso:
> This patch adds the basic infrastructure to support user-space
> expectation helpers via ctnetlink and the netfilter queuing
> infrastructure NFQUEUE. Basically, this patch:
> 
> * adds NF_CT_EXPECT_USERSPACE flag to identify user-space
> created expectations. I have also added a sanity check in
> __nf_ct_expect_check() to avoid that kernel-space helpers
> may create an expectation if the master conntrack has no
> helper assigned.
> * adds some branches to check if the master conntrack helper
> exists, otherwise we skip the code that refers to kernel-space
> helper such as the local expectation list and the expectation
> policy.
> * allows to set the timeout for user-space expectations with
> no helper assigned.
> 
> This patch also modifies ctnetlink to skip including the helper
> name in the Netlink messages if no kernel-space helper is set
> (since no user-space expectation has not kernel-space kernel
> assigned).
> 
> You can access an example user-space FTP conntrack helper at:
> http://people.netfilter.org/pablo/nf-ftp-helper-userspace-POC.tar.bz

Applied, thanks Pablo. I've also fixed up the URL to include
userspace-conntrack-helpers/ in the path :)

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2010-09-28 19:08 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-09-21  9:34 [PATCH 0/4] We all need more expectations Pablo Neira Ayuso
2010-09-21  9:34 ` [PATCH 1/4] netfilter: nf_nat: better error handling of nf_ct_expect_related() in helpers Pablo Neira Ayuso
2010-09-21 15:07   ` Patrick McHardy
2010-09-22  6:35   ` Patrick McHardy
2010-09-21  9:34 ` [PATCH 2/4] netfilter: ctnetlink: missing validation of CTA_EXPECT_ZONE attribute Pablo Neira Ayuso
2010-09-22  6:36   ` Patrick McHardy
2010-09-21  9:35 ` [PATCH 3/4] netfilter: ctnetlink: allow to specify the expectation flags Pablo Neira Ayuso
2010-09-21 15:18   ` Patrick McHardy
2010-09-21 22:38     ` Pablo Neira Ayuso
2010-09-22  6:37       ` Patrick McHardy
2010-09-21  9:35 ` [PATCH 4/4] netfilter: ctnetlink: add support for user-space expectation helpers Pablo Neira Ayuso
2010-09-21 15:20   ` Patrick McHardy
2010-09-21 22:38     ` Pablo Neira Ayuso
2010-09-22  6:45       ` Patrick McHardy
2010-09-22 11:07         ` Pablo Neira Ayuso
2010-09-28 19:08   ` Patrick McHardy

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).