Netdev List
 help / color / mirror / Atom feed
* Re: [PKT_SCHED]: Add stateless NAT
From: Evgeniy Polyakov @ 2007-09-27 13:10 UTC (permalink / raw)
  To: Herbert Xu; +Cc: David S. Miller, netdev, Alexey Kuznetsov, jamal
In-Reply-To: <20070927124515.GA16706@gondor.apana.org.au>

On Thu, Sep 27, 2007 at 08:45:15PM +0800, Herbert Xu (herbert@gondor.apana.org.au) wrote:
> On Thu, Sep 27, 2007 at 04:41:21PM +0400, Evgeniy Polyakov wrote:
> >
> > I've attached simple patch which moves checksum helpers out of
> > CONFIG_NETFILTER option but still in the same linux/netfilter.h header.
> > This should be enough for removing 'select NETFILTER' in your patch.
> 
> Close but no cigar :)

:) take 2.

diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 1dd075e..5313739 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -40,6 +40,41 @@
 #endif
 
 #ifdef __KERNEL__
+
+static inline void nf_csum_replace4(__sum16 *sum, __be32 from, __be32 to)
+{
+	__be32 diff[] = { ~from, to };
+
+	*sum = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(*sum)));
+}
+
+static inline void nf_csum_replace2(__sum16 *sum, __be16 from, __be16 to)
+{
+	nf_csum_replace4(sum, (__force __be32)from, (__force __be32)to);
+}
+
+static inline void nf_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
+			    __be32 from, __be32 to, int pseudohdr)
+{
+	__be32 diff[] = { ~from, to };
+	if (skb->ip_summed != CHECKSUM_PARTIAL) {
+		*sum = csum_fold(csum_partial(diff, sizeof(diff),
+				~csum_unfold(*sum)));
+		if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
+			skb->csum = ~csum_partial(diff, sizeof(diff),
+						~skb->csum);
+	} else if (pseudohdr)
+		*sum = ~csum_fold(csum_partial(diff, sizeof(diff),
+				csum_unfold(*sum)));
+}
+
+static inline void nf_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb,
+				      __be16 from, __be16 to, int pseudohdr)
+{
+	nf_proto_csum_replace4(sum, skb, (__force __be32)from,
+				(__force __be32)to, pseudohdr);
+}
+
 #ifdef CONFIG_NETFILTER
 
 extern void netfilter_init(void);
@@ -289,28 +324,6 @@ extern void nf_invalidate_cache(int pf);
    Returns true or false. */
 extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len);
 
-static inline void nf_csum_replace4(__sum16 *sum, __be32 from, __be32 to)
-{
-	__be32 diff[] = { ~from, to };
-
-	*sum = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(*sum)));
-}
-
-static inline void nf_csum_replace2(__sum16 *sum, __be16 from, __be16 to)
-{
-	nf_csum_replace4(sum, (__force __be32)from, (__force __be32)to);
-}
-
-extern void nf_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
-				      __be32 from, __be32 to, int pseudohdr);
-
-static inline void nf_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb,
-				      __be16 from, __be16 to, int pseudohdr)
-{
-	nf_proto_csum_replace4(sum, skb, (__force __be32)from,
-				(__force __be32)to, pseudohdr);
-}
-
 struct nf_afinfo {
 	unsigned short	family;
 	__sum16		(*checksum)(struct sk_buff *skb, unsigned int hook,
diff --git a/net/netfilter/core.c b/net/netfilter/core.c
index 381a77c..9ffbbe2 100644
--- a/net/netfilter/core.c
+++ b/net/netfilter/core.c
@@ -226,22 +226,6 @@ copy_skb:
 }
 EXPORT_SYMBOL(skb_make_writable);
 
-void nf_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
-			    __be32 from, __be32 to, int pseudohdr)
-{
-	__be32 diff[] = { ~from, to };
-	if (skb->ip_summed != CHECKSUM_PARTIAL) {
-		*sum = csum_fold(csum_partial(diff, sizeof(diff),
-				~csum_unfold(*sum)));
-		if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr)
-			skb->csum = ~csum_partial(diff, sizeof(diff),
-						~skb->csum);
-	} else if (pseudohdr)
-		*sum = ~csum_fold(csum_partial(diff, sizeof(diff),
-				csum_unfold(*sum)));
-}
-EXPORT_SYMBOL(nf_proto_csum_replace4);
-
 #if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
 /* This does not belong here, but locally generated errors need it if connection
    tracking in use: without this, connection may not be in hash table, and hence

-- 
	Evgeniy Polyakov

^ permalink raw reply related

* Re: [PKT_SCHED]: Add stateless NAT
From: Herbert Xu @ 2007-09-27 12:58 UTC (permalink / raw)
  To: jamal; +Cc: David S. Miller, netdev, Alexey Kuznetsov
In-Reply-To: <1190896785.4290.18.camel@localhost>

On Thu, Sep 27, 2007 at 08:39:45AM -0400, jamal wrote:
>
> You also need to p->tcf_qstats.drops++ for all packets that get shot.

I was rather hoping that my packets wouldn't get shot :)
But yeah let's increment the drops counter for consistency.

[PKT_SCHED]: Add stateless NAT

Stateless NAT is useful in controlled environments where restrictions are
placed on through traffic such that we don't need connection tracking to
correctly NAT protocol-specific data.

In particular, this is of interest when the number of flows or the number
of addresses being NATed is large, or if connection tracking information
has to be replicated and where it is not practical to do so.

Previously we had stateless NAT functionality which was integrated into
the IPv4 routing subsystem.  This was a great solution as long as the NAT
worked on a subnet to subnet basis such that the number of NAT rules was
relatively small.  The reason is that for SNAT the routing based system
had to perform a linear scan through the rules.

If the number of rules is large then major renovations would have take
place in the routing subsystem to make this practical.

For the time being, the least intrusive way of achieving this is to use
the u32 classifier written by Alexey Kuznetsov along with the actions
infrastructure implemented by Jamal Hadi Salim.

The following patch is an attempt at this problem by creating a new nat
action that can be invoked from u32 hash tables which would allow large
number of stateless NAT rules that can be used/updated in constant time.

The actual NAT code is mostly based on the previous stateless NAT code
written by Alexey.  In future we might be able to utilise the protocol
NAT code from netfilter to improve support for other protocols.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Thanks,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
diff --git a/include/linux/tc_act/tc_nat.h b/include/linux/tc_act/tc_nat.h
new file mode 100644
index 0000000..9280c6f
--- /dev/null
+++ b/include/linux/tc_act/tc_nat.h
@@ -0,0 +1,29 @@
+#ifndef __LINUX_TC_NAT_H
+#define __LINUX_TC_NAT_H
+
+#include <linux/pkt_cls.h>
+#include <linux/types.h>
+
+#define TCA_ACT_NAT 9
+
+enum
+{
+	TCA_NAT_UNSPEC,
+	TCA_NAT_PARMS,
+	TCA_NAT_TM,
+	__TCA_NAT_MAX
+};
+#define TCA_NAT_MAX (__TCA_NAT_MAX - 1)
+
+#define TCA_NAT_FLAG_EGRESS 1
+                                                                               
+struct tc_nat
+{
+	tc_gen;
+	__be32 old_addr;
+	__be32 new_addr;
+	__be32 mask;
+	__u32 flags;
+};
+
+#endif
diff --git a/include/net/tc_act/tc_nat.h b/include/net/tc_act/tc_nat.h
new file mode 100644
index 0000000..4a691f3
--- /dev/null
+++ b/include/net/tc_act/tc_nat.h
@@ -0,0 +1,21 @@
+#ifndef __NET_TC_NAT_H
+#define __NET_TC_NAT_H
+
+#include <linux/types.h>
+#include <net/act_api.h>
+
+struct tcf_nat {
+	struct tcf_common common;
+
+	__be32 old_addr;
+	__be32 new_addr;
+	__be32 mask;
+	u32 flags;
+};
+
+static inline struct tcf_nat *to_tcf_nat(struct tcf_common *pc)
+{
+	return container_of(pc, struct tcf_nat, common);
+}
+
+#endif /* __NET_TC_NAT_H */
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 8a74cac..92435a8 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -447,6 +447,17 @@ config NET_ACT_IPT
 	  To compile this code as a module, choose M here: the
 	  module will be called ipt.
 
+config NET_ACT_NAT
+        tristate "Stateless NAT"
+        depends on NET_CLS_ACT
+        select NETFILTER
+        ---help---
+	  Say Y here to do stateless NAT on IPv4 packets.  You should use
+	  netfilter for NAT unless you know what you are doing.
+
+	  To compile this code as a module, choose M here: the
+	  module will be called nat.
+
 config NET_ACT_PEDIT
         tristate "Packet Editing"
         depends on NET_CLS_ACT
diff --git a/net/sched/Makefile b/net/sched/Makefile
index b67c36f..81ecbe8 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_NET_ACT_POLICE)	+= act_police.o
 obj-$(CONFIG_NET_ACT_GACT)	+= act_gact.o
 obj-$(CONFIG_NET_ACT_MIRRED)	+= act_mirred.o
 obj-$(CONFIG_NET_ACT_IPT)	+= act_ipt.o
+obj-$(CONFIG_NET_ACT_NAT)	+= act_nat.o
 obj-$(CONFIG_NET_ACT_PEDIT)	+= act_pedit.o
 obj-$(CONFIG_NET_ACT_SIMP)	+= act_simple.o
 obj-$(CONFIG_NET_SCH_FIFO)	+= sch_fifo.o
diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c
new file mode 100644
index 0000000..1bce750
--- /dev/null
+++ b/net/sched/act_nat.c
@@ -0,0 +1,322 @@
+/*
+ * Stateless NAT actions
+ *
+ * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/rtnetlink.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/tc_act/tc_nat.h>
+#include <net/act_api.h>
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/netlink.h>
+#include <net/tc_act/tc_nat.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+
+
+#define NAT_TAB_MASK	15
+static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1];
+static u32 nat_idx_gen;
+static DEFINE_RWLOCK(nat_lock);
+
+static struct tcf_hashinfo nat_hash_info = {
+	.htab	=	tcf_nat_ht,
+	.hmask	=	NAT_TAB_MASK,
+	.lock	=	&nat_lock,
+};
+
+static int tcf_nat_init(struct rtattr *rta, struct rtattr *est,
+			struct tc_action *a, int ovr, int bind)
+{
+	struct rtattr *tb[TCA_NAT_MAX];
+	struct tc_nat *parm;
+	int ret = 0;
+	struct tcf_nat *p;
+	struct tcf_common *pc;
+
+	if (rta == NULL || rtattr_parse_nested(tb, TCA_NAT_MAX, rta) < 0)
+		return -EINVAL;
+
+	if (tb[TCA_NAT_PARMS - 1] == NULL ||
+	    RTA_PAYLOAD(tb[TCA_NAT_PARMS - 1]) < sizeof(*parm))
+		return -EINVAL;
+	parm = RTA_DATA(tb[TCA_NAT_PARMS - 1]);
+
+	pc = tcf_hash_check(parm->index, a, bind, &nat_hash_info);
+	if (!pc) {
+		pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind,
+				     &nat_idx_gen, &nat_hash_info);
+		if (unlikely(!pc))
+			return -ENOMEM;
+		p = to_tcf_nat(pc);
+		ret = ACT_P_CREATED;
+	} else {
+		p = to_tcf_nat(pc);
+		if (!ovr) {
+			tcf_hash_release(pc, bind, &nat_hash_info);
+			return -EEXIST;
+		}
+	}
+
+	spin_lock_bh(&p->tcf_lock);
+	p->old_addr = parm->old_addr;
+	p->new_addr = parm->new_addr;
+	p->mask = parm->mask;
+	p->flags = parm->flags;
+
+	p->tcf_action = parm->action;
+	spin_unlock_bh(&p->tcf_lock);
+
+	if (ret == ACT_P_CREATED)
+		tcf_hash_insert(pc, &nat_hash_info);
+
+	return ret;
+}
+
+static int tcf_nat_cleanup(struct tc_action *a, int bind)
+{
+	struct tcf_nat *p = a->priv;
+
+	return tcf_hash_release(&p->common, bind, &nat_hash_info);
+}
+
+static int tcf_nat(struct sk_buff *skb, struct tc_action *a,
+		   struct tcf_result *res)
+{
+	struct tcf_nat *p = a->priv;
+	struct iphdr *iph;
+	__be32 old_addr;
+	__be32 new_addr;
+	__be32 mask;
+	__be32 addr;
+	int egress;
+	int action;
+	int ihl;
+
+	spin_lock(&p->tcf_lock);
+
+	p->tcf_tm.lastuse = jiffies;
+	old_addr = p->old_addr;
+	new_addr = p->new_addr;
+	mask = p->mask;
+	egress = p->flags & TCA_NAT_FLAG_EGRESS;
+	action = p->tcf_action;
+
+	p->tcf_bstats.bytes += skb->len;
+	p->tcf_bstats.packets++;
+
+	spin_unlock(&p->tcf_lock);
+
+	if (unlikely(action == TC_ACT_SHOT))
+		goto drop;
+
+	if (!pskb_may_pull(skb, sizeof(*iph)))
+		goto drop;
+
+	iph = ip_hdr(skb);
+
+	if (egress)
+		addr = iph->saddr;
+	else
+		addr = iph->daddr;
+
+	if (!((old_addr ^ addr) & mask)) {
+		if (skb_cloned(skb) &&
+		    !skb_clone_writable(skb, sizeof(*iph)) &&
+		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+			goto drop;
+
+		new_addr &= mask;
+		new_addr |= addr & ~mask;
+
+		/* Rewrite IP header */
+		iph = ip_hdr(skb);
+		if (egress)
+			iph->saddr = new_addr;
+		else
+			iph->daddr = new_addr;
+
+		nf_csum_replace4(&iph->check, addr, new_addr);
+	}
+
+	ihl = iph->ihl * 4;
+
+	/* It would be nice to share code with stateful NAT. */
+	switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
+	case IPPROTO_TCP:
+	{
+		struct tcphdr *tcph;
+
+		if (!pskb_may_pull(skb, ihl + sizeof(*tcph)) ||
+		    (skb_cloned(skb) &&
+		     !skb_clone_writable(skb, ihl + sizeof(*tcph)) &&
+		     pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+			goto drop;
+
+		tcph = (void *)(skb_network_header(skb) + ihl);
+		nf_proto_csum_replace4(&tcph->check, skb, addr, new_addr, 1);
+		break;
+	}
+	case IPPROTO_UDP:
+	{
+		struct udphdr *udph;
+
+		if (!pskb_may_pull(skb, ihl + sizeof(*udph)) ||
+		    (skb_cloned(skb) &&
+		     !skb_clone_writable(skb, ihl + sizeof(*udph)) &&
+		     pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+			goto drop;
+
+		udph = (void *)(skb_network_header(skb) + ihl);
+		if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+			nf_proto_csum_replace4(&udph->check, skb, addr,
+					       new_addr, 1);
+			if (!udph->check)
+				udph->check = CSUM_MANGLED_0;
+		}
+		break;
+	}
+	case IPPROTO_ICMP:
+	{
+		struct icmphdr *icmph;
+
+		if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph)))
+			goto drop;
+
+		icmph = (void *)(skb_network_header(skb) + ihl);
+
+		if ((icmph->type != ICMP_DEST_UNREACH) &&
+		    (icmph->type != ICMP_TIME_EXCEEDED) &&
+		    (icmph->type != ICMP_PARAMETERPROB))
+			break;
+
+		iph = (void *)(icmph + 1);
+		if (egress)
+			addr = iph->daddr;
+		else
+			addr = iph->saddr;
+
+		if ((old_addr ^ addr) & mask)
+			break;
+
+		if (skb_cloned(skb) &&
+		    !skb_clone_writable(skb,
+		    			ihl + sizeof(*icmph) + sizeof(*iph)) &&
+		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+			goto drop;
+
+		icmph = (void *)(skb_network_header(skb) + ihl);
+		iph = (void *)(icmph + 1);
+
+		new_addr &= mask;
+		new_addr |= addr & ~mask;
+
+		/* XXX Fix up the inner checksums. */
+		if (egress)
+			iph->daddr = new_addr;
+		else
+			iph->saddr = new_addr;
+
+		nf_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr,
+				       1);
+		break;
+	}
+	default:
+		break;
+	}
+
+	return action;
+
+drop:
+	spin_lock(&p->tcf_lock);
+	p->tcf_qstats.drops++;
+	spin_unlock(&p->tcf_lock);
+	return TC_ACT_SHOT;
+}
+
+static int tcf_nat_dump(struct sk_buff *skb, struct tc_action *a,
+			int bind, int ref)
+{
+	unsigned char *b = skb_tail_pointer(skb);
+	struct tcf_nat *p = a->priv;
+	struct tc_nat *opt;
+	struct tcf_t t;
+	int s;
+
+	s = sizeof(*opt);
+
+	/* netlink spinlocks held above us - must use ATOMIC */
+	opt = kzalloc(s, GFP_ATOMIC);
+	if (unlikely(!opt))
+		return -ENOBUFS;
+
+	opt->old_addr = p->old_addr;
+	opt->new_addr = p->new_addr;
+	opt->mask = p->mask;
+	opt->flags = p->flags;
+
+	opt->index = p->tcf_index;
+	opt->action = p->tcf_action;
+	opt->refcnt = p->tcf_refcnt - ref;
+	opt->bindcnt = p->tcf_bindcnt - bind;
+
+	RTA_PUT(skb, TCA_NAT_PARMS, s, opt);
+	t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install);
+	t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse);
+	t.expires = jiffies_to_clock_t(p->tcf_tm.expires);
+	RTA_PUT(skb, TCA_NAT_TM, sizeof(t), &t);
+
+	kfree(opt);
+
+	return skb->len;
+
+rtattr_failure:
+	nlmsg_trim(skb, b);
+	kfree(opt);
+	return -1;
+}
+
+static struct tc_action_ops act_nat_ops = {
+	.kind		=	"nat",
+	.hinfo		=	&nat_hash_info,
+	.type		=	TCA_ACT_NAT,
+	.capab		=	TCA_CAP_NONE,
+	.owner		=	THIS_MODULE,
+	.act		=	tcf_nat,
+	.dump		=	tcf_nat_dump,
+	.cleanup	=	tcf_nat_cleanup,
+	.lookup		=	tcf_hash_search,
+	.init		=	tcf_nat_init,
+	.walk		=	tcf_generic_walker
+};
+
+MODULE_DESCRIPTION("Stateless NAT actions");
+MODULE_LICENSE("GPL");
+
+static int __init nat_init_module(void)
+{
+	return tcf_register_action(&act_nat_ops);
+}
+
+static void __exit nat_cleanup_module(void)
+{
+	tcf_unregister_action(&act_nat_ops);
+}
+
+module_init(nat_init_module);
+module_exit(nat_cleanup_module);

^ permalink raw reply related

* Re: [PKT_SCHED]: Add stateless NAT
From: Herbert Xu @ 2007-09-27 13:01 UTC (permalink / raw)
  To: jamal, Stephen Hemminger; +Cc: David S. Miller, netdev, Alexey Kuznetsov
In-Reply-To: <1190896785.4290.18.camel@localhost>

On Thu, Sep 27, 2007 at 08:39:45AM -0400, jamal wrote:
>
> Do you have plans to do the iproute bits? If you do it will be nice to
> also update the doc/examples with some simple example(s).

Oh yes, I didn't test this by poking bits in the kernel
you know :)

Here are the iproute bits.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
diff --git a/include/utils.h b/include/utils.h
index a3fd335..b0dc03e 100644
--- a/include/utils.h
+++ b/include/utils.h
@@ -36,7 +36,7 @@ extern char * _SL_;
 
 extern void incomplete_command(void) __attribute__((noreturn));
 
-#define NEXT_ARG() do { argv++; if (--argc <= 0) incomplete_command(); } while(0)
+#define NEXT_ARG() do { argv++; if (--argc < 0) incomplete_command(); } while(0)
 #define NEXT_ARG_OK() (argc - 1 > 0)
 #define PREV_ARG() do { argv--; argc++; } while(0)
 
diff --git a/tc/Makefile b/tc/Makefile
index 22cd437..cd5a69e 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -26,6 +26,7 @@ TCMODULES += q_htb.o
 TCMODULES += m_gact.o
 TCMODULES += m_mirred.o
 TCMODULES += m_ipt.o
+TCMODULES += m_nat.o
 TCMODULES += m_pedit.o
 TCMODULES += p_ip.o
 TCMODULES += p_icmp.o
diff --git a/tc/m_nat.c b/tc/m_nat.c
new file mode 100644
index 0000000..9a6c7da
--- /dev/null
+++ b/tc/m_nat.c
@@ -0,0 +1,208 @@
+/*
+ * m_nat.c		NAT module
+ *
+ *		This program is free software; you can distribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <dlfcn.h>
+#include "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_nat.h>
+
+static void
+explain(void)
+{
+	fprintf(stderr, "Usage: ... nat NAT\n"
+			"NAT := DIRECTION OLD NEW\n"
+			"DIRECTION := { ingress | egress }\n"
+			"OLD := PREFIX\n"
+			"NEW := ADDRESS\n");
+}
+
+static void
+usage(void)
+{
+	explain();
+	exit(-1);
+}
+
+static int
+parse_nat_args(int *argc_p, char ***argv_p,struct tc_nat *sel)
+{
+	int argc = *argc_p;
+	char **argv = *argv_p;
+	inet_prefix addr;
+
+	if (argc <= 0)
+		return -1;
+
+	if (matches(*argv, "egress") == 0)
+		sel->flags |= TCA_NAT_FLAG_EGRESS;
+	else if (matches(*argv, "ingress") != 0)
+		goto bad_val;
+
+	NEXT_ARG();
+
+	if (get_prefix_1(&addr, *argv, AF_INET))
+		goto bad_val;
+
+	sel->old_addr = addr.data[0];
+	sel->mask = htonl(~0u << (32 - addr.bitlen));
+
+	NEXT_ARG();
+
+	if (get_prefix_1(&addr, *argv, AF_INET))
+		goto bad_val;
+
+	sel->new_addr = addr.data[0];
+
+	NEXT_ARG();
+
+	*argc_p = argc;
+	*argv_p = argv;
+	return 0;
+
+bad_val:
+	return -1;
+}
+
+static int
+parse_nat(struct action_util *a, int *argc_p, char ***argv_p, int tca_id, struct nlmsghdr *n)
+{
+	struct tc_nat sel;
+
+	int argc = *argc_p;
+	char **argv = *argv_p;
+	int ok = 0, iok = 0;
+	struct rtattr *tail;
+
+	memset(&sel, 0, sizeof(sel));
+
+	while (argc > 0) {
+		if (matches(*argv, "nat") == 0) {
+			NEXT_ARG();
+			if (parse_nat_args(&argc, &argv, &sel)) {
+				fprintf(stderr, "Illegal nat construct (%s) \n",
+					*argv);
+				explain();
+				return -1;
+			}
+			ok++;
+			continue;
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+		} else {
+			break;
+		}
+
+	}
+
+	if (!ok) {
+		explain();
+		return -1;
+	}
+
+	if (argc) {
+		if (matches(*argv, "reclassify") == 0) {
+			sel.action = TC_ACT_RECLASSIFY;
+			NEXT_ARG();
+		} else if (matches(*argv, "pipe") == 0) {
+			sel.action = TC_ACT_PIPE;
+			NEXT_ARG();
+		} else if (matches(*argv, "drop") == 0 ||
+			matches(*argv, "shot") == 0) {
+			sel.action = TC_ACT_SHOT;
+			NEXT_ARG();
+		} else if (matches(*argv, "continue") == 0) {
+			sel.action = TC_ACT_UNSPEC;
+			NEXT_ARG();
+		} else if (matches(*argv, "pass") == 0) {
+			sel.action = TC_ACT_OK;
+			NEXT_ARG();
+		}
+	}
+
+	if (argc) {
+		if (matches(*argv, "index") == 0) {
+			NEXT_ARG();
+			if (get_u32(&sel.index, *argv, 10)) {
+				fprintf(stderr, "Pedit: Illegal \"index\"\n");
+				return -1;
+			}
+			argc--;
+			argv++;
+			iok++;
+		}
+	}
+
+	tail = NLMSG_TAIL(n);
+	addattr_l(n, MAX_MSG, tca_id, NULL, 0);
+	addattr_l(n, MAX_MSG, TCA_NAT_PARMS, &sel, sizeof(sel));
+	tail->rta_len = (char *)NLMSG_TAIL(n) - (char *)tail;
+
+	*argc_p = argc;
+	*argv_p = argv;
+	return 0;
+}
+
+static int
+print_nat(struct action_util *au,FILE * f, struct rtattr *arg)
+{
+	struct tc_nat *sel;
+	struct rtattr *tb[TCA_NAT_MAX + 1];
+	char buf1[256];
+	char buf2[256];
+	SPRINT_BUF(buf3);
+	int len;
+
+	if (arg == NULL)
+		return -1;
+
+	parse_rtattr_nested(tb, TCA_NAT_MAX, arg);
+
+	if (tb[TCA_NAT_PARMS] == NULL) {
+		fprintf(f, "[NULL nat parameters]");
+		return -1;
+	}
+	sel = RTA_DATA(tb[TCA_NAT_PARMS]);
+
+	len = ffs(sel->mask);
+	len = len ? 33 - len : 0;
+
+	fprintf(f, " nat %s %s/%d %s %s", sel->flags & TCA_NAT_FLAG_EGRESS ?
+					  "egress" : "ingress",
+		format_host(AF_INET, 4, &sel->old_addr, buf1, sizeof(buf1)),
+		len,
+		format_host(AF_INET, 4, &sel->new_addr, buf2, sizeof(buf2)),
+		action_n2a(sel->action, buf3, sizeof (buf3)));
+
+	if (show_stats) {
+		if (tb[TCA_NAT_TM]) {
+			struct tcf_t *tm = RTA_DATA(tb[TCA_NAT_TM]);
+			print_tm(f,tm);
+		}
+	}
+
+	return 0;
+}
+
+struct action_util nat_action_util = {
+	.id = "nat",
+	.parse_aopt = parse_nat,
+	.print_aopt = print_nat,
+};

^ permalink raw reply related

* Re: [PKT_SCHED]: Add stateless NAT
From: Evgeniy Polyakov @ 2007-09-27 13:06 UTC (permalink / raw)
  To: jamal; +Cc: Herbert Xu, David S. Miller, netdev, Alexey Kuznetsov
In-Reply-To: <1190897523.4290.29.camel@localhost>

On Thu, Sep 27, 2007 at 08:52:03AM -0400, jamal (hadi@cyberus.ca) wrote:
> On Thu, 2007-27-09 at 16:41 +0400, Evgeniy Polyakov wrote:
> 
> > I've attached simple patch which moves checksum helpers out of
> > CONFIG_NETFILTER option but still in the same linux/netfilter.h header.
> > This should be enough for removing 'select NETFILTER' in your patch.
> 
> Is there any point in keeping the code inside netfilter or keeping
> the nf_ prefix? something in net/utilities/ or net/core maybe?
> the nf_* can still exist in netfilter as aliases to wherever this is
> moved to.

No, there is no point in keeping that, I just wanted the smallest
possible change :)

-- 
	Evgeniy Polyakov

^ permalink raw reply

* Re: [PKT_SCHED]: Add stateless NAT
From: jamal @ 2007-09-27 12:52 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: Herbert Xu, David S. Miller, netdev, Alexey Kuznetsov
In-Reply-To: <20070927124120.GA8897@2ka.mipt.ru>

On Thu, 2007-27-09 at 16:41 +0400, Evgeniy Polyakov wrote:

> I've attached simple patch which moves checksum helpers out of
> CONFIG_NETFILTER option but still in the same linux/netfilter.h header.
> This should be enough for removing 'select NETFILTER' in your patch.

Is there any point in keeping the code inside netfilter or keeping
the nf_ prefix? something in net/utilities/ or net/core maybe?
the nf_* can still exist in netfilter as aliases to wherever this is
moved to.

cheers,
jamal




^ permalink raw reply

* Re: [PKT_SCHED]: Add stateless NAT
From: jamal @ 2007-09-27 12:46 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: Herbert Xu, David S. Miller, netdev, Alexey Kuznetsov
In-Reply-To: <20070927092512.GD29329@2ka.mipt.ru>

On Thu, 2007-27-09 at 13:25 +0400, Evgeniy Polyakov wrote:


> > +#define NAT_TAB_MASK	15
> 
> This really wants to be configurable at least via module parameter.

Would not be a bad idea, but not specific to just this one action.
Note: probably not a very big deal to have 15 there because in the fast
path that table is not being looked up; once an action is bound to a
filter, its the filter lookups that count.

> > +static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1];
> > +static u32 nat_idx_gen;
> > +static DEFINE_RWLOCK(nat_lock);
> 
> > +static struct tcf_hashinfo nat_hash_info = {
> > +	.htab	=	tcf_nat_ht,
> > +	.hmask	=	NAT_TAB_MASK,
> > +	.lock	=	&nat_lock,
> > +};
> 
> When I read this I swear I heard 'I want to be RCU'.
> But that is another task.

Go ahead and clean that up to use rcu. What would be really nice is to
have generic hash table scheme which uses the scheme above and then this
code (and a lot other) is converted to use it; if you stared a bit at
the code youd notice a lot of commonalities with a lot of similar
netcode (look at the ipip driver for example). The LinuxWay(tm).
I know Patrick hinted at some point he may do that (or maybe it was my
imagination;->)

cheers,
jamal


^ permalink raw reply

* Re: [PKT_SCHED]: Add stateless NAT
From: Herbert Xu @ 2007-09-27 12:45 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: David S. Miller, netdev, Alexey Kuznetsov, jamal
In-Reply-To: <20070927124120.GA8897@2ka.mipt.ru>

On Thu, Sep 27, 2007 at 04:41:21PM +0400, Evgeniy Polyakov wrote:
>
> I've attached simple patch which moves checksum helpers out of
> CONFIG_NETFILTER option but still in the same linux/netfilter.h header.
> This should be enough for removing 'select NETFILTER' in your patch.

Close but no cigar :)

> diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
> index 1dd075e..6c3fb10 100644
> --- a/include/linux/netfilter.h
> +++ b/include/linux/netfilter.h
> @@ -40,6 +40,29 @@
>  #endif
>  
>  #ifdef __KERNEL__
> +
> +static inline void nf_csum_replace4(__sum16 *sum, __be32 from, __be32 to)
> +{
> +	__be32 diff[] = { ~from, to };
> +
> +	*sum = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(*sum)));
> +}
> +
> +static inline void nf_csum_replace2(__sum16 *sum, __be16 from, __be16 to)
> +{
> +	nf_csum_replace4(sum, (__force __be32)from, (__force __be32)to);
> +}
> +
> +extern void nf_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
> +				      __be32 from, __be32 to, int pseudohdr);

This function won't exist if NETFILTER is off.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [PKT_SCHED]: Add stateless NAT
From: Evgeniy Polyakov @ 2007-09-27 12:41 UTC (permalink / raw)
  To: Herbert Xu; +Cc: David S. Miller, netdev, Alexey Kuznetsov, jamal
In-Reply-To: <20070927102916.GA15749@gondor.apana.org.au>

On Thu, Sep 27, 2007 at 06:29:16PM +0800, Herbert Xu (herbert@gondor.apana.org.au) wrote:
> On Thu, Sep 27, 2007 at 02:07:53PM +0400, Evgeniy Polyakov wrote:
> >
> > It forces all inpuit/pre/post/forward hooks to be enbled not as a direct
> > function call, but as additional lookups. And unability to remove
> > netfilter from config. And just because of couple of checksum helpers...
> 
> I'm certainly not against patches moving that code out of
> netfilter.

I've attached simple patch which moves checksum helpers out of
CONFIG_NETFILTER option but still in the same linux/netfilter.h header.
This should be enough for removing 'select NETFILTER' in your patch.

diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h
index 1dd075e..6c3fb10 100644
--- a/include/linux/netfilter.h
+++ b/include/linux/netfilter.h
@@ -40,6 +40,29 @@
 #endif
 
 #ifdef __KERNEL__
+
+static inline void nf_csum_replace4(__sum16 *sum, __be32 from, __be32 to)
+{
+	__be32 diff[] = { ~from, to };
+
+	*sum = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(*sum)));
+}
+
+static inline void nf_csum_replace2(__sum16 *sum, __be16 from, __be16 to)
+{
+	nf_csum_replace4(sum, (__force __be32)from, (__force __be32)to);
+}
+
+extern void nf_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
+				      __be32 from, __be32 to, int pseudohdr);
+
+static inline void nf_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb,
+				      __be16 from, __be16 to, int pseudohdr)
+{
+	nf_proto_csum_replace4(sum, skb, (__force __be32)from,
+				(__force __be32)to, pseudohdr);
+}
+
 #ifdef CONFIG_NETFILTER
 
 extern void netfilter_init(void);
@@ -289,28 +312,6 @@ extern void nf_invalidate_cache(int pf);
    Returns true or false. */
 extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len);
 
-static inline void nf_csum_replace4(__sum16 *sum, __be32 from, __be32 to)
-{
-	__be32 diff[] = { ~from, to };
-
-	*sum = csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum_unfold(*sum)));
-}
-
-static inline void nf_csum_replace2(__sum16 *sum, __be16 from, __be16 to)
-{
-	nf_csum_replace4(sum, (__force __be32)from, (__force __be32)to);
-}
-
-extern void nf_proto_csum_replace4(__sum16 *sum, struct sk_buff *skb,
-				      __be32 from, __be32 to, int pseudohdr);
-
-static inline void nf_proto_csum_replace2(__sum16 *sum, struct sk_buff *skb,
-				      __be16 from, __be16 to, int pseudohdr)
-{
-	nf_proto_csum_replace4(sum, skb, (__force __be32)from,
-				(__force __be32)to, pseudohdr);
-}
-
 struct nf_afinfo {
 	unsigned short	family;
 	__sum16		(*checksum)(struct sk_buff *skb, unsigned int hook,



-- 
	Evgeniy Polyakov

^ permalink raw reply related

* Re: [PKT_SCHED]: Add stateless NAT
From: jamal @ 2007-09-27 12:39 UTC (permalink / raw)
  To: Herbert Xu; +Cc: David S. Miller, netdev, Alexey Kuznetsov
In-Reply-To: <20070927073446.GA14643@gondor.apana.org.au>

nice work. I like the egress flag idea;->
and who would have thunk stateless nat could be written in such a few
lines ;-> I would have put the checksum as a separate action but it is
fine the way you did it since it simplifies config.
more comments below.

On Thu, 2007-27-09 at 15:34 +0800, Herbert Xu wrote:

> +config NET_ACT_NAT
> +        tristate "Stateless NAT"
> +        depends on NET_CLS_ACT
> +        select NETFILTER

I am gonna have to agree with Evgeniy on this Herbert;->
The rewards are it will improve performance for people who dont need
netfilter.
Ok, who is gonna move the csum utility functions out? /me looks at
Evgeniy;->
I could do it realsoonnow if noone raises their hands. 
In any case, it would be real nice to have but i dont see it as a show
stopper for inclusion.
 
> +static int tcf_nat(struct sk_buff *skb, struct tc_action *a,
> +		   struct tcf_result *res)
> +{
> +	struct tcf_nat *p = a->priv;

> +	spin_lock(&p->tcf_lock);
> +
> +	p->tcf_tm.lastuse = jiffies;
> +	old_addr = p->old_addr;
> +	new_addr = p->new_addr;
> +	mask = p->mask;
> +	egress = p->flags & TCA_NAT_FLAG_EGRESS;
> +	action = p->tcf_action;
> +
> +	p->tcf_bstats.bytes += skb->len;
> +	p->tcf_bstats.packets++;
> +
> +	spin_unlock(&p->tcf_lock);

You also need to p->tcf_qstats.drops++ for all packets that get shot.
I would just hold tcf_lock until the end.
If you are concerned about performance of multiple flows contending for
the lock, then just create a new action entry per flow by using a
different index for each; you cant avoid contention with control path in
case of updates, but i suspect that would be a rare occasion.

Do you have plans to do the iproute bits? If you do it will be nice to
also update the doc/examples with some simple example(s).

cheers,
jamal


^ permalink raw reply

* Re: [PKT_SCHED]: Add stateless NAT
From: Herbert Xu @ 2007-09-27 12:27 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: David S. Miller, netdev, Alexey Kuznetsov, jamal
In-Reply-To: <20070927093358.GA15357@gondor.apana.org.au>

On Thu, Sep 27, 2007 at 05:33:58PM +0800, Herbert Xu wrote:
>
> > Modile will be called 'nat' I believe.
> 
> Good catch, now you know where I copied it from :)

Here's an updated version with this typo fixed.

[PKT_SCHED]: Add stateless NAT

Stateless NAT is useful in controlled environments where restrictions are
placed on through traffic such that we don't need connection tracking to
correctly NAT protocol-specific data.

In particular, this is of interest when the number of flows or the number
of addresses being NATed is large, or if connection tracking information
has to be replicated and where it is not practical to do so.

Previously we had stateless NAT functionality which was integrated into
the IPv4 routing subsystem.  This was a great solution as long as the NAT
worked on a subnet to subnet basis such that the number of NAT rules was
relatively small.  The reason is that for SNAT the routing based system
had to perform a linear scan through the rules.

If the number of rules is large then major renovations would have take
place in the routing subsystem to make this practical.

For the time being, the least intrusive way of achieving this is to use
the u32 classifier written by Alexey Kuznetsov along with the actions
infrastructure implemented by Jamal Hadi Salim.

The following patch is an attempt at this problem by creating a new nat
action that can be invoked from u32 hash tables which would allow large
number of stateless NAT rules that can be used/updated in constant time.

The actual NAT code is mostly based on the previous stateless NAT code
written by Alexey.  In future we might be able to utilise the protocol
NAT code from netfilter to improve support for other protocols.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt
--
diff --git a/include/linux/tc_act/tc_nat.h b/include/linux/tc_act/tc_nat.h
new file mode 100644
index 0000000..9280c6f
--- /dev/null
+++ b/include/linux/tc_act/tc_nat.h
@@ -0,0 +1,29 @@
+#ifndef __LINUX_TC_NAT_H
+#define __LINUX_TC_NAT_H
+
+#include <linux/pkt_cls.h>
+#include <linux/types.h>
+
+#define TCA_ACT_NAT 9
+
+enum
+{
+	TCA_NAT_UNSPEC,
+	TCA_NAT_PARMS,
+	TCA_NAT_TM,
+	__TCA_NAT_MAX
+};
+#define TCA_NAT_MAX (__TCA_NAT_MAX - 1)
+
+#define TCA_NAT_FLAG_EGRESS 1
+                                                                               
+struct tc_nat
+{
+	tc_gen;
+	__be32 old_addr;
+	__be32 new_addr;
+	__be32 mask;
+	__u32 flags;
+};
+
+#endif
diff --git a/include/net/tc_act/tc_nat.h b/include/net/tc_act/tc_nat.h
new file mode 100644
index 0000000..4a691f3
--- /dev/null
+++ b/include/net/tc_act/tc_nat.h
@@ -0,0 +1,21 @@
+#ifndef __NET_TC_NAT_H
+#define __NET_TC_NAT_H
+
+#include <linux/types.h>
+#include <net/act_api.h>
+
+struct tcf_nat {
+	struct tcf_common common;
+
+	__be32 old_addr;
+	__be32 new_addr;
+	__be32 mask;
+	u32 flags;
+};
+
+static inline struct tcf_nat *to_tcf_nat(struct tcf_common *pc)
+{
+	return container_of(pc, struct tcf_nat, common);
+}
+
+#endif /* __NET_TC_NAT_H */
diff --git a/net/sched/Kconfig b/net/sched/Kconfig
index 8a74cac..22b34f2 100644
--- a/net/sched/Kconfig
+++ b/net/sched/Kconfig
@@ -447,6 +447,17 @@ config NET_ACT_IPT
 	  To compile this code as a module, choose M here: the
 	  module will be called ipt.
 
+config NET_ACT_NAT
+        tristate "Stateless NAT"
+        depends on NET_CLS_ACT
+        select NETFILTER
+        ---help---
+	  Say Y here to do stateless NAT on IPv4 packets.  You should use
+	  netfilter for NAT unless you know what you are doing.
+
+	  To compile this code as a module, choose M here: the
+	  module will be called nat.
+
 config NET_ACT_PEDIT
         tristate "Packet Editing"
         depends on NET_CLS_ACT
diff --git a/net/sched/Makefile b/net/sched/Makefile
index b67c36f..81ecbe8 100644
--- a/net/sched/Makefile
+++ b/net/sched/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_NET_ACT_POLICE)	+= act_police.o
 obj-$(CONFIG_NET_ACT_GACT)	+= act_gact.o
 obj-$(CONFIG_NET_ACT_MIRRED)	+= act_mirred.o
 obj-$(CONFIG_NET_ACT_IPT)	+= act_ipt.o
+obj-$(CONFIG_NET_ACT_NAT)	+= act_nat.o
 obj-$(CONFIG_NET_ACT_PEDIT)	+= act_pedit.o
 obj-$(CONFIG_NET_ACT_SIMP)	+= act_simple.o
 obj-$(CONFIG_NET_SCH_FIFO)	+= sch_fifo.o
diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c
new file mode 100644
index 0000000..efd6d7d
--- /dev/null
+++ b/net/sched/act_nat.c
@@ -0,0 +1,313 @@
+/*
+ * Stateless NAT actions
+ *
+ * Copyright (c) 2007 Herbert Xu <herbert@gondor.apana.org.au>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/netfilter.h>
+#include <linux/rtnetlink.h>
+#include <linux/skbuff.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/tc_act/tc_nat.h>
+#include <net/act_api.h>
+#include <net/icmp.h>
+#include <net/ip.h>
+#include <net/netlink.h>
+#include <net/tc_act/tc_nat.h>
+#include <net/tcp.h>
+#include <net/udp.h>
+
+
+#define NAT_TAB_MASK	15
+static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1];
+static u32 nat_idx_gen;
+static DEFINE_RWLOCK(nat_lock);
+
+static struct tcf_hashinfo nat_hash_info = {
+	.htab	=	tcf_nat_ht,
+	.hmask	=	NAT_TAB_MASK,
+	.lock	=	&nat_lock,
+};
+
+static int tcf_nat_init(struct rtattr *rta, struct rtattr *est,
+			struct tc_action *a, int ovr, int bind)
+{
+	struct rtattr *tb[TCA_NAT_MAX];
+	struct tc_nat *parm;
+	int ret = 0;
+	struct tcf_nat *p;
+	struct tcf_common *pc;
+
+	if (rta == NULL || rtattr_parse_nested(tb, TCA_NAT_MAX, rta) < 0)
+		return -EINVAL;
+
+	if (tb[TCA_NAT_PARMS - 1] == NULL ||
+	    RTA_PAYLOAD(tb[TCA_NAT_PARMS - 1]) < sizeof(*parm))
+		return -EINVAL;
+	parm = RTA_DATA(tb[TCA_NAT_PARMS - 1]);
+
+	pc = tcf_hash_check(parm->index, a, bind, &nat_hash_info);
+	if (!pc) {
+		pc = tcf_hash_create(parm->index, est, a, sizeof(*p), bind,
+				     &nat_idx_gen, &nat_hash_info);
+		if (unlikely(!pc))
+			return -ENOMEM;
+		p = to_tcf_nat(pc);
+		ret = ACT_P_CREATED;
+	} else {
+		p = to_tcf_nat(pc);
+		if (!ovr) {
+			tcf_hash_release(pc, bind, &nat_hash_info);
+			return -EEXIST;
+		}
+	}
+
+	spin_lock_bh(&p->tcf_lock);
+	p->old_addr = parm->old_addr;
+	p->new_addr = parm->new_addr;
+	p->mask = parm->mask;
+	p->flags = parm->flags;
+
+	p->tcf_action = parm->action;
+	spin_unlock_bh(&p->tcf_lock);
+
+	if (ret == ACT_P_CREATED)
+		tcf_hash_insert(pc, &nat_hash_info);
+
+	return ret;
+}
+
+static int tcf_nat_cleanup(struct tc_action *a, int bind)
+{
+	struct tcf_nat *p = a->priv;
+
+	return tcf_hash_release(&p->common, bind, &nat_hash_info);
+}
+
+static int tcf_nat(struct sk_buff *skb, struct tc_action *a,
+		   struct tcf_result *res)
+{
+	struct tcf_nat *p = a->priv;
+	struct iphdr *iph;
+	__be32 old_addr;
+	__be32 new_addr;
+	__be32 mask;
+	__be32 addr;
+	int egress;
+	int action;
+	int ihl;
+
+	spin_lock(&p->tcf_lock);
+
+	p->tcf_tm.lastuse = jiffies;
+	old_addr = p->old_addr;
+	new_addr = p->new_addr;
+	mask = p->mask;
+	egress = p->flags & TCA_NAT_FLAG_EGRESS;
+	action = p->tcf_action;
+
+	p->tcf_bstats.bytes += skb->len;
+	p->tcf_bstats.packets++;
+
+	spin_unlock(&p->tcf_lock);
+
+	if (!pskb_may_pull(skb, sizeof(*iph)))
+		return TC_ACT_SHOT;
+
+	iph = ip_hdr(skb);
+
+	if (egress)
+		addr = iph->saddr;
+	else
+		addr = iph->daddr;
+
+	if (!((old_addr ^ addr) & mask)) {
+		if (skb_cloned(skb) &&
+		    !skb_clone_writable(skb, sizeof(*iph)) &&
+		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+			return TC_ACT_SHOT;
+
+		new_addr &= mask;
+		new_addr |= addr & ~mask;
+
+		/* Rewrite IP header */
+		iph = ip_hdr(skb);
+		if (egress)
+			iph->saddr = new_addr;
+		else
+			iph->daddr = new_addr;
+
+		nf_csum_replace4(&iph->check, addr, new_addr);
+	}
+
+	ihl = iph->ihl * 4;
+
+	/* It would be nice to share code with stateful NAT. */
+	switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
+	case IPPROTO_TCP:
+	{
+		struct tcphdr *tcph;
+
+		if (!pskb_may_pull(skb, ihl + sizeof(*tcph)) ||
+		    (skb_cloned(skb) &&
+		     !skb_clone_writable(skb, ihl + sizeof(*tcph)) &&
+		     pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+			return TC_ACT_SHOT;
+
+		tcph = (void *)(skb_network_header(skb) + ihl);
+		nf_proto_csum_replace4(&tcph->check, skb, addr, new_addr, 1);
+		break;
+	}
+	case IPPROTO_UDP:
+	{
+		struct udphdr *udph;
+
+		if (!pskb_may_pull(skb, ihl + sizeof(*udph)) ||
+		    (skb_cloned(skb) &&
+		     !skb_clone_writable(skb, ihl + sizeof(*udph)) &&
+		     pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+			return TC_ACT_SHOT;
+
+		udph = (void *)(skb_network_header(skb) + ihl);
+		if (udph->check || skb->ip_summed == CHECKSUM_PARTIAL) {
+			nf_proto_csum_replace4(&udph->check, skb, addr,
+					       new_addr, 1);
+			if (!udph->check)
+				udph->check = CSUM_MANGLED_0;
+		}
+		break;
+	}
+	case IPPROTO_ICMP:
+	{
+		struct icmphdr *icmph;
+
+		if (!pskb_may_pull(skb, ihl + sizeof(*icmph) + sizeof(*iph)))
+			return TC_ACT_SHOT;
+
+		icmph = (void *)(skb_network_header(skb) + ihl);
+
+		if ((icmph->type != ICMP_DEST_UNREACH) &&
+		    (icmph->type != ICMP_TIME_EXCEEDED) &&
+		    (icmph->type != ICMP_PARAMETERPROB))
+			break;
+
+		iph = (void *)(icmph + 1);
+		if (egress)
+			addr = iph->daddr;
+		else
+			addr = iph->saddr;
+
+		if ((old_addr ^ addr) & mask)
+			break;
+
+		if (skb_cloned(skb) &&
+		    !skb_clone_writable(skb,
+		    			ihl + sizeof(*icmph) + sizeof(*iph)) &&
+		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+			return TC_ACT_SHOT;
+
+		icmph = (void *)(skb_network_header(skb) + ihl);
+		iph = (void *)(icmph + 1);
+
+		new_addr &= mask;
+		new_addr |= addr & ~mask;
+
+		/* XXX Fix up the inner checksums. */
+		if (egress)
+			iph->daddr = new_addr;
+		else
+			iph->saddr = new_addr;
+
+		nf_proto_csum_replace4(&icmph->checksum, skb, addr, new_addr,
+				       1);
+		break;
+	}
+	default:
+		break;
+	}
+
+	return action;
+}
+
+static int tcf_nat_dump(struct sk_buff *skb, struct tc_action *a,
+			int bind, int ref)
+{
+	unsigned char *b = skb_tail_pointer(skb);
+	struct tcf_nat *p = a->priv;
+	struct tc_nat *opt;
+	struct tcf_t t;
+	int s;
+
+	s = sizeof(*opt);
+
+	/* netlink spinlocks held above us - must use ATOMIC */
+	opt = kzalloc(s, GFP_ATOMIC);
+	if (unlikely(!opt))
+		return -ENOBUFS;
+
+	opt->old_addr = p->old_addr;
+	opt->new_addr = p->new_addr;
+	opt->mask = p->mask;
+	opt->flags = p->flags;
+
+	opt->index = p->tcf_index;
+	opt->action = p->tcf_action;
+	opt->refcnt = p->tcf_refcnt - ref;
+	opt->bindcnt = p->tcf_bindcnt - bind;
+
+	RTA_PUT(skb, TCA_NAT_PARMS, s, opt);
+	t.install = jiffies_to_clock_t(jiffies - p->tcf_tm.install);
+	t.lastuse = jiffies_to_clock_t(jiffies - p->tcf_tm.lastuse);
+	t.expires = jiffies_to_clock_t(p->tcf_tm.expires);
+	RTA_PUT(skb, TCA_NAT_TM, sizeof(t), &t);
+
+	kfree(opt);
+
+	return skb->len;
+
+rtattr_failure:
+	nlmsg_trim(skb, b);
+	kfree(opt);
+	return -1;
+}
+
+static struct tc_action_ops act_nat_ops = {
+	.kind		=	"nat",
+	.hinfo		=	&nat_hash_info,
+	.type		=	TCA_ACT_NAT,
+	.capab		=	TCA_CAP_NONE,
+	.owner		=	THIS_MODULE,
+	.act		=	tcf_nat,
+	.dump		=	tcf_nat_dump,
+	.cleanup	=	tcf_nat_cleanup,
+	.lookup		=	tcf_hash_search,
+	.init		=	tcf_nat_init,
+	.walk		=	tcf_generic_walker
+};
+
+MODULE_DESCRIPTION("Stateless NAT actions");
+MODULE_LICENSE("GPL");
+
+static int __init nat_init_module(void)
+{
+	return tcf_register_action(&act_nat_ops);
+}
+
+static void __exit nat_cleanup_module(void)
+{
+	tcf_unregister_action(&act_nat_ops);
+}
+
+module_init(nat_init_module);
+module_exit(nat_cleanup_module);

^ permalink raw reply related

* [PATCH] proper comment for loopback initialization order
From: Denis V. Lunev @ 2007-09-27 12:25 UTC (permalink / raw)
  To: davem; +Cc: dev, devel, netdev, containers

Loopback device is special. It should be initialized at the very
beginning.  Initialization order has been changed by
Eric W. Biederman <ebiederm@xmission.com> and this change is non-obvious
and important enough to add proper comment.

Signed-off-by: Denis V. Lunev <den@openvz.org>

--- ./drivers/net/loopback.c.loopcomment	2007-08-26 19:30:38.000000000 +0400
+++ ./drivers/net/loopback.c	2007-09-27 16:08:06.000000000 +0400
@@ -293,4 +293,6 @@ static int __init loopback_init(void)
 	return register_pernet_device(&loopback_net_ops);
 }
 
+/* Loopback is special. It should be initialized before any other network
+   device and network subsystem */
 fs_initcall(loopback_init);

^ permalink raw reply

* Re: [Devel] [PATCH 4/4] net: Make the loopback device per network namespace
From: Denis V. Lunev @ 2007-09-27 12:14 UTC (permalink / raw)
  To: Eric W. Biederman; +Cc: David Miller, Linux Containers, netdev
In-Reply-To: <m17imdrn8r.fsf_-_@ebiederm.dsl.xmission.com>

Eric W. Biederman wrote:
> This patch makes loopback_dev per network namespace.  Adding
> code to create a different loopback device for each network
> namespace and adding the code to free a loopback device
> when a network namespace exits.
> 
> This patch modifies all users the loopback_dev so they
> access it as init_net.loopback_dev, keeping all of the
> code compiling and working.  A later pass will be needed to
> update the users to use something other than the initial network
> namespace.

A pity that an important bit of explanation is missed. The
initialization of loopback_dev is moved from a chain of devices
(init_module) to a subsystem initialization to keep proper order, i.e.
we must be sure that the initialization order is correct.

Regards,
	Den

^ permalink raw reply

* Re: 2.6.23-rc8-mm2 - drivers/net/ibm_newemac/mal - broken
From: Kamalesh Babulal @ 2007-09-27 10:52 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, netdev, eugene.surovegin
In-Reply-To: <20070927022220.c76a7a6e.akpm@linux-foundation.org>

Andrew Morton wrote:
> ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.23-rc8/2.6.23-rc8-mm2/

Hi Andrew,

The drivers/net/ibm_newemac/mal seems to be broken with 2.6.23-rc8-mm2 also, it was
reported on 2.6.23-rc8-mm1 (http://lkml.org/lkml/2007/9/25/173).


-- 
Thanks & Regards,
Kamalesh Babulal,
Linux Technology Center,
IBM, ISTL.

^ permalink raw reply

* ax88796: add 93cx6 eeprom support
From: Magnus Damm @ 2007-09-27 10:51 UTC (permalink / raw)
  To: netdev; +Cc: Magnus Damm, lethal, jgarzik, akpm, ben-linux

ax88796: add 93cx6 eeprom support

This patch hooks up the 93cx6 eeprom code to the ax88796 driver and modifies
the ax88796 driver to read out the mac address from the eeprom. We need
this for the ax88796 on certain SuperH boards. The pin configuration used
to connect the eeprom to the ax88796 on these boards is the same as pointed
out by the ax88796 datasheet, so we can probably reuse this code for multiple
platforms in the future.

Signed-off-by: Magnus Damm <damm@igel.co.jp>
---

 This is a broken out version of the larger patch recently posted to netdev:
 http://www.mail-archive.com/netdev@vger.kernel.org/msg47278.html

 drivers/net/Kconfig          |    7 ++++++
 drivers/net/ax88796.c        |   49 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/eeprom_93cx6.h |    3 +-
 include/net/ax88796.h        |    1 
 4 files changed, 59 insertions(+), 1 deletion(-)

--- 0001/drivers/net/Kconfig
+++ work/drivers/net/Kconfig	2007-09-27 19:32:10.000000000 +0900
@@ -225,6 +225,13 @@ config AX88796
 	  AX88796 driver, using platform bus to provide
 	  chip detection and resources
 
+config AX88796_93CX6
+	bool "ASIX AX88796 external 93CX6 eeprom support"
+	depends on AX88796
+	select EEPROM_93CX6
+	help
+	  Select this if your platform comes with an external 93CX6 eeprom.
+
 config MACE
 	tristate "MACE (Power Mac ethernet) support"
 	depends on PPC_PMAC && PPC32
--- 0001/drivers/net/ax88796.c
+++ work/drivers/net/ax88796.c	2007-09-27 19:17:44.000000000 +0900
@@ -24,6 +24,7 @@
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
 #include <linux/mii.h>
+#include <linux/eeprom_93cx6.h>
 
 #include <net/ax88796.h>
 
@@ -582,6 +583,37 @@ static const struct ethtool_ops ax_ethto
 	.get_link		= ax_get_link,
 };
 
+#ifdef CONFIG_AX88796_93CX6
+static void ax_eeprom_register_read(struct eeprom_93cx6 *eeprom)
+{
+	struct ei_device *ei_local = eeprom->data;
+	u8 reg = ei_inb(ei_local->mem + AX_MEMR);
+
+	eeprom->reg_data_in = reg & AX_MEMR_EEI;
+	eeprom->reg_data_out = reg & AX_MEMR_EEO; /* Input pin */
+	eeprom->reg_data_clock = reg & AX_MEMR_EECLK;
+	eeprom->reg_chip_select = reg & AX_MEMR_EECS;
+}
+
+static void ax_eeprom_register_write(struct eeprom_93cx6 *eeprom)
+{
+	struct ei_device *ei_local = eeprom->data;
+	u8 reg = ei_inb(ei_local->mem + AX_MEMR);
+
+	reg &= ~(AX_MEMR_EEI | AX_MEMR_EECLK | AX_MEMR_EECS);
+
+	if (eeprom->reg_data_in)
+		reg |= AX_MEMR_EEI;
+	if (eeprom->reg_data_clock)
+		reg |= AX_MEMR_EECLK;
+	if (eeprom->reg_chip_select)
+		reg |= AX_MEMR_EECS;
+
+	ei_outb(reg, ei_local->mem + AX_MEMR);
+	udelay(10);
+}
+#endif
+
 /* setup code */
 
 static void ax_initial_setup(struct net_device *dev, struct ei_device *ei_local)
@@ -640,6 +672,23 @@ static int ax_init_dev(struct net_device
 		memcpy(dev->dev_addr,  SA_prom, 6);
 	}
 
+#ifdef CONFIG_AX88796_93CX6
+	if (first_init && ax->plat->flags & AXFLG_HAS_93CX6) {
+		unsigned char mac_addr[6];
+		struct eeprom_93cx6 eeprom;
+
+		eeprom.data = ei_local;
+		eeprom.register_read = ax_eeprom_register_read;
+		eeprom.register_write = ax_eeprom_register_write;
+		eeprom.width = PCI_EEPROM_WIDTH_93C56;
+
+		eeprom_93cx6_multiread(&eeprom, 0,
+				       (__le16 __force *)mac_addr,
+				       sizeof(mac_addr) >> 1);
+
+		memcpy(dev->dev_addr,  mac_addr, 6);
+	}
+#endif
 	if (ax->plat->wordlength == 2) {
 		/* We must set the 8390 for word mode. */
 		ei_outb(ax->plat->dcr_val, ei_local->mem + EN0_DCFG);
--- 0001/include/linux/eeprom_93cx6.h
+++ work/include/linux/eeprom_93cx6.h	2007-09-27 19:17:44.000000000 +0900
@@ -21,13 +21,14 @@
 /*
 	Module: eeprom_93cx6
 	Abstract: EEPROM reader datastructures for 93cx6 chipsets.
-	Supported chipsets: 93c46 & 93c66.
+	Supported chipsets: 93c46, 93c56 and 93c66.
  */
 
 /*
  * EEPROM operation defines.
  */
 #define PCI_EEPROM_WIDTH_93C46	6
+#define PCI_EEPROM_WIDTH_93C56	8
 #define PCI_EEPROM_WIDTH_93C66	8
 #define PCI_EEPROM_WIDTH_OPCODE	3
 #define PCI_EEPROM_WRITE_OPCODE	0x05
--- 0001/include/net/ax88796.h
+++ work/include/net/ax88796.h	2007-09-27 19:17:44.000000000 +0900
@@ -14,6 +14,7 @@
 
 #define AXFLG_HAS_EEPROM		(1<<0)
 #define AXFLG_MAC_FROMDEV		(1<<1)	/* device already has MAC */
+#define AXFLG_HAS_93CX6			(1<<2)	/* use eeprom_93cx6 driver */
 
 struct ax_plat_data {
 	unsigned int	 flags;

^ permalink raw reply

* Re: Linux networking implementation and packet capture
From: Alan Menegotto @ 2007-09-27 10:48 UTC (permalink / raw)
  To: Gaurav Aggarwal; +Cc: kernelnewbies, linux-net, netdev
In-Reply-To: <1a41e0840709262347m1fc5d99dge9aeb172b7fc610e@mail.gmail.com>

Gaurav Aggarwal escreveu:
> Hi,
>
> I am trying to understand the implementation of linux 2.4 and linux 
> 2.6's networking (IPV4) . Can anyone give me some idea/pointers about 
> some of the good resources/whitepapers available in the market to 
> understand the same. If there is any document that mention the changes 
> between the implementation of networking in 2.4 & 2.6
>
> I am also trying to write a simple program(preferably a userspace 
> application) which captures all the incoming and outgoing packets of a 
> particular machine (preferably at PREROUTING stage), then according to 
> the SRC/DST addresses, changes the IP address of some of the packets 
> and then reinject it back into the local IP stack. I am able to do 
> that in 2.4 kernel by using libipq and ip_tables but that prog is not 
> running in 2.6 kernel. (It hits at ip_route_BUG). Any idea or code 
> snippet will be really appreciated.
>
> -- 
> Regards,
> Gaurav Aggarwal
>
You may also see the information on the linux-net wiki: 
http://linux-net.osdl.org/index.php/Main_Page.

Also, read some threads on netdev list. It has a lot of information 
there too.





-- 


--
Best Regards

Alan Menegotto


^ permalink raw reply

* [PATCH] fixed broken bootp compilation
From: Denis V. Lunev @ 2007-09-27 10:46 UTC (permalink / raw)
  To: davem; +Cc: shemminger, netdev

Compilation fix. Extra bracket removed.
Broken by "[NET]: Wrap netdevice hardware header creation" from
Stephen Hemminger <shemminger@linux-foundation.org>

Signed-off-by: Denis V. Lunev <den@openvz.org>

--- ./net/ipv4/ipconfig.c.compile	2007-09-27 13:32:35.000000000 +0400
+++ ./net/ipv4/ipconfig.c	2007-09-27 14:36:19.000000000 +0400
@@ -758,7 +758,7 @@ static void __init ic_bootp_send_if(stru
 	skb->dev = dev;
 	skb->protocol = htons(ETH_P_IP);
 	if (dev_hard_header(skb, dev, ntohs(skb->protocol),
-			    dev->broadcast, dev->dev_addr, skb->len) < 0) ||
+			    dev->broadcast, dev->dev_addr, skb->len) < 0 ||
 	    dev_queue_xmit(skb) < 0)
 		printk("E");
 }

^ permalink raw reply

* Re: [PATCH 3/4] net ipv4: When possible test for IFF_LOOPBACK and not dev == loopback_dev
From: Daniel Lezcano @ 2007-09-27 10:34 UTC (permalink / raw)
  To: Eric W. Biederman; +Cc: David Miller, Linux Containers, netdev, urs
In-Reply-To: <m1bqbprnc2.fsf_-_@ebiederm.dsl.xmission.com>

Eric W. Biederman wrote:
> Now that multiple loopback devices are becoming possible it makes
> the code a little cleaner and more maintainable to test if a deivice
> is th a loopback device by testing dev->flags & IFF_LOOPBACK instead
> of dev == loopback_dev.
> 
> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>


Urs Thuermann posted the patch:

	[PATCH 5/7] CAN: Add virtual CAN netdevice driver

This network driver set its flag to IFF_LOOPBACK for testing.
Is it possible this can be a collision with your patch ?


^ permalink raw reply

* Re: [PKT_SCHED]: Add stateless NAT
From: Herbert Xu @ 2007-09-27 10:29 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: David S. Miller, netdev, Alexey Kuznetsov, jamal
In-Reply-To: <20070927100753.GA22323@2ka.mipt.ru>

On Thu, Sep 27, 2007 at 02:07:53PM +0400, Evgeniy Polyakov wrote:
>
> It forces all inpuit/pre/post/forward hooks to be enbled not as a direct
> function call, but as additional lookups. And unability to remove
> netfilter from config. And just because of couple of checksum helpers...

I'm certainly not against patches moving that code out of
netfilter.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [PKT_SCHED]: Add stateless NAT
From: Evgeniy Polyakov @ 2007-09-27 10:07 UTC (permalink / raw)
  To: Herbert Xu; +Cc: David S. Miller, netdev, Alexey Kuznetsov, jamal
In-Reply-To: <20070927093358.GA15357@gondor.apana.org.au>

On Thu, Sep 27, 2007 at 05:33:58PM +0800, Herbert Xu (herbert@gondor.apana.org.au) wrote:
> > > +config NET_ACT_NAT
> > > +        tristate "Stateless NAT"
> > > +        depends on NET_CLS_ACT
> > > +        select NETFILTER
> > 
> > Argh... People usually do not understand such jokes :)
> > What about not using netfilter helpers and just move them to the
> > accessible header so that no additional slow path would ever be enabled?
> 
> Sure.  However, as it is it's just including the netfilter core
> which does not mean the inclusion of connection trakcing.  It's
> only connection tracking that *may* (so don't flame me for this :)
> pose a scalability problem.

It forces all inpuit/pre/post/forward hooks to be enbled not as a direct
function call, but as additional lookups. And unability to remove
netfilter from config. And just because of couple of checksum helpers...

> > > +++ b/net/sched/act_nat.c
> > ...
> > > +#define NAT_TAB_MASK	15
> > 
> > This really wants to be configurable at least via module parameter.
> > 
> > > +static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1];
> > > +static u32 nat_idx_gen;
> > > +static DEFINE_RWLOCK(nat_lock);
> > 
> > > +static struct tcf_hashinfo nat_hash_info = {
> > > +	.htab	=	tcf_nat_ht,
> > > +	.hmask	=	NAT_TAB_MASK,
> > > +	.lock	=	&nat_lock,
> > > +};
> > 
> > When I read this I swear I heard 'I want to be RCU'.
> > But that is another task.
> 
> Yes there are a lot of clean-up's that can be done for all
> actions.  You're most welcome to send patches in this area.
> 
> > > +		tcph = (void *)(skb_network_header(skb) + ihl);
> > 
> > Were you too lazy to write struct tcphdr here and in other places? :)
> 
> Unfortunately it doesn't work.  For prerouting, we've not
> entered the IP stack yet so the transport header isn't set.

I meant instead of dereferencing to void * it should be struct tcphdr *.

-- 
	Evgeniy Polyakov

^ permalink raw reply

* Re: [PKT_SCHED]: Add stateless NAT
From: Herbert Xu @ 2007-09-27  9:33 UTC (permalink / raw)
  To: Evgeniy Polyakov; +Cc: David S. Miller, netdev, Alexey Kuznetsov, jamal
In-Reply-To: <20070927092512.GD29329@2ka.mipt.ru>

On Thu, Sep 27, 2007 at 01:25:12PM +0400, Evgeniy Polyakov wrote:
>
> Couple of comments below.

Thanks Evgeniey :)

> > --- a/net/sched/Kconfig
> > +++ b/net/sched/Kconfig
> > @@ -447,6 +447,17 @@ config NET_ACT_IPT
> >  	  To compile this code as a module, choose M here: the
> >  	  module will be called ipt.
> >
> > +config NET_ACT_NAT
> > +        tristate "Stateless NAT"
> > +        depends on NET_CLS_ACT
> > +        select NETFILTER
> 
> Argh... People usually do not understand such jokes :)
> What about not using netfilter helpers and just move them to the
> accessible header so that no additional slow path would ever be enabled?

Sure.  However, as it is it's just including the netfilter core
which does not mean the inclusion of connection trakcing.  It's
only connection tracking that *may* (so don't flame me for this :)
pose a scalability problem.

> > +        ---help---
> > +	  Say Y here to do stateless NAT on IPv4 packets.  You should use
> > +	  netfilter for NAT unless you know what you are doing.
> > +
> > +	  To compile this code as a module, choose M here: the
> > +	  module will be called ipt.
> > +
> 
> Modile will be called 'nat' I believe.

Good catch, now you know where I copied it from :)

> > +++ b/net/sched/act_nat.c
> ...
> > +#define NAT_TAB_MASK	15
> 
> This really wants to be configurable at least via module parameter.
> 
> > +static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1];
> > +static u32 nat_idx_gen;
> > +static DEFINE_RWLOCK(nat_lock);
> 
> > +static struct tcf_hashinfo nat_hash_info = {
> > +	.htab	=	tcf_nat_ht,
> > +	.hmask	=	NAT_TAB_MASK,
> > +	.lock	=	&nat_lock,
> > +};
> 
> When I read this I swear I heard 'I want to be RCU'.
> But that is another task.

Yes there are a lot of clean-up's that can be done for all
actions.  You're most welcome to send patches in this area.

> > +		tcph = (void *)(skb_network_header(skb) + ihl);
> 
> Were you too lazy to write struct tcphdr here and in other places? :)

Unfortunately it doesn't work.  For prerouting, we've not
entered the IP stack yet so the transport header isn't set.

Cheers,
-- 
Visit Openswan at http://www.openswan.org/
Email: Herbert Xu ~{PmV>HI~} <herbert@gondor.apana.org.au>
Home Page: http://gondor.apana.org.au/~herbert/
PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt

^ permalink raw reply

* Re: [PKT_SCHED]: Add stateless NAT
From: Evgeniy Polyakov @ 2007-09-27  9:25 UTC (permalink / raw)
  To: Herbert Xu; +Cc: David S. Miller, netdev, Alexey Kuznetsov, jamal
In-Reply-To: <20070927073446.GA14643@gondor.apana.org.au>

Hi Herbert.

On Thu, Sep 27, 2007 at 03:34:47PM +0800, Herbert Xu (herbert@gondor.apana.org.au) wrote:
> Hi:
> 
> [PKT_SCHED]: Add stateless NAT
> 
> Stateless NAT is useful in controlled environments where restrictions are
> placed on through traffic such that we don't need connection tracking to
> correctly NAT protocol-specific data.

Couple of comments below.
> --- a/net/sched/Kconfig
> +++ b/net/sched/Kconfig
> @@ -447,6 +447,17 @@ config NET_ACT_IPT
>  	  To compile this code as a module, choose M here: the
>  	  module will be called ipt.
>
> +config NET_ACT_NAT
> +        tristate "Stateless NAT"
> +        depends on NET_CLS_ACT
> +        select NETFILTER

Argh... People usually do not understand such jokes :)
What about not using netfilter helpers and just move them to the
accessible header so that no additional slow path would ever be enabled?

> +        ---help---
> +	  Say Y here to do stateless NAT on IPv4 packets.  You should use
> +	  netfilter for NAT unless you know what you are doing.
> +
> +	  To compile this code as a module, choose M here: the
> +	  module will be called ipt.
> +

Modile will be called 'nat' I believe.

> +++ b/net/sched/act_nat.c
...
> +#define NAT_TAB_MASK	15

This really wants to be configurable at least via module parameter.

> +static struct tcf_common *tcf_nat_ht[NAT_TAB_MASK + 1];
> +static u32 nat_idx_gen;
> +static DEFINE_RWLOCK(nat_lock);

> +static struct tcf_hashinfo nat_hash_info = {
> +	.htab	=	tcf_nat_ht,
> +	.hmask	=	NAT_TAB_MASK,
> +	.lock	=	&nat_lock,
> +};

When I read this I swear I heard 'I want to be RCU'.
But that is another task.

> +static int tcf_nat(struct sk_buff *skb, struct tc_action *a,
> +		   struct tcf_result *res)
> +{
> +	struct tcf_nat *p = a->priv;
> +	struct iphdr *iph;
> +	__be32 old_addr;
> +	__be32 new_addr;
> +	__be32 mask;
> +	__be32 addr;
> +	int egress;
> +	int action;
> +	int ihl;
> +
> +	spin_lock(&p->tcf_lock);
> +
> +	p->tcf_tm.lastuse = jiffies;
> +	old_addr = p->old_addr;
> +	new_addr = p->new_addr;
> +	mask = p->mask;
> +	egress = p->flags & TCA_NAT_FLAG_EGRESS;
> +	action = p->tcf_action;
> +
> +	p->tcf_bstats.bytes += skb->len;
> +	p->tcf_bstats.packets++;
> +
> +	spin_unlock(&p->tcf_lock);
> +
> +	if (!pskb_may_pull(skb, sizeof(*iph)))
> +		return TC_ACT_SHOT;
> +
> +	iph = ip_hdr(skb);
> +
> +	if (egress)
> +		addr = iph->saddr;
> +	else
> +		addr = iph->daddr;
> +
> +	if (!((old_addr ^ addr) & mask)) {
> +		if (skb_cloned(skb) &&
> +		    !skb_clone_writable(skb, sizeof(*iph)) &&
> +		    pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
> +			return TC_ACT_SHOT;
> +
> +		new_addr &= mask;
> +		new_addr |= addr & ~mask;
> +
> +		/* Rewrite IP header */
> +		iph = ip_hdr(skb);
> +		if (egress)
> +			iph->saddr = new_addr;
> +		else
> +			iph->daddr = new_addr;
> +
> +		nf_csum_replace4(&iph->check, addr, new_addr);
> +	}
> +
> +	ihl = iph->ihl * 4;
> +
> +	/* It would be nice to share code with stateful NAT. */
> +	switch (iph->frag_off & htons(IP_OFFSET) ? 0 : iph->protocol) {
> +	case IPPROTO_TCP:
> +	{
> +		struct tcphdr *tcph;
> +
> +		if (!pskb_may_pull(skb, ihl + sizeof(*tcph)) ||
> +		    (skb_cloned(skb) &&
> +		     !skb_clone_writable(skb, ihl + sizeof(*tcph)) &&
> +		     pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
> +			return TC_ACT_SHOT;
> +
> +		tcph = (void *)(skb_network_header(skb) + ihl);

Were you too lazy to write struct tcphdr here and in other places? :)


-- 
	Evgeniy Polyakov

^ permalink raw reply

* Re: [PATCH] sky2: sky2 FE+ receive status workaround
From: Jochen Voß @ 2007-09-27  8:14 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: Jeff Garzik, netdev
In-Reply-To: <20070926175847.706025d0@freepuppy.rosehill>

Hi Stephen,

On 27 Sep 2007, at 01:58, Stephen Hemminger wrote:
> +	/* This chip has hardware problems that generates bogus status.
> +	 * So do only marginal checking and expect higher level protocols
> +	 * to handle crap frames.
> +	 */
> +	if (sky2->hw->chip_id == CHIP_ID_YUKON_FE_P &&
> +	    sky2->hw->chip_rev == CHIP_REV_YU_FE2_A0 &&
> +	    length != count)
> +		goto okay;

Shouldn't the condition be "length == count"?

I hope this helps,
Jochen
--
http://seehuhn.de/



^ permalink raw reply

* Re: [PATCH] net: Add network namespace clone & unshare support.
From: Cedric Le Goater @ 2007-09-27  8:51 UTC (permalink / raw)
  To: Eric W. Biederman; +Cc: David Miller, Linux Containers, netdev
In-Reply-To: <m1tzphrnq5.fsf@ebiederm.dsl.xmission.com>

Eric W. Biederman wrote:
> This patch allows you to create a new network namespace
> using sys_clone, or sys_unshare.
> 
> As the network namespace is still experimental and under development
> clone and unshare support is only made available when CONFIG_NET_NS is
> selected at compile time.
> 
> As this patch introduces network namespace support into code paths
> that exist when the CONFIG_NET is not selected there are a few
> additions made to net_namespace.h to allow a few more functions
> to be used when the networking stack is not compiled in.
> 
> Signed-off-by: Eric W. Biederman <ebiederm@xmission.com>
> ---
>  include/linux/sched.h       |    1 +
>  include/net/net_namespace.h |   18 ++++++++++++++++++
>  kernel/fork.c               |    3 ++-
>  kernel/nsproxy.c            |   15 +++++++++++++--
>  net/Kconfig                 |    8 ++++++++
>  net/core/net_namespace.c    |   43 +++++++++++++++++++++++++++++++++++++++++--
>  6 files changed, 83 insertions(+), 5 deletions(-)
> 
> diff --git a/include/linux/sched.h b/include/linux/sched.h
> index a01ac6d..e10a0a8 100644
> --- a/include/linux/sched.h
> +++ b/include/linux/sched.h
> @@ -27,6 +27,7 @@
>  #define CLONE_NEWUTS		0x04000000	/* New utsname group? */
>  #define CLONE_NEWIPC		0x08000000	/* New ipcs */
>  #define CLONE_NEWUSER		0x10000000	/* New user namespace */
> +#define CLONE_NEWNET		0x20000000	/* New network namespace */

This new flag is going to conflict with the pid namespace flag 
CLONE_NEWPID in -mm. It might be worth changing it to:

#define CLONE_NEWNET		0x40000000

The changes in nxproxy.c and fork.c will also conflict but I don't 
think we can do much about it for now.

C. 

>  /*
>   * Scheduling policies
> diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
> index ac8f830..3ea4194 100644
> --- a/include/net/net_namespace.h
> +++ b/include/net/net_namespace.h
> @@ -38,11 +38,23 @@ extern struct net init_net;
> 
>  extern struct list_head net_namespace_list;
> 
> +#ifdef CONFIG_NET
> +extern struct net *copy_net_ns(unsigned long flags, struct net *net_ns);
> +#else
> +static inline struct net *copy_net_ns(unsigned long flags, struct net *net_ns)
> +{
> +	/* There is nothing to copy so this is a noop */
> +	return net_ns;
> +}
> +#endif
> +
>  extern void __put_net(struct net *net);
> 
>  static inline struct net *get_net(struct net *net)
>  {
> +#ifdef CONFIG_NET
>  	atomic_inc(&net->count);
> +#endif
>  	return net;
>  }
> 
> @@ -60,19 +72,25 @@ static inline struct net *maybe_get_net(struct net *net)
> 
>  static inline void put_net(struct net *net)
>  {
> +#ifdef CONFIG_NET
>  	if (atomic_dec_and_test(&net->count))
>  		__put_net(net);
> +#endif
>  }
> 
>  static inline struct net *hold_net(struct net *net)
>  {
> +#ifdef CONFIG_NET
>  	atomic_inc(&net->use_count);
> +#endif
>  	return net;
>  }
> 
>  static inline void release_net(struct net *net)
>  {
> +#ifdef CONFIG_NET
>  	atomic_dec(&net->use_count);
> +#endif
>  }
> 
>  extern void net_lock(void);
> diff --git a/kernel/fork.c b/kernel/fork.c
> index 33f12f4..5e67f90 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -1608,7 +1608,8 @@ asmlinkage long sys_unshare(unsigned long unshare_flags)
>  	err = -EINVAL;
>  	if (unshare_flags & ~(CLONE_THREAD|CLONE_FS|CLONE_NEWNS|CLONE_SIGHAND|
>  				CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
> -				CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER))
> +				CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWUSER|
> +				CLONE_NEWNET))
>  		goto bad_unshare_out;
> 
>  	if ((err = unshare_thread(unshare_flags)))
> diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
> index a4fb7d4..f1decd2 100644
> --- a/kernel/nsproxy.c
> +++ b/kernel/nsproxy.c
> @@ -20,6 +20,7 @@
>  #include <linux/mnt_namespace.h>
>  #include <linux/utsname.h>
>  #include <linux/pid_namespace.h>
> +#include <net/net_namespace.h>
> 
>  static struct kmem_cache *nsproxy_cachep;
> 
> @@ -98,8 +99,17 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
>  		goto out_user;
>  	}
> 
> +	new_nsp->net_ns = copy_net_ns(flags, tsk->nsproxy->net_ns);
> +	if (IS_ERR(new_nsp->net_ns)) {
> +		err = PTR_ERR(new_nsp->net_ns);
> +		goto out_net;
> +	}
> +
>  	return new_nsp;
> 
> +out_net:
> +	if (new_nsp->user_ns)
> +		put_user_ns(new_nsp->user_ns);
>  out_user:
>  	if (new_nsp->pid_ns)
>  		put_pid_ns(new_nsp->pid_ns);
> @@ -132,7 +142,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
> 
>  	get_nsproxy(old_ns);
> 
> -	if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER)))
> +	if (!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWNET)))
>  		return 0;
> 
>  	if (!capable(CAP_SYS_ADMIN)) {
> @@ -164,6 +174,7 @@ void free_nsproxy(struct nsproxy *ns)
>  		put_pid_ns(ns->pid_ns);
>  	if (ns->user_ns)
>  		put_user_ns(ns->user_ns);
> +	put_net(ns->net_ns);
>  	kmem_cache_free(nsproxy_cachep, ns);
>  }
> 
> @@ -177,7 +188,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
>  	int err = 0;
> 
>  	if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
> -			       CLONE_NEWUSER)))
> +			       CLONE_NEWUSER | CLONE_NEWNET)))
>  		return 0;
> 
>  	if (!capable(CAP_SYS_ADMIN))
> diff --git a/net/Kconfig b/net/Kconfig
> index cdba08c..ab4e6da 100644
> --- a/net/Kconfig
> +++ b/net/Kconfig
> @@ -27,6 +27,14 @@ if NET
> 
>  menu "Networking options"
> 
> +config NET_NS
> +	bool "Network namespace support"
> +	default n
> +	depends on EXPERIMENTAL && !SYSFS
> +	help
> +	  Allow user space to create what appear to be multiple instances
> +	  of the network stack.
> +
>  source "net/packet/Kconfig"
>  source "net/unix/Kconfig"
>  source "net/xfrm/Kconfig"
> diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
> index 0e6cb02..e478e35 100644
> --- a/net/core/net_namespace.c
> +++ b/net/core/net_namespace.c
> @@ -4,6 +4,7 @@
>  #include <linux/slab.h>
>  #include <linux/list.h>
>  #include <linux/delay.h>
> +#include <linux/sched.h>
>  #include <net/net_namespace.h>
> 
>  /*
> @@ -32,12 +33,10 @@ void net_unlock(void)
>  	mutex_unlock(&net_list_mutex);
>  }
> 
> -#if 0
>  static struct net *net_alloc(void)
>  {
>  	return kmem_cache_alloc(net_cachep, GFP_KERNEL);
>  }
> -#endif
> 
>  static void net_free(struct net *net)
>  {
> @@ -128,6 +127,46 @@ out_undo:
>  	goto out;
>  }
> 
> +struct net *copy_net_ns(unsigned long flags, struct net *old_net)
> +{
> +	struct net *new_net = NULL;
> +	int err;
> +
> +	get_net(old_net);
> +
> +	if (!(flags & CLONE_NEWNET))
> +		return old_net;
> +
> +#ifndef CONFIG_NET_NS
> +	return ERR_PTR(-EINVAL);
> +#endif
> +
> +	err = -ENOMEM;
> +	new_net = net_alloc();
> +	if (!new_net)
> +		goto out;
> +
> +	mutex_lock(&net_mutex);
> +	err = setup_net(new_net);
> +	if (err)
> +		goto out_unlock;
> +
> +	net_lock();
> +	list_add_tail(&new_net->list, &net_namespace_list);
> +	net_unlock();
> +
> +
> +out_unlock:
> +	mutex_unlock(&net_mutex);
> +out:
> +	put_net(old_net);
> +	if (err) {
> +		net_free(new_net);
> +		new_net = ERR_PTR(err);
> +	}
> +	return new_net;
> +}
> +
>  static int __init net_ns_init(void)
>  {
>  	int err;


^ permalink raw reply

* Re: [PATCH v3 2/2][BNX2]: Add iSCSI support to BNX2 devices.
From: FUJITA Tomonori @ 2007-09-27  8:46 UTC (permalink / raw)
  To: jeff
  Cc: benh, tomof, hare, open-iscsi, hch, davem, mchristi, netdev,
	anilgv, talm, lusinsky, uri, fujita.tomonori, jens.axboe,
	James.Bottomley, linux-scsi
In-Reply-To: <46FB6837.7040308@garzik.org>

CC'ed Jens, James, and linux-scsi again.

On Thu, 27 Sep 2007 04:22:15 -0400
Jeff Garzik <jeff@garzik.org> wrote:

> Benjamin Herrenschmidt wrote:
> > On Thu, 2007-09-27 at 03:49 -0400, Jeff Garzik wrote:
> >> Benjamin Herrenschmidt wrote:
> >>> On Thu, 2007-09-27 at 03:31 -0400, Jeff Garzik wrote:
> >>>> A key problem I was hoping would be solved with your work here was
> >>>> the 
> >>>> elimination of that post dma_map_sg() split.
> >>>>
> >>>> If I understood James and Ben correctly, one of the key problems was 
> >>>> always in communicating libata's segment boundary needs to the IOMMU
> >>>> layers?
> >>> Yup. If we can put some constraint in struct device that the dma mapping
> >>> code can then look at ... we also need to ensure that what's passed in
> >>> for DMA'ing already matches those constraints as well since no-iommu
> >>> platforms will basically just keep the dma table as-is.
> >> That's a good point...  no-iommu platforms would need to be updated to 
> >> do the split for me.  I suppose we can steal that code from swiotlb or 
> >> somewhere.
> > 
> > Doing the split means being able to grow the sglist... which the dma_*
> > calls can't do at least not in their current form.
> 
> IMO one straightforward approach is for the struct scatterlist owner to 
> provide a table large enough to accomodate the possible splits (perhaps 
> along with communicate that table's max size to the IOMMU/dma layers).

As I said in another mail, the block layer and scsi-ml work properly,
I think. So there is no need to split sg lists for no-iommu platforms.

We need to fix only iommu code merge sglists (already done) for the
segment size restriction but we need to fix all iommu code and swiotlb
for the segment boundary restriction. Splitting sg list might be
useful for the case that iommu can't find a proper boundary memory
area. But I think that it rarely happens (and there are few llds has
the boundary restriction).

^ permalink raw reply

* Re: [PATCH v3 2/2][BNX2]: Add iSCSI support to BNX2 devices.
From: Jeff Garzik @ 2007-09-27  8:23 UTC (permalink / raw)
  To: FUJITA Tomonori
  Cc: hare, open-iscsi, hch, davem, mchristi, netdev, anilgv, talm,
	lusinsky, uri, fujita.tomonori, benh, jens.axboe, James.Bottomley,
	linux-scsi
In-Reply-To: <20070926075800T.tomof@acm.org>

FUJITA Tomonori wrote:
> CC'ed Jens, James, and linux-scsi.
> 
> On Thu, 27 Sep 2007 03:31:55 -0400
> Jeff Garzik <jeff@garzik.org> wrote:
> 
>> FUJITA Tomonori wrote:
>>> Yeah, we could nicely handle lld's restrictions (especially with
>>> stacking devices). But iommu code needs only max_segment_size and
>>> seg_boundary_mask, right? If so, the first simple approach to add two
>>> values to device structure is not so bad, I think.
>> (replying to slightly older email in the thread)
>> (added benh, since we've discussed this issue in the past)
>>
>> dumb question, what happened to seg_boundary_mask?
> 
> I'll work on it too after finishing max_seg_size.
> 
> 
>> If you look at drivers/ata/libata-core.c:ata_fill_sg(), you will note 
>> that we split s/g segments after DMA-mapping.  Looking at libata LLDD's, 
>> you will also note judicious use of ATA_DMA_BOUNDARY (0xffff).
> 
> I know the workaround since I fixed libata's sg chaining patch.
> 
> 
>> It was drilled into my head by James and benh that I cannot rely on the 
>> DMA boundary + block/scsi + dma_map_sg() to ensure that my S/G segments 
>> never cross a 64K boundary, a legacy IDE requirement.  Thus the 
>> additional code in ata_fill_sg() to split S/G segments straddling 64K, 
>> in addition to setting dma boundary to 0xffff.
> 
> I think that the block layer can handle both max_segment_size and
> seg_boundary_mask properly (and SCSI-ml just uses the block layer). So
> if we fix iommu, then we can remove a workaround to fix sg lists in
> llds.
> 
> 
>> A key problem I was hoping would be solved with your work here was the 
>> elimination of that post dma_map_sg() split.
> 
> Yeah, that's my goal too.

Great :)  Well, I'm generally happy with your max-seg-size stuff (sans 
the minor nits I pointed out in another email).

Thanks for pursuing this,

	Jeff




^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox