Netdev List
 help / color / mirror / Atom feed
* [PATCH 3/5] netfilter: xtables: add an IPv6 capable version of the ECN match
From: pablo @ 2011-12-28 13:39 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, jengelh, kaber
In-Reply-To: <1325079573-6120-1-git-send-email-pablo@netfilter.org>

From: Patrick McHardy <kaber@trash.net>

References: http://www.spinics.net/lists/netfilter-devel/msg18875.html

Augment xt_ecn by facilities to match on IPv6 packets' DSCP/TOS field
similar to how it is already done for the IPv4 packet field.

Signed-off-by: Jan Engelhardt <jengelh@medozas.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/xt_ecn.c |  106 ++++++++++++++++++++++++++++++++++++------------
 1 files changed, 80 insertions(+), 26 deletions(-)

diff --git a/net/netfilter/xt_ecn.c b/net/netfilter/xt_ecn.c
index 3ebb3dc..6ccc35d 100644
--- a/net/netfilter/xt_ecn.c
+++ b/net/netfilter/xt_ecn.c
@@ -1,6 +1,8 @@
-/* IP tables module for matching the value of the IPv4 and TCP ECN bits
+/*
+ * Xtables module for matching the value of the IPv4/IPv6 and TCP ECN bits
  *
  * (C) 2002 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2011 Patrick McHardy <kaber@trash.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -17,32 +19,25 @@
 #include <linux/netfilter/x_tables.h>
 #include <linux/netfilter/xt_ecn.h>
 #include <linux/netfilter_ipv4/ip_tables.h>
+#include <linux/netfilter_ipv6/ip6_tables.h>
 
 MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
-MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match for IPv4");
+MODULE_DESCRIPTION("Xtables: Explicit Congestion Notification (ECN) flag match");
 MODULE_LICENSE("GPL");
 MODULE_ALIAS("ipt_ecn");
+MODULE_ALIAS("ip6t_ecn");
 
-static inline bool match_ip(const struct sk_buff *skb,
-			    const struct xt_ecn_info *einfo)
-{
-	return ((ip_hdr(skb)->tos & XT_ECN_IP_MASK) == einfo->ip_ect) ^
-	       !!(einfo->invert & XT_ECN_OP_MATCH_IP);
-}
-
-static inline bool match_tcp(const struct sk_buff *skb,
-			     const struct xt_ecn_info *einfo,
-			     bool *hotdrop)
+static bool match_tcp(const struct sk_buff *skb, struct xt_action_param *par)
 {
+	const struct xt_ecn_info *einfo = par->matchinfo;
 	struct tcphdr _tcph;
 	const struct tcphdr *th;
 
 	/* In practice, TCP match does this, so can't fail.  But let's
 	 * be good citizens.
 	 */
-	th = skb_header_pointer(skb, ip_hdrlen(skb), sizeof(_tcph), &_tcph);
+	th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
 	if (th == NULL) {
-		*hotdrop = false;
 		return false;
 	}
 
@@ -69,7 +64,14 @@ static inline bool match_tcp(const struct sk_buff *skb,
 	return true;
 }
 
-static bool ecn_mt(const struct sk_buff *skb, struct xt_action_param *par)
+static inline bool match_ip(const struct sk_buff *skb,
+			    const struct xt_ecn_info *einfo)
+{
+	return ((ip_hdr(skb)->tos & XT_ECN_IP_MASK) == einfo->ip_ect) ^
+	       !!(einfo->invert & XT_ECN_OP_MATCH_IP);
+}
+
+static bool ecn_mt4(const struct sk_buff *skb, struct xt_action_param *par)
 {
 	const struct xt_ecn_info *info = par->matchinfo;
 
@@ -78,14 +80,14 @@ static bool ecn_mt(const struct sk_buff *skb, struct xt_action_param *par)
 			return false;
 
 	if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR)) {
-		if (!match_tcp(skb, info, &par->hotdrop))
+		if (!match_tcp(skb, par))
 			return false;
 	}
 
 	return true;
 }
 
-static int ecn_mt_check(const struct xt_mtchk_param *par)
+static int ecn_mt_check4(const struct xt_mtchk_param *par)
 {
 	const struct xt_ecn_info *info = par->matchinfo;
 	const struct ipt_ip *ip = par->entryinfo;
@@ -105,23 +107,75 @@ static int ecn_mt_check(const struct xt_mtchk_param *par)
 	return 0;
 }
 
-static struct xt_match ecn_mt_reg __read_mostly = {
-	.name		= "ecn",
-	.family		= NFPROTO_IPV4,
-	.match		= ecn_mt,
-	.matchsize	= sizeof(struct xt_ecn_info),
-	.checkentry	= ecn_mt_check,
-	.me		= THIS_MODULE,
+static inline bool match_ipv6(const struct sk_buff *skb,
+			      const struct xt_ecn_info *einfo)
+{
+	return (((ipv6_hdr(skb)->flow_lbl[0] >> 4) & XT_ECN_IP_MASK) ==
+	        einfo->ip_ect) ^
+	       !!(einfo->invert & XT_ECN_OP_MATCH_IP);
+}
+
+static bool ecn_mt6(const struct sk_buff *skb, struct xt_action_param *par)
+{
+	const struct xt_ecn_info *info = par->matchinfo;
+
+	if (info->operation & XT_ECN_OP_MATCH_IP && !match_ipv6(skb, info))
+		return false;
+
+	if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
+	    !match_tcp(skb, par))
+		return false;
+
+	return true;
+}
+
+static int ecn_mt_check6(const struct xt_mtchk_param *par)
+{
+	const struct xt_ecn_info *info = par->matchinfo;
+	const struct ip6t_ip6 *ip = par->entryinfo;
+
+	if (info->operation & XT_ECN_OP_MATCH_MASK)
+		return -EINVAL;
+
+	if (info->invert & XT_ECN_OP_MATCH_MASK)
+		return -EINVAL;
+
+	if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
+	    (ip->proto != IPPROTO_TCP || ip->invflags & IP6T_INV_PROTO)) {
+		pr_info("cannot match TCP bits in rule for non-tcp packets\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct xt_match ecn_mt_reg[] __read_mostly = {
+	{
+		.name		= "ecn",
+		.family		= NFPROTO_IPV4,
+		.match		= ecn_mt4,
+		.matchsize	= sizeof(struct xt_ecn_info),
+		.checkentry	= ecn_mt_check4,
+		.me		= THIS_MODULE,
+	},
+	{
+		.name		= "ecn",
+		.family		= NFPROTO_IPV6,
+		.match		= ecn_mt6,
+		.matchsize	= sizeof(struct xt_ecn_info),
+		.checkentry	= ecn_mt_check6,
+		.me		= THIS_MODULE,
+	},
 };
 
 static int __init ecn_mt_init(void)
 {
-	return xt_register_match(&ecn_mt_reg);
+	return xt_register_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
 }
 
 static void __exit ecn_mt_exit(void)
 {
-	xt_unregister_match(&ecn_mt_reg);
+	xt_unregister_matches(ecn_mt_reg, ARRAY_SIZE(ecn_mt_reg));
 }
 
 module_init(ecn_mt_init);
-- 
1.7.7.3


^ permalink raw reply related

* [PATCH 4/5] unix_diag: Report memory usage info
From: Pavel Emelyanov @ 2011-12-28 13:39 UTC (permalink / raw)
  To: David Miller, Linux Netdev List

Symmetrical to inet sockets, but unix only have two meaningful
values -- the wmem allocation and sndbuf.

Signed-off-by: Pavel Emelyanov <xemul@parallels.com>

---
 include/linux/unix_diag.h |    7 +++++++
 net/unix/diag.c           |   18 ++++++++++++++++++
 2 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/include/linux/unix_diag.h b/include/linux/unix_diag.h
index 3f7afb0..d62e562 100644
--- a/include/linux/unix_diag.h
+++ b/include/linux/unix_diag.h
@@ -16,6 +16,7 @@ struct unix_diag_req {
 #define UDIAG_SHOW_PEER		0x00000004	/* show peer socket info */
 #define UDIAG_SHOW_ICONS	0x00000008	/* show pending connections */
 #define UDIAG_SHOW_RQLEN	0x00000010	/* show skb receive queue len */
+#define UDIAG_SHOW_MEMINFO	0x00000020
 
 struct unix_diag_msg {
 	__u8	udiag_family;
@@ -33,6 +34,7 @@ enum {
 	UNIX_DIAG_PEER,
 	UNIX_DIAG_ICONS,
 	UNIX_DIAG_RQLEN,
+	UNIX_DIAG_MEMINFO,
 
 	UNIX_DIAG_MAX,
 };
@@ -42,4 +44,9 @@ struct unix_diag_vfs {
 	__u32	udiag_vfs_dev;
 };
 
+struct unix_diag_meminfo {
+	__u32	udiag_len;
+	__u32	udiag_limit;
+};
+
 #endif
diff --git a/net/unix/diag.c b/net/unix/diag.c
index c5bdbcb..dbae8a4 100644
--- a/net/unix/diag.c
+++ b/net/unix/diag.c
@@ -108,6 +108,20 @@ rtattr_failure:
 	return -EMSGSIZE;
 }
 
+static int sk_diag_show_meminfo(struct sock *sk, struct sk_buff *nlskb)
+{
+	struct unix_diag_meminfo *mi;
+
+	mi = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_MEMINFO, sizeof(*mi));
+	mi->udiag_len = sk_wmem_alloc_get(sk);
+	mi->udiag_limit = sk->sk_sndbuf;
+
+	return 0;
+
+rtattr_failure:
+	return -EMSGSIZE;
+}
+
 static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_req *req,
 		u32 pid, u32 seq, u32 flags, int sk_ino)
 {
@@ -146,6 +160,10 @@ static int sk_diag_fill(struct sock *sk, struct sk_buff *skb, struct unix_diag_r
 			sk_diag_show_rqlen(sk, skb))
 		goto nlmsg_failure;
 
+	if ((req->udiag_show & UDIAG_SHOW_MEMINFO) &&
+			sk_diag_show_meminfo(sk, skb))
+		goto nlmsg_failure;
+
 	nlh->nlmsg_len = skb_tail_pointer(skb) - b;
 	return skb->len;
 
-- 
1.5.5.6

^ permalink raw reply related

* [PATCH 4/5] netfilter: xtables: collapse conditions in xt_ecn
From: pablo @ 2011-12-28 13:39 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, jengelh, kaber
In-Reply-To: <1325079573-6120-1-git-send-email-pablo@netfilter.org>

From: Jan Engelhardt <jengelh@medozas.de>

One simplification of an if clause.

Signed-off-by: Jan Engelhardt <jengelh@medozas.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/netfilter/xt_ecn.c |   15 ++++++---------
 1 files changed, 6 insertions(+), 9 deletions(-)

diff --git a/net/netfilter/xt_ecn.c b/net/netfilter/xt_ecn.c
index 6ccc35d..3c831a8 100644
--- a/net/netfilter/xt_ecn.c
+++ b/net/netfilter/xt_ecn.c
@@ -37,9 +37,8 @@ static bool match_tcp(const struct sk_buff *skb, struct xt_action_param *par)
 	 * be good citizens.
 	 */
 	th = skb_header_pointer(skb, par->thoff, sizeof(_tcph), &_tcph);
-	if (th == NULL) {
+	if (th == NULL)
 		return false;
-	}
 
 	if (einfo->operation & XT_ECN_OP_MATCH_ECE) {
 		if (einfo->invert & XT_ECN_OP_MATCH_ECE) {
@@ -75,14 +74,12 @@ static bool ecn_mt4(const struct sk_buff *skb, struct xt_action_param *par)
 {
 	const struct xt_ecn_info *info = par->matchinfo;
 
-	if (info->operation & XT_ECN_OP_MATCH_IP)
-		if (!match_ip(skb, info))
-			return false;
+	if (info->operation & XT_ECN_OP_MATCH_IP && !match_ip(skb, info))
+		return false;
 
-	if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR)) {
-		if (!match_tcp(skb, par))
-			return false;
-	}
+	if (info->operation & (XT_ECN_OP_MATCH_ECE | XT_ECN_OP_MATCH_CWR) &&
+	    !match_tcp(skb, par))
+		return false;
 
 	return true;
 }
-- 
1.7.7.3

^ permalink raw reply related

* [PATCH 5/5] netfilter: provide config option to disable ancient procfs parts
From: pablo @ 2011-12-28 13:39 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, jengelh, kaber
In-Reply-To: <1325079573-6120-1-git-send-email-pablo@netfilter.org>

From: Jan Engelhardt <jengelh@medozas.de>

Using /proc/net/nf_conntrack has been deprecated in favour of the
conntrack(8) tool.

Signed-off-by: Jan Engelhardt <jengelh@medozas.de>
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 net/ipv4/netfilter/Kconfig              |    2 +-
 net/netfilter/Kconfig                   |   10 ++++++++++
 net/netfilter/nf_conntrack_expect.c     |   12 ++++++------
 net/netfilter/nf_conntrack_standalone.c |    4 ++--
 4 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig
index 53b9c79..74dfc9e 100644
--- a/net/ipv4/netfilter/Kconfig
+++ b/net/ipv4/netfilter/Kconfig
@@ -27,7 +27,7 @@ config NF_CONNTRACK_IPV4
 
 config NF_CONNTRACK_PROC_COMPAT
 	bool "proc/sysctl compatibility with old connection tracking"
-	depends on NF_CONNTRACK_IPV4
+	depends on NF_CONNTRACK_PROCFS && NF_CONNTRACK_IPV4
 	default y
 	help
 	  This option enables /proc and sysctl compatibility with the old
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 20388a9..f6275a0 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -83,6 +83,16 @@ config NF_CONNTRACK_ZONES
 
 	  If unsure, say `N'.
 
+config NF_CONNTRACK_PROCFS
+	bool "Supply CT list in procfs (OBSOLETE)"
+	default y
+	depends on PROC_FS
+	---help---
+	This option enables for the list of known conntrack entries
+	to be shown in procfs under net/netfilter/nf_conntrack. This
+	is considered obsolete in favor of using the conntrack(8)
+	tool which uses Netlink.
+
 config NF_CONNTRACK_EVENTS
 	bool "Connection tracking events"
 	depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index bebb167..4147ba3 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -455,7 +455,7 @@ out:
 }
 EXPORT_SYMBOL_GPL(nf_ct_expect_related_report);
 
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_NF_CONNTRACK_PROCFS
 struct ct_expect_iter_state {
 	struct seq_net_private p;
 	unsigned int bucket;
@@ -583,25 +583,25 @@ static const struct file_operations exp_file_ops = {
 	.llseek  = seq_lseek,
 	.release = seq_release_net,
 };
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NF_CONNTRACK_PROCFS */
 
 static int exp_proc_init(struct net *net)
 {
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_NF_CONNTRACK_PROCFS
 	struct proc_dir_entry *proc;
 
 	proc = proc_net_fops_create(net, "nf_conntrack_expect", 0440, &exp_file_ops);
 	if (!proc)
 		return -ENOMEM;
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NF_CONNTRACK_PROCFS */
 	return 0;
 }
 
 static void exp_proc_remove(struct net *net)
 {
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_NF_CONNTRACK_PROCFS
 	proc_net_remove(net, "nf_conntrack_expect");
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NF_CONNTRACK_PROCFS */
 }
 
 module_param_named(expect_hashsize, nf_ct_expect_hsize, uint, 0400);
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 05e9feb..885f5ab 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -34,7 +34,7 @@
 
 MODULE_LICENSE("GPL");
 
-#ifdef CONFIG_PROC_FS
+#ifdef CONFIG_NF_CONNTRACK_PROCFS
 int
 print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple,
             const struct nf_conntrack_l3proto *l3proto,
@@ -396,7 +396,7 @@ static int nf_conntrack_standalone_init_proc(struct net *net)
 static void nf_conntrack_standalone_fini_proc(struct net *net)
 {
 }
-#endif /* CONFIG_PROC_FS */
+#endif /* CONFIG_NF_CONNTRACK_PROCFS */
 
 /* Sysctl support */
 
-- 
1.7.7.3

^ permalink raw reply related

* [PATCH 0/5] netfilter updates for net-next (2nd round)
From: pablo @ 2011-12-28 13:39 UTC (permalink / raw)
  To: netfilter-devel; +Cc: davem, netdev, jengelh, kaber

From: Pablo Neira Ayuso <pablo@netfilter.org>

Hi Dave,

These are a couple of late updates from Jan Engelhardt, who has been
kind enough to recover the unmerged ECN support for IPv6. The IPv6
ECN support is original work from Patrick McHardy.

This includes one patch to obsolete the /proc/net/[nf|ip]_conntrack
outputs in favour of conntrack(8). Basically, it adds some compilation
time option to disable it. Later on, we can schedule it for removal.

You can pull this changes from:

git://1984.lsi.us.es/net-next nf-next

Let us know if it's too late for this merge window.
Thanks again!

Jan Engelhardt (4):
  netfilter: xtables: move ipt_ecn to xt_ecn
  netfilter: xtables: give xt_ecn its own name
  netfilter: xtables: collapse conditions in xt_ecn
  netfilter: provide config option to disable ancient procfs parts

Patrick McHardy (1):
  netfilter: xtables: add an IPv6 capable version of the ECN match

 include/linux/netfilter/Kbuild          |    1 +
 include/linux/netfilter/xt_ecn.h        |   35 ++++++
 include/linux/netfilter_ipv4/ipt_ecn.h  |   38 ++-----
 net/ipv4/netfilter/Kconfig              |   12 +-
 net/ipv4/netfilter/Makefile             |    1 -
 net/ipv4/netfilter/ipt_ecn.c            |  127 ----------------------
 net/netfilter/Kconfig                   |   19 ++++
 net/netfilter/Makefile                  |    1 +
 net/netfilter/nf_conntrack_expect.c     |   12 +-
 net/netfilter/nf_conntrack_standalone.c |    4 +-
 net/netfilter/xt_ecn.c                  |  179 +++++++++++++++++++++++++++++++
 11 files changed, 258 insertions(+), 171 deletions(-)
 create mode 100644 include/linux/netfilter/xt_ecn.h
 delete mode 100644 net/ipv4/netfilter/ipt_ecn.c
 create mode 100644 net/netfilter/xt_ecn.c

-- 
1.7.7.3

^ permalink raw reply

* [PATCH 5/5] unix_diag: Report ack backlog info for listen sockets
From: Pavel Emelyanov @ 2011-12-28 13:40 UTC (permalink / raw)
  To: David Miller, Linux Netdev List

I propose to reuse the MEMINFO bit and message for this, since it's

a) also 2 values
b) symmetrical to how inet diag reports the length

Is it OK, or is it better to introduce separate bit and message?

Signed-off-by: Pavel Emelyanov <xemul@parallels.com>

---
 net/unix/diag.c |   10 ++++++++--
 1 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/net/unix/diag.c b/net/unix/diag.c
index dbae8a4..ebec5fc 100644
--- a/net/unix/diag.c
+++ b/net/unix/diag.c
@@ -113,8 +113,14 @@ static int sk_diag_show_meminfo(struct sock *sk, struct sk_buff *nlskb)
 	struct unix_diag_meminfo *mi;
 
 	mi = UNIX_DIAG_PUT(nlskb, UNIX_DIAG_MEMINFO, sizeof(*mi));
-	mi->udiag_len = sk_wmem_alloc_get(sk);
-	mi->udiag_limit = sk->sk_sndbuf;
+
+	if (sk->sk_state == TCP_LISTEN) {
+		mi->udiag_len = sk->sk_receive_queue.qlen;
+		mi->udiag_limit = sk->sk_max_ack_backlog;
+	} else {
+		mi->udiag_len = sk_wmem_alloc_get(sk);
+		mi->udiag_limit = sk->sk_sndbuf;
+	}
 
 	return 0;
 
-- 
1.5.5.6

^ permalink raw reply related

* [PATCH RFC] IPv6: RTM_GETROUTE NLM_F_MATCH handled as stated in RFC 3549
From: Matti Vaittinen @ 2011-12-28 14:01 UTC (permalink / raw)
  To: netdev, davem

Hi Dee Ho!

RFC 3549 states:

Additional flag bits for GET requests on config information in
   the FEC.
          NLM_F_ROOT     Return the complete table instead of a
                         single entry.
          NLM_F_MATCH    Return all entries matching criteria passed in
                         message content.
          NLM_F_ATOMIC   Return an atomic snapshot of the table being
                         referenced.  This may require special
                         privileges because it has the potential to
                         interrupt service in the FE for a longer time.

   Convenience macros for flag bits:
          NLM_F_DUMP     This is NLM_F_ROOT or'ed with NLM_F_MATCH

However, currently requests with NLM_F_ROOT or NLM_F_MATCH or both 
(NLM_F_DUMP) specified will return all the (routing) entries.


To me it sounds that the NLM_F_MATCH was originally meant to
allow user to ask only entries (routes) fulfilling conditions given in 
request. This would further simplify userland applications which need 
to get only certain entries - and I believe that is often the case. 
I believe the current operation which differs from RFC makes netlink 
socket usage even more confusing. (There really is not too much 
up-to-date documentation telling how these requests+all the attributes
work).

This patch makes ipv6 module to return only routes which match 
attributes / filled fields in RTM_GETROUTE, if NLM_F_MATCH is 
specified and NLM_F_ROOT is not. This patch has not been tested, 
and is meant more to be for visualization of what I thought of doing.
If the NLM_F_MATCH support is considered to be good idea, then I 
will check this more thoroughly and send another patch.

I assume this would not break *many* existing userspace applications, 
since specifying NLM_F_MATCH (especially with no NLM_F_ROOT) sounds 
pretty stupid - if no entries should be filtered.

I checked iproute2, and it uses NLM_F_DUMP and does filtering entries 
in userspace - thus it is not affected. 

I guess this same idea could be brought in RTM_GETADDR and RTM_GETLINK 
too? Maybe also on IPv4 side? 

Any comments? 


















This patch demonstrates how RTM_GETROUTE request behaviour for IPv6 
could be changed to better match RFC 3549 when NLM_F_MATCH flag is 
specified.

"NLM_F_MATCH    Return all entries matching criteria passed in
                         message content."

Signed-off-by: Matti Vaittinen <Mazziesaccount@gmail.com>
---

diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h
index 2ad92ca..27313ad 100644
--- a/include/net/ip6_route.h
+++ b/include/net/ip6_route.h
@@ -139,12 +139,21 @@ extern void			rt6_pmtu_discovery(const struct in6_addr *daddr,
 struct netlink_callback;
 
 struct rt6_rtnl_dump_arg {
+	long maxtbl;
 	struct sk_buff *skb;
 	struct netlink_callback *cb;
 	struct net *net;
 };
+struct rt6_rtnl_match_arg {
+	long maxtbl;
+	struct sk_buff *skb;
+	struct netlink_callback *cb;
+	struct net *net;
+	struct fib6_config *cfg;
+};
 
 extern int rt6_dump_route(struct rt6_info *rt, void *p_arg);
+extern int rt6_dump_route_if_match(struct rt6_info *rt, void *p_arg);
 extern void rt6_ifdown(struct net *net, struct net_device *dev);
 extern void rt6_mtu_change(struct net_device *dev, unsigned mtu);
 extern void rt6_remove_prefsrc(struct inet6_ifaddr *ifp);
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 2783631..13bea56 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -268,7 +268,26 @@ static void __net_init fib6_tables_init(struct net *net)
 }
 
 #endif
+static int fib6_dump_node_if_match(struct fib6_walker_t *w)
+{
+	int res;
+	struct rt6_info *rt;
+
+	for (rt = w->leaf; rt; rt = rt->dst.rt6_next) {
+		res = rt6_dump_route_if_match(rt, w->args);
+		if (res < 0) {
+			/* Frame is full or request was not parsed correctly.
+			 * Anyways, suspend walking
+			 */
+			w->leaf = rt;
+			return 1;
+		}
+		WARN_ON(res == 0);
+	}
+	w->leaf = NULL;
+	return 0;
 
+}
 static int fib6_dump_node(struct fib6_walker_t *w)
 {
 	int res;
@@ -292,11 +311,14 @@ static void fib6_dump_end(struct netlink_callback *cb)
 	struct fib6_walker_t *w = (void*)cb->args[2];
 
 	if (w) {
+		if (w->func == fib6_dump_node_if_match)
+			kfree(((struct rt6_rtnl_match_arg *)w->args)->cfg);
 		if (cb->args[4]) {
 			cb->args[4] = 0;
 			fib6_walker_unlink(w);
 		}
 		cb->args[2] = 0;
+		kfree(w->args);
 		kfree(w);
 	}
 	cb->done = (void*)cb->args[3];
@@ -356,16 +378,13 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
 	struct net *net = sock_net(skb->sk);
 	unsigned int h, s_h;
 	unsigned int e = 0, s_e;
-	struct rt6_rtnl_dump_arg arg;
+	struct rt6_rtnl_dump_arg *arg;
 	struct fib6_walker_t *w;
 	struct fib6_table *tb;
 	struct hlist_node *node;
 	struct hlist_head *head;
 	int res = 0;
 
-	s_h = cb->args[0];
-	s_e = cb->args[1];
-
 	w = (void *)cb->args[2];
 	if (!w) {
 		/* New dump:
@@ -381,17 +400,57 @@ static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
 		w = kzalloc(sizeof(*w), GFP_ATOMIC);
 		if (!w)
 			return -ENOMEM;
-		w->func = fib6_dump_node;
+		if (cb->nlh && (cb->nlh->nlmsg_flags & NLM_F_MATCH) &&
+		    !(cb->nlh->nlmsg_flags & NLM_F_ROOT) &&
+		    nlmsg_len(cb->nlh) >= sizeof(struct rtmsg)) {
+
+			struct rtmsg *rtm = nlmsg_data(cb->nlh);
+
+			cb->done = fib6_dump_done;
+			w->args = kzalloc(sizeof(struct rt6_rtnl_match_arg), GFP_ATOMIC);
+			if (!w->args) {
+				kfree(w);
+				return -ENOMEM;
+			}
+			((struct rt6_rtnl_match_arg *)w->args)->cfg =
+			    kzalloc(sizeof(struct fib6_config), GFP_ATOMIC);
+
+			if (!((struct rt6_rtnl_match_arg *)w->args)->cfg) {
+				kfree(w->args);
+				kfree(w);
+				return -ENOMEM;
+			}
+#ifdef CONFIG_IPV6_MULTIPLE_TABLES
+			if (rtm->rtm_table <= FIB6_TABLE_HASHSZ) {
+				cb->args[0] = rtm->rtm_table;
+				((struct rt6_rtnl_match_arg *)w->args)->maxtbl =
+				    (rtm->rtm_table) ? rtm->rtm_table+1 : FIB6_TABLE_HASHSZ;
+			}
+#endif
+
+			((struct rt6_rtnl_match_arg *)w->args)->cfg->fc_table = 0xFFFFFFFF;
+			w->func = fib6_dump_node_if_match;
+		} else {
+			w->func = fib6_dump_node;
+			w->args = kzalloc(sizeof(struct rt6_rtnl_dump_arg), GFP_ATOMIC);
+			if (!w->args) {
+				kfree(w);
+				return -ENOMEM;
+			}
+			((struct rt6_rtnl_dump_arg *)w->args)->maxtbl = FIB6_TABLE_HASHSZ;
+		}
 		cb->args[2] = (long)w;
 	}
+	arg = (struct rt6_rtnl_dump_arg *)w->args;
 
-	arg.skb = skb;
-	arg.cb = cb;
-	arg.net = net;
-	w->args = &arg;
+	s_h = cb->args[0];
+	s_e = cb->args[1];
+	arg->skb = skb;
+	arg->cb = cb;
+	arg->net = net;
 
 	rcu_read_lock();
-	for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
+	for (h = s_h; h < arg->maxtbl; h++, s_e = 0) {
 		e = 0;
 		head = &net->ipv6.fib_table_hash[h];
 		hlist_for_each_entry_rcu(tb, node, head, tb6_hlist) {
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 35b07cc..a71d44d 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -2350,7 +2350,154 @@ static inline size_t rt6_nlmsg_size(void)
 	       + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */
 	       + nla_total_size(sizeof(struct rta_cacheinfo));
 }
+static int rt6_cmp_fill_node(struct net *net,
+		     struct sk_buff *skb, struct rt6_info *rt,
+		     u32 pid,
+		     int prefix, struct fib6_config *cfg)
+{
+	struct rtmsg *rtm;
+	struct nlmsghdr *nlh;
+	long expires;
+	u32 table;
+	struct neighbour *n = NULL;
+	u32 seq;
+	struct in6_addr gwaddr;
+
+
+	if (prefix) {	/* user wants prefix routes only */
+		if (!(rt->rt6i_flags & RTF_PREFIX_RT)) {
+			/* success since this is not a prefix route */
+			return 1;
+		}
+	}
+	if (cfg->fc_dst_len && cfg->fc_dst_len != rt->rt6i_dst.plen)
+		return 1;
+
+	if (cfg->fc_src_len && cfg->fc_src_len != rt->rt6i_src.plen)
+		return 1;
+	cfg->fc_flags = ((~RTF_UP)&cfg->fc_flags);
+	if (cfg->fc_flags && cfg->fc_flags != rt->rt6i_flags)
+		return 1;
+
+	if (cfg->fc_protocol && cfg->fc_protocol != rt->rt6i_protocol)
+		return 1;
+
+	if (cfg->fc_flags & RTF_GATEWAY) {
+		rcu_read_lock();
+		n = dst_get_neighbour_noref(&rt->dst);
+		if (!n) {
+			rcu_read_unlock();
+			return 1;
+		}
+		gwaddr = *(struct in6_addr *)&(n->primary_key);
+		rcu_read_unlock();
+		if (memcmp(&gwaddr, &cfg->fc_gateway, 16))
+			return 1;
+	}
+
+	if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric)
+		return 1;
+
+	if (cfg->fc_ifindex)
+	   if (!rt->dst.dev || rt->rt6i_dev->ifindex != cfg->fc_ifindex)
+			return 1;
+
+	if (memcmp(&in6addr_any, &cfg->fc_dst, sizeof(struct in6_addr)))
+		if (memcmp(&cfg->fc_dst, &rt->rt6i_dst.addr, sizeof(struct in6_addr)))
+			return 1;
+
+#ifdef CONFIG_IPV6_SUBTREES
+	if (memcmp(&in6addr_any, &cfg->fc_src, sizeof(struct in6_addr)))
+		if (memcmp(&cfg->fc_src, &rt->rt6i_src.addr, sizeof(struct in6_addr)))
+			return 1;
+#endif
+	if (memcmp(&in6addr_any, &cfg->fc_prefsrc, sizeof(struct in6_addr)))
+		if (!rt->rt6i_prefsrc.plen ||
+		    memcmp(&rt->rt6i_prefsrc.addr, &cfg->fc_prefsrc, sizeof(struct in6_addr)))
+			return 1;
+	/* All values checked, now allocate and fill NLMSG */
+	seq = cfg->fc_nlinfo.nlh->nlmsg_seq;
+
+	nlh = nlmsg_put(skb, pid, seq, RTM_NEWROUTE, sizeof(*rtm), NLM_F_MULTI);
+	if (nlh == NULL)
+		return -EMSGSIZE;
+
+	rtm = nlmsg_data(nlh);
+	rtm->rtm_family = AF_INET6;
+	rtm->rtm_dst_len = rt->rt6i_dst.plen;
+	rtm->rtm_src_len = rt->rt6i_src.plen;
+	rtm->rtm_tos = 0;
+	if (rt->rt6i_table)
+		table = rt->rt6i_table->tb6_id;
+	else
+		table = RT6_TABLE_UNSPEC;
+	rtm->rtm_table = table;
+	NLA_PUT_U32(skb, RTA_TABLE, table);
+	if (rt->rt6i_flags&RTF_REJECT)
+		rtm->rtm_type = RTN_UNREACHABLE;
+	else if (rt->rt6i_flags&RTF_LOCAL)
+		rtm->rtm_type = RTN_LOCAL;
+	else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK))
+		rtm->rtm_type = RTN_LOCAL;
+	else
+		rtm->rtm_type = RTN_UNICAST;
+	rtm->rtm_flags = 0;
+	rtm->rtm_scope = RT_SCOPE_UNIVERSE;
+	rtm->rtm_protocol = rt->rt6i_protocol;
+	if (rt->rt6i_flags&RTF_DYNAMIC)
+		rtm->rtm_protocol = RTPROT_REDIRECT;
+	else if (rt->rt6i_flags & RTF_ADDRCONF)
+		rtm->rtm_protocol = RTPROT_KERNEL;
+	else if (rt->rt6i_flags&RTF_DEFAULT)
+		rtm->rtm_protocol = RTPROT_RA;
+
+	if (rt->rt6i_flags&RTF_CACHE)
+		rtm->rtm_flags |= RTM_F_CLONED;
+
+	if (rtm->rtm_dst_len)
+		NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);
+#ifdef CONFIG_IPV6_SUBTREES
+	if (rtm->rtm_src_len)
+		NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);
+#endif
+	if (rt->rt6i_prefsrc.plen) {
+		struct in6_addr saddr_buf;
+		saddr_buf = rt->rt6i_prefsrc.addr;
+		NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf);
+	}
 
+	if (rtnetlink_put_metrics(skb, dst_metrics_ptr(&rt->dst)) < 0)
+		goto nla_put_failure;
+
+	rcu_read_lock();
+	n = dst_get_neighbour_noref(&rt->dst);
+	if (n)
+		NLA_PUT(skb, RTA_GATEWAY, 16, &n->primary_key);
+	rcu_read_unlock();
+
+	if (rt->dst.dev)
+		NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex);
+
+	NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric);
+
+	if (!(rt->rt6i_flags & RTF_EXPIRES))
+		expires = 0;
+	else if (rt->rt6i_expires - jiffies < INT_MAX)
+		expires = rt->rt6i_expires - jiffies;
+	else
+		expires = INT_MAX;
+
+	if (rtnl_put_cacheinfo(skb, &rt->dst, 0, 0, 0,
+			       expires, rt->dst.error) < 0)
+		goto nla_put_failure;
+
+	return nlmsg_end(skb, nlh);
+
+nla_put_failure:
+	nlmsg_cancel(skb, nlh);
+	return -EMSGSIZE;
+
+}
 static int rt6_fill_node(struct net *net,
 			 struct sk_buff *skb, struct rt6_info *rt,
 			 struct in6_addr *dst, struct in6_addr *src,
@@ -2478,7 +2625,34 @@ nla_put_failure:
 	nlmsg_cancel(skb, nlh);
 	return -EMSGSIZE;
 }
+int rt6_dump_route_if_match(struct rt6_info *rt, void *p_arg)
+{
+	struct rt6_rtnl_match_arg *arg = (struct rt6_rtnl_match_arg *) p_arg;
+	int prefix;
+	int err;
 
+	if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) {
+		struct rtmsg *rtm = nlmsg_data(arg->cb->nlh);
+		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
+	} else
+		prefix = 0;
+	/* this is a bit of a hack. But we do not want to
+	 * evaluate config struct for same nlmsg
+	 * for each route we fetch from fib.
+	 * It's enough to do it once.
+	 */
+	if (0xFFFFFFFF == arg->cfg->fc_table) {
+		arg->cfg->fc_table = 0;
+		err = rtm_to_fib6_config(arg->cb->skb,
+		    (struct nlmsghdr *)arg->cb->nlh, arg->cfg);
+		if (err)
+			return err;
+	}
+	return rt6_cmp_fill_node(arg->net,
+		     arg->skb, rt,
+		     NETLINK_CB(arg->cb->skb).pid,
+		     prefix, arg->cfg);
+}
 int rt6_dump_route(struct rt6_info *rt, void *p_arg)
 {
 	struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg;
@@ -2489,7 +2663,6 @@ int rt6_dump_route(struct rt6_info *rt, void *p_arg)
 		prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0;
 	} else
 		prefix = 0;
-
 	return rt6_fill_node(arg->net,
 		     arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE,
 		     NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq,

^ permalink raw reply related

* Re: [PATCH 1/1] via-rhine: Fix hanging with high CPU load on low-end broads.
From: Ben Hutchings @ 2011-12-28 14:37 UTC (permalink / raw)
  To: Bjarke Istrup Pedersen; +Cc: linux-kernel, netdev, Roger Luethi
In-Reply-To: <1325075319-13464-1-git-send-email-gurligebis@gentoo.org>

On Wed, 2011-12-28 at 12:28 +0000, Bjarke Istrup Pedersen wrote:
> Working around problem causing high CPU load and hanging system when
> there is alot of network trafic.
> 
> It is kind of an ugly way to work around it, but it allows the Soekris
> net5501 to have trafic between two of it's NICs without hanging so much
> that the watchdog kicks in and does a hard reboot of the system.
> 
> There is more info on the problem here:
> http://http://lists.soekris.com/pipermail/soekris-tech/2010-October/016889.html
> 
> Tested with positive results on two Soekris net5501-70 boxes.

This is completely wrong.  In a UP configuration the extra spinlock
calls have no effect (except perhaps a small delay).  In an SMP
configuration they will cause rhine_tx() to deadlock when it also tries
to acquire the spinlock.

Ben.

[...]
> diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c
> index f34dd99..8c77dcd 100644
> --- a/drivers/net/ethernet/via/via-rhine.c
> +++ b/drivers/net/ethernet/via/via-rhine.c
> @@ -1567,6 +1567,9 @@ static irqreturn_t rhine_interrupt(int irq, void *dev_instance)
>         int boguscnt = max_interrupt_work;
>         int handled = 0;
>  
> +       if (!spin_trylock(&rp->lock))
> +               return IRQ_RETVAL(handled);
> +
>         while ((intr_status = get_intr_status(dev))) {
>                 handled = 1;
>  
> @@ -1616,6 +1619,8 @@ static irqreturn_t rhine_interrupt(int irq, void *dev_instance)
>                 }
>         }
>  
> +       spin_unlock(&rp->lock);
> +
>         if (debug > 3)
>                 netdev_dbg(dev, "exiting interrupt, status=%08x\n",
>                            ioread16(ioaddr + IntrStatus));

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply

* Re: [PATCH 1/1] via-rhine: Fix hanging with high CPU load on low-end broads.
From: Bjarke Istrup Pedersen @ 2011-12-28 15:14 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: linux-kernel, netdev, Roger Luethi
In-Reply-To: <1325083051.2327.47.camel@deadeye>

2011/12/28 Ben Hutchings <bhutchings@solarflare.com>:
> On Wed, 2011-12-28 at 12:28 +0000, Bjarke Istrup Pedersen wrote:
>> Working around problem causing high CPU load and hanging system when
>> there is alot of network trafic.
>>
>> It is kind of an ugly way to work around it, but it allows the Soekris
>> net5501 to have trafic between two of it's NICs without hanging so much
>> that the watchdog kicks in and does a hard reboot of the system.
>>
>> There is more info on the problem here:
>> http://http://lists.soekris.com/pipermail/soekris-tech/2010-October/016889.html
>>
>> Tested with positive results on two Soekris net5501-70 boxes.
>
> This is completely wrong.  In a UP configuration the extra spinlock
> calls have no effect (except perhaps a small delay).  In an SMP
> configuration they will cause rhine_tx() to deadlock when it also tries
> to acquire the spinlock.
>
> Ben.

Okay, the Soekris net5501-70 boxes are single-core, and I haven't got
any SMP boxes with that nic.
Is there a better solution for the problem then, to avoid it hanging
the box on a non-smp machine with a slow (500mhz) cpu?

/Bjarke

> [...]
>> diff --git a/drivers/net/ethernet/via/via-rhine.c b/drivers/net/ethernet/via/via-rhine.c
>> index f34dd99..8c77dcd 100644
>> --- a/drivers/net/ethernet/via/via-rhine.c
>> +++ b/drivers/net/ethernet/via/via-rhine.c
>> @@ -1567,6 +1567,9 @@ static irqreturn_t rhine_interrupt(int irq, void *dev_instance)
>>         int boguscnt = max_interrupt_work;
>>         int handled = 0;
>>
>> +       if (!spin_trylock(&rp->lock))
>> +               return IRQ_RETVAL(handled);
>> +
>>         while ((intr_status = get_intr_status(dev))) {
>>                 handled = 1;
>>
>> @@ -1616,6 +1619,8 @@ static irqreturn_t rhine_interrupt(int irq, void *dev_instance)
>>                 }
>>         }
>>
>> +       spin_unlock(&rp->lock);
>> +
>>         if (debug > 3)
>>                 netdev_dbg(dev, "exiting interrupt, status=%08x\n",
>>                            ioread16(ioaddr + IntrStatus));
>
> --
> Ben Hutchings, Staff Engineer, Solarflare
> Not speaking for my employer; that's the marketing department's job.
> They asked us to note that Solarflare product names are trademarked.
>

^ permalink raw reply

* Re: The mystery of optimistic ipv6 DAD handling
From: Neil Horman @ 2011-12-28 15:19 UTC (permalink / raw)
  To: David Miller; +Cc: netdev
In-Reply-To: <20111227.145305.229364634255751368.davem@davemloft.net>

On Tue, Dec 27, 2011 at 02:53:05PM -0500, David Miller wrote:
> From: David Miller <davem@davemloft.net>
> Date: Tue, 27 Dec 2011 14:06:52 -0500 (EST)
> 
> > However, two things strike me:
> > 
> > 1) even beforehand we'd only get a NULL neigh when the device pointer
> >    is NULL, that can't happen here, rt->rt6i_dev will be non-NULL always
> >    in these circumstances, it's forced to be net->loopback_dev by the
> >    ip6_dst_alloc() call made by addrconf_dst_alloc() (the code back
> >    in 2.6.21 does the same exact thing, just inline)
> > 
> > 2) on top of that it's looking up a neighbour using rt->rt6i_gateway
> >    as the key, that's either bogus or pointless.  As far as I can tell
> >    it's uninitialized at this point and therefore always all-zeros.
> > 
> > My quick hunch is that the neigh should be looked up based upon 'addr', and
> > that ipv6_add_addr() should test if the neigh is resolved in order to determine
> > if the optimistic flag should be cleared.  That matches the logic in the comment
> > "do not yet know the link layer address of our nexthop router."
> > 
> > Neil, any chance you can help me unravel this?
> 
> So the fundamental thing is that these addrconf routes are mainly for
> for input and looping back traffic on output.
> 
> We create the addrconf route "to us", for input.  That's why the lookup
> key is the ipv6 address we are configuring on the interface.
> 
> For output, the neigh is "don't care".  It goes to the loopback device
> and for loopback ->hard_header is NULL so
> net/ipv6/ndisc.c:ndisc_constructor() directly hooks up
> neigh->ops->queue_xmit as the neigh->output method, which is
> dev_queue_xmit().  So it doesn't matter that we lookup the neigh in
> addrconf_dst_alloc() using the all-zeros rt->rt6i_gateway.
> 
> So this route and it's neigh have nothing to do with link layer
> address of any nexthop gatway(s) in this interface.
> 
> Therefore the test is completely wrong, and all I can determine is
> that we should flat out remove it.  It never triggers anyways.
> 
> Perhaps we should find another appropriate spot to put this kind of
> test, but in this spot and against this route object seems totally not
> right.
> 
> Neil, what say you?
Yeah, If the premise that the test never triggers is true (and it seems like
you've confirmed that), then theres no harm in removing it.  It still
seems like we might still need the test somewhere (maybe on a manual route add).
I've family in town, so I'm not able to look at this closely today, but I'd say this
is at least a harmless change right now.  Lets take this change, and I'll
re-read the RFC and look for appropriate code paths where we may need this
test in a few days.  Thanks Dave!

Acked-By: Neil Horman <nhorman@tuxdriver.com>

> 
> --------------------
> ipv6: Remove optimistic DAD flag test in ipv6_add_addr()
> 
> The route we have here is for the address being added to the interface,
> ie. for input packet processing.
> 
> Therefore using that route to determine whether an output nexthop gateway
> is known and resolved doesn't make any sense.
> 
> So, simply remove this test, it never triggered anyways.
> 
> Signed-off-by: David S. Miller <davem@davemloft.net>
> 
> diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
> index 59a9d0e..85421cc 100644
> --- a/net/ipv6/addrconf.c
> +++ b/net/ipv6/addrconf.c
> @@ -650,16 +650,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr, int pfxlen,
>  
>  	ifa->rt = rt;
>  
> -	/*
> -	 * part one of RFC 4429, section 3.3
> -	 * We should not configure an address as
> -	 * optimistic if we do not yet know the link
> -	 * layer address of our nexhop router
> -	 */
> -
> -	if (dst_get_neighbour_noref_raw(&rt->dst) == NULL)
> -		ifa->flags &= ~IFA_F_OPTIMISTIC;
> -
>  	ifa->idev = idev;
>  	in6_dev_hold(idev);
>  	/* For caller */
> 

^ permalink raw reply

* [PATCH v6] ARM: net: JIT compiler for packet filters
From: Mircea Gherzan @ 2011-12-28 15:35 UTC (permalink / raw)
  To: linux-arm-kernel; +Cc: mgherzan, netdev, eric.dumazet, linux

Based of Matt Evans's PPC64 implementation.

The compiler generates ARM instructions but interworking is
supported for Thumb2 kernels.

Supports both little and big endian. Unaligned loads are emitted
for ARMv6+. Not all the BPF opcodes that deal with ancillary data
are supported. The scratch memory of the filter lives on the stack.
Hardware integer division is used if it is available.

Enabled in the same way as for x86-64 and PPC64:

	echo 1 > /proc/sys/net/core/bpf_jit_enable

A value greater than 1 enables opcode output.

Signed-off-by: Mircea Gherzan <mgherzan@gmail.com>
---

Changes in v6:
 * fix the code generation for the ANC_CPU opcode

Changes in v5:
 * replace SEEN_LEN with SEEN_SKB
 * set ctx->seen when handling some ancillary data opcodes

Changes in v4:
 * first check if the JIT compiler is enabled
 * fix the code generation for the LDX_MSH opcode

Changes in v3:
 * no longer depend on EABI and !Thumb2
 * add BLX "emulation" for ARMv4 without Thumb
 * use the integer divide instruction on Cortex-A15
 * fix the handling of the DIV_K opcode
 * use a C wrapper for __aeabi_uidiv
 * fix the generation of the epilogue (non-FP case)

Changes in v2:
 * enable the compiler only for ARMv5+ because of the BLX instruction
 * use the same comparison for the ARM version checks
 * use misaligned accesses on ARMv6
 * fix the SEEN_MEM
 * fix the mem_words_used()

 arch/arm/Kconfig          |    1 +
 arch/arm/Makefile         |    1 +
 arch/arm/net/Makefile     |    3 +
 arch/arm/net/bpf_jit_32.c |  887 +++++++++++++++++++++++++++++++++++++++++++++
 arch/arm/net/bpf_jit_32.h |  186 ++++++++++
 5 files changed, 1078 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/net/Makefile
 create mode 100644 arch/arm/net/bpf_jit_32.c
 create mode 100644 arch/arm/net/bpf_jit_32.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index abba5b8..0014b4b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -30,6 +30,7 @@ config ARM
 	select HAVE_SPARSE_IRQ
 	select GENERIC_IRQ_SHOW
 	select CPU_PM if (SUSPEND || CPU_IDLE)
+	select HAVE_BPF_JIT
 	help
 	  The ARM series is a line of low-power-consumption RISC chip designs
 	  licensed by ARM Ltd and targeted at embedded applications and
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index dfcf3b0..8810a10 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -255,6 +255,7 @@ core-$(CONFIG_VFP)		+= arch/arm/vfp/
 
 # If we have a machine-specific directory, then include it in the build.
 core-y				+= arch/arm/kernel/ arch/arm/mm/ arch/arm/common/
+core-y				+= arch/arm/net/
 core-y				+= $(machdirs) $(platdirs)
 
 drivers-$(CONFIG_OPROFILE)      += arch/arm/oprofile/
diff --git a/arch/arm/net/Makefile b/arch/arm/net/Makefile
new file mode 100644
index 0000000..c2c1084
--- /dev/null
+++ b/arch/arm/net/Makefile
@@ -0,0 +1,3 @@
+# ARM-specific networking code
+
+obj-$(CONFIG_BPF_JIT) += bpf_jit_32.o
diff --git a/arch/arm/net/bpf_jit_32.c b/arch/arm/net/bpf_jit_32.c
new file mode 100644
index 0000000..0b9cc6c
--- /dev/null
+++ b/arch/arm/net/bpf_jit_32.c
@@ -0,0 +1,887 @@
+/*
+ * Just-In-Time compiler for BPF filters on 32bit ARM
+ *
+ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
+ *
+ * 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; version 2 of the License.
+ */
+
+#include <linux/bitops.h>
+#include <linux/compiler.h>
+#include <linux/filter.h>
+#include <linux/moduleloader.h>
+#include <linux/netdevice.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/cacheflush.h>
+#include <asm/hwcap.h>
+
+#include "bpf_jit_32.h"
+
+/*
+ * ABI:
+ *
+ * r0	scratch register
+ * r4	BPF register A
+ * r5	BPF register X
+ * r6	pointer to the skb
+ * r7	skb->data
+ * r8	skb_headlen(skb)
+ */
+
+#define r_scratch	ARM_R0
+/* r1-r3 are (also) used for the unaligned loads on the non-ARMv7 slowpath */
+#define r_off		ARM_R1
+#define r_A		ARM_R4
+#define r_X		ARM_R5
+#define r_skb		ARM_R6
+#define r_skb_data	ARM_R7
+#define r_skb_hl	ARM_R8
+
+#define SCRATCH_SP_OFFSET	0
+#define SCRATCH_OFF(k)		(SCRATCH_SP_OFFSET + (k))
+
+#define SEEN_MEM		((1 << BPF_MEMWORDS) - 1)
+#define SEEN_MEM_WORD(k)	(1 << (k))
+#define SEEN_X			(1 << BPF_MEMWORDS)
+#define SEEN_CALL		(1 << (BPF_MEMWORDS + 1))
+#define SEEN_SKB		(1 << (BPF_MEMWORDS + 2))
+#define SEEN_DATA		(1 << (BPF_MEMWORDS + 3))
+
+struct jit_ctx {
+	const struct sk_filter *skf;
+	unsigned idx;
+	unsigned prologue_bytes;
+	int ret0_fp_idx;
+	u32 seen;
+	u32 *offsets;
+	u32 *target;
+#if __LINUX_ARM_ARCH__ < 7
+	u16 epilogue_bytes;
+	u16 imm_count;
+	u32 *imms;
+#endif
+};
+
+int bpf_jit_enable __read_mostly;
+
+static u8 jit_get_skb_b(struct sk_buff *skb, unsigned offset)
+{
+	u8 ret;
+	skb_copy_bits(skb, offset, &ret, 1);
+	return ret;
+}
+
+static u16 jit_get_skb_h(struct sk_buff *skb, unsigned offset)
+{
+	u16 ret;
+	skb_copy_bits(skb, offset, &ret, 2);
+	return ntohs(ret);
+}
+
+static u32 jit_get_skb_w(struct sk_buff *skb, unsigned offset)
+{
+	u32 ret;
+	skb_copy_bits(skb, offset, &ret, 4);
+	return ntohl(ret);
+}
+
+/*
+ * Wrapper that handles both OABI and EABI and assures Thumb2 interworking
+ * (where the assembly routines like __aeabi_uidiv could cause problems).
+ */
+static u32 jit_udiv(u32 dividend, u32 divisor)
+{
+	return dividend / divisor;
+}
+
+static inline void _emit(int cond, u32 inst, struct jit_ctx *ctx)
+{
+	if (ctx->target != NULL)
+		ctx->target[ctx->idx] = inst | (cond << 28);
+
+	ctx->idx++;
+}
+
+/*
+ * Emit an instruction that will be executed unconditionally.
+ */
+static inline void emit(u32 inst, struct jit_ctx *ctx)
+{
+	_emit(ARM_COND_AL, inst, ctx);
+}
+
+static u16 saved_regs(struct jit_ctx *ctx)
+{
+	u16 ret = 0;
+
+	if (ctx->skf->len > 1)
+		ret |= 1 << r_A;
+
+#ifdef CONFIG_FRAME_POINTER
+	ret |= (1 << ARM_FP) | (1 << ARM_IP) | (1 << ARM_LR) | (1 << ARM_PC);
+#else
+	if (ctx->seen & SEEN_CALL)
+		ret |= 1 << ARM_LR;
+#endif
+	if (ctx->seen & (SEEN_DATA | SEEN_SKB))
+		ret |= 1 << r_skb;
+	if (ctx->seen & SEEN_DATA)
+		ret |= (1 << r_skb_data) | (1 << r_skb_hl);
+	if (ctx->seen & SEEN_X)
+		ret |= 1 << r_X;
+
+	return ret;
+}
+
+static inline int mem_words_used(struct jit_ctx *ctx)
+{
+	u32 words = ctx->seen & SEEN_MEM;
+	/* yes, we do waste some stack space IF there are "holes" in the set" */
+	return 32 - __builtin_clz(words);
+}
+
+static inline bool is_load_to_a(u16 inst)
+{
+	switch (inst) {
+	case BPF_S_LD_W_LEN:
+	case BPF_S_LD_W_ABS:
+	case BPF_S_LD_H_ABS:
+	case BPF_S_LD_B_ABS:
+	case BPF_S_ANC_CPU:
+	case BPF_S_ANC_IFINDEX:
+	case BPF_S_ANC_MARK:
+	case BPF_S_ANC_PROTOCOL:
+	case BPF_S_ANC_RXHASH:
+	case BPF_S_ANC_QUEUE:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static void build_prologue(struct jit_ctx *ctx)
+{
+	u16 reg_set = saved_regs(ctx);
+	u16 first_inst = ctx->skf->insns[0].code;
+	u16 off;
+
+#ifdef CONFIG_FRAME_POINTER
+	emit(ARM_MOV_R(ARM_IP, ARM_SP), ctx);
+	emit(ARM_PUSH(reg_set), ctx);
+	emit(ARM_SUB_I(ARM_FP, ARM_IP, 4), ctx);
+#else
+	if (reg_set)
+		emit(ARM_PUSH(reg_set), ctx);
+#endif
+
+	if (ctx->seen & (SEEN_DATA | SEEN_SKB))
+		emit(ARM_MOV_R(r_skb, ARM_R0), ctx);
+
+	if (ctx->seen & SEEN_DATA) {
+		off = offsetof(struct sk_buff, data);
+		emit(ARM_LDR_I(r_skb_data, r_skb, off), ctx);
+		/* headlen = len - data_len */
+		off = offsetof(struct sk_buff, len);
+		emit(ARM_LDR_I(r_skb_hl, r_skb, off), ctx);
+		off = offsetof(struct sk_buff, data_len);
+		emit(ARM_LDR_I(r_scratch, r_skb, off), ctx);
+		emit(ARM_SUB_R(r_skb_hl, r_skb_hl, r_scratch), ctx);
+	}
+
+	if (ctx->seen & SEEN_X)
+		emit(ARM_MOV_I(r_X, 0), ctx);
+
+	/* do not leak kernel data to userspace */
+	if ((first_inst != BPF_S_RET_K) && !(is_load_to_a(first_inst)))
+		emit(ARM_MOV_I(r_A, 0), ctx);
+
+	/* stack space for the BPF_MEM words */
+	if (ctx->seen & SEEN_MEM)
+		emit(ARM_SUB_I(ARM_SP, ARM_SP, mem_words_used(ctx) * 4), ctx);
+}
+
+static void build_epilogue(struct jit_ctx *ctx)
+{
+	u16 reg_set = saved_regs(ctx);
+
+	if (ctx->seen & SEEN_MEM)
+		emit(ARM_ADD_I(ARM_SP, ARM_SP, mem_words_used(ctx) * 4), ctx);
+
+	reg_set &= ~(1 << ARM_LR);
+
+#ifdef CONFIG_FRAME_POINTER
+	/* the first instruction of the prologue was: mov ip, sp */
+	reg_set &= ~(1 << ARM_IP);
+	reg_set |= (1 << ARM_SP);
+	emit(ARM_LDM(ARM_SP, reg_set), ctx);
+#else
+	if (ctx->seen) {
+		if (ctx->seen & SEEN_CALL)
+			reg_set |= 1 << ARM_PC;
+		emit(ARM_POP(reg_set), ctx);
+	}
+
+	if (!(ctx->seen & SEEN_CALL))
+		emit(ARM_BX(ARM_LR), ctx);
+#endif
+}
+
+static int16_t imm8m(u32 x)
+{
+	u32 rot;
+
+	for (rot = 0; rot < 16; rot++)
+		if ((x & ~ror32(0xff, 2 * rot)) == 0)
+			return rol32(x, 2 * rot) | (rot << 8);
+
+	return -1;
+}
+
+#if __LINUX_ARM_ARCH__ < 7
+
+static u16 imm_offset(u32 k, struct jit_ctx *ctx)
+{
+	unsigned i = 0, offset;
+	u16 imm;
+
+	/* on the "fake" run we just count them (duplicates included) */
+	if (ctx->target == NULL) {
+		ctx->imm_count++;
+		return 0;
+	}
+
+	while ((i < ctx->imm_count) && ctx->imms[i]) {
+		if (ctx->imms[i] == k)
+			break;
+		i++;
+	}
+
+	if (ctx->imms[i] == 0)
+		ctx->imms[i] = k;
+
+	/* constants go just after the epilogue */
+	offset =  ctx->offsets[ctx->skf->len];
+	offset += ctx->prologue_bytes;
+	offset += ctx->epilogue_bytes;
+	offset += i * 4;
+
+	ctx->target[offset / 4] = k;
+
+	/* PC in ARM mode == address of the instruction + 8 */
+	imm = offset - (8 + ctx->idx * 4);
+
+	return imm;
+}
+
+#endif /* __LINUX_ARM_ARCH__ */
+
+/*
+ * Move an immediate that's not an imm8m to a core register.
+ */
+static inline void emit_mov_i_no8m(int rd, u32 val, struct jit_ctx *ctx)
+{
+#if __LINUX_ARM_ARCH__ < 7
+	emit(ARM_LDR_I(rd, ARM_PC, imm_offset(val, ctx)), ctx);
+#else
+	emit(ARM_MOVW(rd, val & 0xffff), ctx);
+	if (val > 0xffff)
+		emit(ARM_MOVT(rd, val >> 16), ctx);
+#endif
+}
+
+static inline void emit_mov_i(int rd, u32 val, struct jit_ctx *ctx)
+{
+	int imm12 = imm8m(val);
+
+	if (imm12 >= 0)
+		emit(ARM_MOV_I(rd, imm12), ctx);
+	else
+		emit_mov_i_no8m(rd, val, ctx);
+}
+
+#if __LINUX_ARM_ARCH__ < 6
+
+static void emit_load_be32(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx)
+{
+	_emit(cond, ARM_LDRB_I(ARM_R3, r_addr, 1), ctx);
+	_emit(cond, ARM_LDRB_I(ARM_R1, r_addr, 0), ctx);
+	_emit(cond, ARM_LDRB_I(ARM_R2, r_addr, 3), ctx);
+	_emit(cond, ARM_LSL_I(ARM_R3, ARM_R3, 16), ctx);
+	_emit(cond, ARM_LDRB_I(ARM_R0, r_addr, 2), ctx);
+	_emit(cond, ARM_ORR_S(ARM_R3, ARM_R3, ARM_R1, SRTYPE_LSL, 24), ctx);
+	_emit(cond, ARM_ORR_R(ARM_R3, ARM_R3, ARM_R2), ctx);
+	_emit(cond, ARM_ORR_S(r_res, ARM_R3, ARM_R0, SRTYPE_LSL, 8), ctx);
+}
+
+static void emit_load_be16(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx)
+{
+	_emit(cond, ARM_LDRB_I(ARM_R1, r_addr, 0), ctx);
+	_emit(cond, ARM_LDRB_I(ARM_R2, r_addr, 1), ctx);
+	_emit(cond, ARM_ORR_S(r_res, ARM_R2, ARM_R1, SRTYPE_LSL, 8), ctx);
+}
+
+static inline void emit_swap16(u8 r_dst, u8 r_src, struct jit_ctx *ctx)
+{
+	emit(ARM_LSL_R(ARM_R1, r_src, 8), ctx);
+	emit(ARM_ORR_S(r_dst, ARM_R1, r_src, SRTYPE_LSL, 8), ctx);
+	emit(ARM_LSL_I(r_dst, r_dst, 8), ctx);
+	emit(ARM_LSL_R(r_dst, r_dst, 8), ctx);
+}
+
+#else  /* ARMv6+ */
+
+static void emit_load_be32(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx)
+{
+	_emit(cond, ARM_LDR_I(r_res, r_addr, 0), ctx);
+#ifdef __LITTLE_ENDIAN
+	_emit(cond, ARM_REV(r_res, r_res), ctx);
+#endif
+}
+
+static void emit_load_be16(u8 cond, u8 r_res, u8 r_addr, struct jit_ctx *ctx)
+{
+	_emit(cond, ARM_LDRH_I(r_res, r_addr, 0), ctx);
+#ifdef __LITTLE_ENDIAN
+	_emit(cond, ARM_REV16(r_res, r_res), ctx);
+#endif
+}
+
+static inline void emit_swap16(u8 r_dst __maybe_unused,
+			       u8 r_src __maybe_unused,
+			       struct jit_ctx *ctx __maybe_unused)
+{
+#ifdef __LITTLE_ENDIAN
+	emit(ARM_REV16(r_dst, r_src), ctx);
+#endif
+}
+
+#endif /* __LINUX_ARM_ARCH__ < 6 */
+
+
+/* Compute the immediate value for a PC-relative branch. */
+static inline u32 b_imm(unsigned tgt, struct jit_ctx *ctx)
+{
+	u32 imm;
+
+	if (ctx->target == NULL)
+		return 0;
+	/*
+	 * BPF allows only forward jumps and the offset of the target is
+	 * still the one computed during the first pass.
+	 */
+	imm  = ctx->offsets[tgt] + ctx->prologue_bytes - (ctx->idx * 4 + 8);
+
+	return imm >> 2;
+}
+
+#define OP_IMM3(op, r1, r2, imm_val, ctx)				\
+	do {								\
+		imm12 = imm8m(imm_val);					\
+		if (imm12 < 0) {					\
+			emit_mov_i_no8m(r_scratch, imm_val, ctx);	\
+			emit(op ## _R((r1), (r2), r_scratch), ctx);	\
+		} else {						\
+			emit(op ## _I((r1), (r2), imm12), ctx);		\
+		}							\
+	} while (0)
+
+static inline void emit_err_ret(u8 cond, struct jit_ctx *ctx)
+{
+	if (ctx->ret0_fp_idx >= 0) {
+		_emit(cond, ARM_B(b_imm(ctx->ret0_fp_idx, ctx)), ctx);
+		/* NOP to keep the size constant between passes */
+		emit(ARM_MOV_R(ARM_R0, ARM_R0), ctx);
+	} else {
+		_emit(cond, ARM_MOV_I(ARM_R0, 0), ctx);
+		_emit(cond, ARM_B(b_imm(ctx->skf->len, ctx)), ctx);
+	}
+}
+
+static inline void emit_blx_r(u8 tgt_reg, struct jit_ctx *ctx)
+{
+#if __LINUX_ARM_ARCH__ < 5
+	emit(ARM_MOV_R(ARM_LR, ARM_PC), ctx);
+
+	if (elf_hwcap & HWCAP_THUMB)
+		emit(ARM_BX(tgt_reg), ctx);
+	else
+		emit(ARM_MOV_R(ARM_PC, tgt_reg), ctx);
+#else
+	emit(ARM_BLX_R(tgt_reg), ctx);
+#endif
+}
+
+static inline void emit_udiv(u8 rd, u8 rm, u8 rn, struct jit_ctx *ctx)
+{
+#if __LINUX_ARM_ARCH__ == 7
+	if (elf_hwcap & HWCAP_IDIVA) {
+		emit(ARM_UDIV(rd, rm, rn), ctx);
+		return;
+	}
+#endif
+	if (rm != ARM_R0)
+		emit(ARM_MOV_R(ARM_R0, rm), ctx);
+	if (rn != ARM_R1)
+		emit(ARM_MOV_R(ARM_R1, rn), ctx);
+
+	ctx->seen |= SEEN_CALL;
+	emit_mov_i(ARM_R3, (u32)jit_udiv, ctx);
+	emit_blx_r(ARM_R3, ctx);
+
+	if (rd != ARM_R0)
+		emit(ARM_MOV_R(rd, ARM_R0), ctx);
+}
+
+static int build_body(struct jit_ctx *ctx)
+{
+	void *load_func[] = {jit_get_skb_b, jit_get_skb_h, jit_get_skb_w};
+	const struct sk_filter *prog = ctx->skf;
+	const struct sock_filter *inst;
+	unsigned i, load_order, off, condt, condf;
+	int imm12;
+	u32 k;
+
+	for (i = 0; i < prog->len; i++) {
+		inst = &(prog->insns[i]);
+		/* K as an immediate value operand */
+		k = inst->k;
+
+		/* compute offsets only in the fake pass */
+		if (ctx->target == NULL)
+			ctx->offsets[i] = ctx->idx * 4;
+
+		switch (inst->code) {
+		case BPF_S_LD_IMM:
+			emit_mov_i(r_A, k, ctx);
+			break;
+		case BPF_S_LD_W_LEN:
+			ctx->seen |= SEEN_SKB;
+			BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
+			emit(ARM_LDR_I(r_A, r_skb,
+				       offsetof(struct sk_buff, len)), ctx);
+			break;
+		case BPF_S_LD_MEM:
+			/* A = scratch[k] */
+			ctx->seen |= SEEN_MEM_WORD(k);
+			emit(ARM_LDR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx);
+			break;
+		case BPF_S_LD_W_ABS:
+			load_order = 2;
+			goto load;
+		case BPF_S_LD_H_ABS:
+			load_order = 1;
+			goto load;
+		case BPF_S_LD_B_ABS:
+			load_order = 0;
+load:
+			emit_mov_i(r_off, k, ctx);
+load_common:
+			ctx->seen |= SEEN_DATA | SEEN_CALL;
+
+			if (load_order > 0) {
+				emit(ARM_SUB_I(r_scratch, r_skb_hl,
+					       1 << load_order), ctx);
+				emit(ARM_CMP_R(r_scratch, r_off), ctx);
+				condt = ARM_COND_HS;
+			} else {
+				emit(ARM_CMP_R(r_skb_hl, r_off), ctx);
+				condt = ARM_COND_HI;
+			}
+
+			_emit(condt, ARM_ADD_R(r_scratch, r_off, r_skb_data),
+			      ctx);
+
+			if (load_order == 0)
+				_emit(condt, ARM_LDRB_I(r_A, r_scratch, 0),
+				      ctx);
+			else if (load_order == 1)
+				emit_load_be16(condt, r_A, r_scratch, ctx);
+			else if (load_order == 2)
+				emit_load_be32(condt, r_A, r_scratch, ctx);
+
+			_emit(condt, ARM_B(b_imm(i + 1, ctx)), ctx);
+
+			/* the slowpath */
+			emit_mov_i(ARM_R3, (u32)load_func[load_order], ctx);
+			emit(ARM_MOV_R(ARM_R0, r_skb), ctx);
+			/* the offset is already in R1 */
+			emit_blx_r(ARM_R3, ctx);
+			emit(ARM_MOV_R(r_A, ARM_R0), ctx);
+			break;
+		case BPF_S_LD_W_IND:
+			load_order = 2;
+			goto load_ind;
+		case BPF_S_LD_H_IND:
+			load_order = 1;
+			goto load_ind;
+		case BPF_S_LD_B_IND:
+			load_order = 0;
+load_ind:
+			OP_IMM3(ARM_ADD, r_off, r_X, k, ctx);
+			goto load_common;
+		case BPF_S_LDX_IMM:
+			ctx->seen |= SEEN_X;
+			emit_mov_i(r_X, k, ctx);
+			break;
+		case BPF_S_LDX_W_LEN:
+			ctx->seen |= SEEN_X | SEEN_SKB;
+			emit(ARM_LDR_I(r_X, r_skb,
+				       offsetof(struct sk_buff, len)), ctx);
+			break;
+		case BPF_S_LDX_MEM:
+			ctx->seen |= SEEN_X | SEEN_MEM_WORD(k);
+			emit(ARM_LDR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx);
+			break;
+		case BPF_S_LDX_B_MSH:
+			/* x = ((*(frame + k)) & 0xf) << 2; */
+			ctx->seen |= SEEN_X | SEEN_DATA | SEEN_CALL;
+			/* offset in r1: we might have to take the slow path */
+			emit_mov_i(r_off, k, ctx);
+			emit(ARM_CMP_R(r_skb_hl, r_off), ctx);
+
+			/* load in r0: common with the slowpath */
+			_emit(ARM_COND_HI, ARM_LDRB_R(ARM_R0, r_skb_data,
+						      ARM_R1), ctx);
+			/*
+			 * emit_mov_i() might generate one or two instructions,
+			 * the same holds for emit_blx_r()
+			 */
+			_emit(ARM_COND_HI, ARM_B(b_imm(i + 1, ctx) - 2), ctx);
+
+			emit(ARM_MOV_R(ARM_R0, r_skb), ctx);
+			/* r_off is r1 */
+			emit_mov_i(ARM_R3, (u32)jit_get_skb_b, ctx);
+			emit_blx_r(ARM_R3, ctx);
+
+			emit(ARM_ORR_I(r_X, ARM_R0, 0x00f), ctx);
+			emit(ARM_LSL_I(r_X, r_X, 2), ctx);
+			break;
+		case BPF_S_ST:
+			ctx->seen |= SEEN_MEM_WORD(k);
+			emit(ARM_STR_I(r_A, ARM_SP, SCRATCH_OFF(k)), ctx);
+			break;
+		case BPF_S_STX:
+			ctx->seen |= SEEN_MEM_WORD(k) | SEEN_X;
+			emit(ARM_STR_I(r_X, ARM_SP, SCRATCH_OFF(k)), ctx);
+			break;
+		case BPF_S_ALU_ADD_K:
+			/* A += K */
+			OP_IMM3(ARM_ADD, r_A, r_A, k, ctx);
+			break;
+		case BPF_S_ALU_ADD_X:
+			ctx->seen |= SEEN_X;
+			emit(ARM_ADD_R(r_A, r_A, r_X), ctx);
+			break;
+		case BPF_S_ALU_SUB_K:
+			/* A -= K */
+			OP_IMM3(ARM_SUB, r_A, r_A, k, ctx);
+			break;
+		case BPF_S_ALU_SUB_X:
+			ctx->seen |= SEEN_X;
+			emit(ARM_SUB_R(r_A, r_A, r_X), ctx);
+			break;
+		case BPF_S_ALU_MUL_K:
+			/* A *= K */
+			emit_mov_i(r_scratch, k, ctx);
+			emit(ARM_MUL(r_A, r_A, r_scratch), ctx);
+			break;
+		case BPF_S_ALU_MUL_X:
+			ctx->seen |= SEEN_X;
+			emit(ARM_MUL(r_A, r_A, r_X), ctx);
+			break;
+		case BPF_S_ALU_DIV_K:
+			/* current k == reciprocal_value(userspace k) */
+			emit_mov_i(r_scratch, k, ctx);
+			/* A = top 32 bits of the product */
+			emit(ARM_UMULL(r_scratch, r_A, r_A, r_scratch), ctx);
+			break;
+		case BPF_S_ALU_DIV_X:
+			ctx->seen |= SEEN_X;
+			emit(ARM_CMP_I(r_X, 0), ctx);
+			emit_err_ret(ARM_COND_EQ, ctx);
+			emit_udiv(r_A, r_A, r_X, ctx);
+			break;
+		case BPF_S_ALU_OR_K:
+			/* A |= K */
+			OP_IMM3(ARM_ORR, r_A, r_A, k, ctx);
+			break;
+		case BPF_S_ALU_OR_X:
+			ctx->seen |= SEEN_X;
+			emit(ARM_ORR_I(r_A, r_A, r_X), ctx);
+			break;
+		case BPF_S_ALU_AND_K:
+			/* A &= K */
+			OP_IMM3(ARM_AND, r_A, r_A, k, ctx);
+			break;
+		case BPF_S_ALU_AND_X:
+			ctx->seen |= SEEN_X;
+			emit(ARM_AND_R(r_A, r_A, r_X), ctx);
+			break;
+		case BPF_S_ALU_LSH_K:
+			if (unlikely(k > 31))
+				return -1;
+			emit(ARM_LSL_I(r_A, r_A, k), ctx);
+			break;
+		case BPF_S_ALU_LSH_X:
+			ctx->seen |= SEEN_X;
+			emit(ARM_LSL_R(r_A, r_A, r_X), ctx);
+			break;
+		case BPF_S_ALU_RSH_K:
+			if (unlikely(k > 31))
+				return -1;
+			emit(ARM_LSR_I(r_A, r_A, k), ctx);
+			break;
+		case BPF_S_ALU_RSH_X:
+			ctx->seen |= SEEN_X;
+			emit(ARM_LSR_R(r_A, r_A, r_X), ctx);
+			break;
+		case BPF_S_JMP_JA:
+			/* pc += K */
+			emit(ARM_B(b_imm(i + k, ctx)), ctx);
+			break;
+		case BPF_S_JMP_JEQ_K:
+			/* pc += (A == K) ? pc->jt : pc->jf */
+			condt   = ARM_COND_EQ;
+			condf  = ARM_COND_NE;
+			goto cmp_imm;
+		case BPF_S_JMP_JGT_K:
+			/* pc += (A > K) ? pc->jt : pc->jf */
+			condt   = ARM_COND_HI;
+			condf  = ARM_COND_LS;
+			goto cmp_imm;
+		case BPF_S_JMP_JGE_K:
+			/* pc += (A >= K) ? pc->jt : pc->jf */
+			condt   = ARM_COND_HI;
+			condf  = ARM_COND_LS;
+			/* (x >= y) IFF (x > y - 1) */
+			if (unlikely(k == 0))
+				return -1;
+			k--;
+cmp_imm:
+			imm12 = imm8m(k);
+			if (imm12 < 0) {
+				emit_mov_i_no8m(r_scratch, k, ctx);
+				emit(ARM_CMP_R(r_A, r_scratch), ctx);
+			} else {
+				emit(ARM_CMP_I(r_A, imm12), ctx);
+			}
+cond_jump:
+			if (inst->jt)
+				_emit(condt, ARM_B(b_imm(i + inst->jt + 1,
+						   ctx)), ctx);
+			if (inst->jf)
+				_emit(condf, ARM_B(b_imm(i + inst->jf + 1,
+						   ctx)), ctx);
+			break;
+		case BPF_S_JMP_JEQ_X:
+			/* pc += (A == X) ? pc->jt : pc->jf */
+			condt   = ARM_COND_EQ;
+			condf  = ARM_COND_NE;
+			goto cmp_x;
+		case BPF_S_JMP_JGT_X:
+			/* pc += (A > X) ? pc->jt : pc->jf */
+			condt   = ARM_COND_HI;
+			condf  = ARM_COND_LS;
+			goto cmp_x;
+		case BPF_S_JMP_JGE_X:
+			/* pc += (A >= X) ? pc->jt : pc->jf */
+			condt   = ARM_COND_CS;
+			condf  = ARM_COND_CC;
+cmp_x:
+			ctx->seen |= SEEN_X;
+			emit(ARM_CMP_R(r_A, r_X), ctx);
+			goto cond_jump;
+		case BPF_S_JMP_JSET_K:
+			/* pc += (A & K) ? pc->jt : pc->jf */
+			condt  = ARM_COND_NE;
+			/* not set iff all zeroes iff Z==1 iff EQ */
+			condf  = ARM_COND_EQ;
+
+			imm12 = imm8m(k);
+			if (imm12 < 0) {
+				emit_mov_i_no8m(r_scratch, k, ctx);
+				emit(ARM_TST_R(r_A, r_scratch), ctx);
+			} else {
+				emit(ARM_TST_I(r_A, imm12), ctx);
+			}
+			goto cond_jump;
+		case BPF_S_JMP_JSET_X:
+			/* pc += (A & X) ? pc->jt : pc->jf */
+			condt  = ARM_COND_NE;
+			condf  = ARM_COND_EQ;
+			emit(ARM_TST_R(r_A, r_X), ctx);
+			goto cond_jump;
+		case BPF_S_RET_A:
+			emit(ARM_MOV_R(ARM_R0, r_A), ctx);
+			goto b_epilogue;
+		case BPF_S_RET_K:
+			if ((k == 0) && (ctx->ret0_fp_idx < 0))
+				ctx->ret0_fp_idx = i;
+			emit_mov_i(ARM_R0, k, ctx);
+b_epilogue:
+			if (i != ctx->skf->len - 1)
+				emit(ARM_B(b_imm(prog->len, ctx)), ctx);
+			break;
+		case BPF_S_MISC_TAX:
+			/* X = A */
+			emit(ARM_MOV_R(r_X, r_A), ctx);
+			break;
+		case BPF_S_MISC_TXA:
+			/* A = X */
+			emit(ARM_MOV_R(r_A, r_X), ctx);
+			break;
+		case BPF_S_ANC_PROTOCOL:
+			/* A = ntohs(skb->protocol) */
+			ctx->seen |= SEEN_SKB;
+			BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
+						  protocol) != 2);
+			off = offsetof(struct sk_buff, protocol);
+			emit(ARM_LDRH_I(r_scratch, r_skb, off), ctx);
+			emit_swap16(r_A, r_scratch, ctx);
+			break;
+		case BPF_S_ANC_CPU:
+			/* r_scratch = current_thread_info() */
+			OP_IMM3(ARM_BIC, r_scratch, ARM_SP, THREAD_SIZE - 1, ctx);
+			/* A = current_thread_info()->cpu */
+			BUILD_BUG_ON(FIELD_SIZEOF(struct thread_info, cpu) != 4);
+			off = offsetof(struct thread_info, cpu);
+			emit(ARM_LDR_I(r_A, r_scratch, off), ctx);
+			break;
+		case BPF_S_ANC_IFINDEX:
+			/* A = skb->dev->ifindex */
+			ctx->seen |= SEEN_SKB;
+			off = offsetof(struct sk_buff, dev);
+			emit(ARM_LDR_I(r_scratch, r_skb, off), ctx);
+
+			emit(ARM_CMP_I(r_scratch, 0), ctx);
+			emit_err_ret(ARM_COND_EQ, ctx);
+
+			BUILD_BUG_ON(FIELD_SIZEOF(struct net_device,
+						  ifindex) != 4);
+			off = offsetof(struct net_device, ifindex);
+			emit(ARM_LDR_I(r_A, r_scratch, off), ctx);
+			break;
+		case BPF_S_ANC_MARK:
+			ctx->seen |= SEEN_SKB;
+			BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
+			off = offsetof(struct sk_buff, mark);
+			emit(ARM_LDR_I(r_A, r_skb, off), ctx);
+			break;
+		case BPF_S_ANC_RXHASH:
+			ctx->seen |= SEEN_SKB;
+			BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4);
+			off = offsetof(struct sk_buff, rxhash);
+			emit(ARM_LDR_I(r_A, r_skb, off), ctx);
+			break;
+		case BPF_S_ANC_QUEUE:
+			ctx->seen |= SEEN_SKB;
+			BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff,
+						  queue_mapping) != 2);
+			BUILD_BUG_ON(offsetof(struct sk_buff,
+					      queue_mapping) > 0xff);
+			off = offsetof(struct sk_buff, queue_mapping);
+			emit(ARM_LDRH_I(r_A, r_skb, off), ctx);
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	/* compute offsets only during the first pass */
+	if (ctx->target == NULL)
+		ctx->offsets[i] = ctx->idx * 4;
+
+	return 0;
+}
+
+
+void bpf_jit_compile(struct sk_filter *fp)
+{
+	struct jit_ctx ctx;
+	unsigned tmp_idx;
+	unsigned alloc_size;
+
+	if (!bpf_jit_enable)
+		return;
+
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.skf		= fp;
+	ctx.ret0_fp_idx = -1;
+
+	ctx.offsets = kzalloc(GFP_KERNEL, 4 * (ctx.skf->len + 1));
+	if (ctx.offsets == NULL)
+		return;
+
+	/* fake pass to fill in the ctx->seen */
+	if (unlikely(build_body(&ctx)))
+		goto out;
+
+	tmp_idx = ctx.idx;
+	build_prologue(&ctx);
+	ctx.prologue_bytes = (ctx.idx - tmp_idx) * 4;
+
+#if __LINUX_ARM_ARCH__ < 7
+	tmp_idx = ctx.idx;
+	build_epilogue(&ctx);
+	ctx.epilogue_bytes = (ctx.idx - tmp_idx) * 4;
+
+	ctx.idx += ctx.imm_count;
+	if (ctx.imm_count) {
+		ctx.imms = kzalloc(GFP_KERNEL, 4 * ctx.imm_count);
+		if (ctx.imms == NULL)
+			goto out;
+	}
+#else
+	/* there's nothing after the epilogue on ARMv7 */
+	build_epilogue(&ctx);
+#endif
+
+	alloc_size = 4 * ctx.idx;
+	ctx.target = module_alloc(alloc_size > sizeof(struct work_struct) ?
+				  alloc_size : sizeof(struct work_struct));
+	if (unlikely(ctx.target == NULL))
+		goto out;
+
+	ctx.idx = 0;
+	build_prologue(&ctx);
+	build_body(&ctx);
+	build_epilogue(&ctx);
+
+	flush_icache_range((u32)ctx.target, (u32)(ctx.target + ctx.idx));
+
+#if __LINUX_ARM_ARCH__ < 7
+	if (ctx.imm_count)
+		kfree(ctx.imms);
+#endif
+
+	if (bpf_jit_enable > 1)
+		print_hex_dump(KERN_INFO, "BPF JIT code: ",
+			       DUMP_PREFIX_ADDRESS, 16, 4, ctx.target,
+			       alloc_size, false);
+
+	fp->bpf_func = (void *)ctx.target;
+out:
+	kfree(ctx.offsets);
+	return;
+}
+
+static void bpf_jit_free_worker(struct work_struct *work)
+{
+	module_free(NULL, work);
+}
+
+void bpf_jit_free(struct sk_filter *fp)
+{
+	struct work_struct *work;
+
+	if (fp->bpf_func != sk_run_filter) {
+		work = (struct work_struct *)fp->bpf_func;
+
+		INIT_WORK(work, bpf_jit_free_worker);
+		schedule_work(work);
+	}
+}
+
diff --git a/arch/arm/net/bpf_jit_32.h b/arch/arm/net/bpf_jit_32.h
new file mode 100644
index 0000000..59ce83e
--- /dev/null
+++ b/arch/arm/net/bpf_jit_32.h
@@ -0,0 +1,186 @@
+/*
+ * Just-In-Time compiler for BPF filters on 32bit ARM
+ *
+ * Copyright (c) 2011 Mircea Gherzan <mgherzan@gmail.com>
+ *
+ * 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; version 2 of the License.
+ */
+
+#ifndef PFILTER_OPCODES_ARM_H
+#define PFILTER_OPCODES_ARM_H
+
+#define ARM_R0	0
+#define ARM_R1	1
+#define ARM_R2	2
+#define ARM_R3	3
+#define ARM_R4	4
+#define ARM_R5	5
+#define ARM_R6	6
+#define ARM_R7	7
+#define ARM_R8	8
+#define ARM_R9	9
+#define ARM_R10	10
+#define ARM_FP	11
+#define ARM_IP	12
+#define ARM_SP	13
+#define ARM_LR	14
+#define ARM_PC	15
+
+#define ARM_COND_EQ		0x0
+#define ARM_COND_NE		0x1
+#define ARM_COND_CS		0x2
+#define ARM_COND_HS		ARM_COND_CS
+#define ARM_COND_CC		0x3
+#define ARM_COND_LO		ARM_COND_CC
+#define ARM_COND_MI		0x4
+#define ARM_COND_PL		0x5
+#define ARM_COND_VS		0x6
+#define ARM_COND_VC		0x7
+#define ARM_COND_HI		0x8
+#define ARM_COND_LS		0x9
+#define ARM_COND_GE		0xa
+#define ARM_COND_LT		0xb
+#define ARM_COND_GT		0xc
+#define ARM_COND_LE		0xd
+#define ARM_COND_AL		0xe
+
+/* register shift types */
+#define SRTYPE_LSL		0
+#define SRTYPE_LSR		1
+#define SRTYPE_ASR		2
+#define SRTYPE_ROR		3
+
+#define ARM_INST_ADD_R		0x00800000
+#define ARM_INST_ADD_I		0x02800000
+
+#define ARM_INST_AND_R		0x00000000
+#define ARM_INST_AND_I		0x02000000
+
+#define ARM_INST_BIC_R		0x01c00000
+#define ARM_INST_BIC_I		0x03c00000
+
+#define ARM_INST_B		0x0a000000
+#define ARM_INST_BX		0x012FFF10
+#define ARM_INST_BLX_R		0x012fff30
+
+#define ARM_INST_CMP_R		0x01500000
+#define ARM_INST_CMP_I		0x03500000
+
+#define ARM_INST_LDRB_I		0x05d00000
+#define ARM_INST_LDRB_R		0x07d00000
+#define ARM_INST_LDRH_I		0x01d000b0
+#define ARM_INST_LDR_I		0x05900000
+
+#define ARM_INST_LDM		0x08900000
+
+#define ARM_INST_LSL_I		0x01a00000
+#define ARM_INST_LSL_R		0x01a00010
+
+#define ARM_INST_LSR_I		0x01a00020
+#define ARM_INST_LSR_R		0x01a00030
+
+#define ARM_INST_MOV_R		0x01a00000
+#define ARM_INST_MOV_I		0x03a00000
+#define ARM_INST_MOVW		0x03000000
+#define ARM_INST_MOVT		0x03400000
+
+#define ARM_INST_MUL		0x00000090
+
+#define ARM_INST_POP		0x08bd0000
+#define ARM_INST_PUSH		0x092d0000
+
+#define ARM_INST_REV		0x06bf0f30
+#define ARM_INST_REV16		0x06bf0fb0
+
+#define ARM_INST_ORR_R		0x01800000
+#define ARM_INST_ORR_I		0x03800000
+
+#define ARM_INST_SUB_R		0x00400000
+#define ARM_INST_SUB_I		0x02400000
+
+#define ARM_INST_STR_I		0x05800000
+
+#define ARM_INST_TST_R		0x01100000
+#define ARM_INST_TST_I		0x03100000
+
+#define ARM_INST_UDIV		0x0730f010
+
+#define ARM_INST_UMULL		0x00800090
+
+/* register */
+#define _AL3_R(op, rd, rn, rm)	((op ## _R) | (rd) << 12 | (rn) << 16 | (rm))
+/* immediate */
+#define _AL3_I(op, rd, rn, imm)	((op ## _I) | (rd) << 12 | (rn) << 16 | (imm))
+
+#define ARM_ADD_R(rd, rn, rm)	_AL3_R(ARM_INST_ADD, rd, rn, rm)
+#define ARM_ADD_I(rd, rn, imm)	_AL3_I(ARM_INST_ADD, rd, rn, imm)
+
+#define ARM_AND_R(rd, rn, rm)	_AL3_R(ARM_INST_AND, rd, rn, rm)
+#define ARM_AND_I(rd, rn, imm)	_AL3_I(ARM_INST_AND, rd, rn, imm)
+
+#define ARM_BIC_R(rd, rn, rm)	_AL3_R(ARM_INST_BIC, rd, rn, rm)
+#define ARM_BIC_I(rd, rn, imm)	_AL3_I(ARM_INST_BIC, rd, rn, imm)
+
+#define ARM_B(imm24)		(ARM_INST_B | (imm24))
+#define ARM_BX(rm)		(ARM_INST_BX | (rm))
+#define ARM_BLX_R(rm)		(ARM_INST_BLX_R | (rm))
+
+#define ARM_CMP_R(rn, rm)	_AL3_R(ARM_INST_CMP, 0, rn, rm)
+#define ARM_CMP_I(rn, imm)	_AL3_I(ARM_INST_CMP, 0, rn, imm)
+
+#define ARM_LDR_I(rt, rn, off)	(ARM_INST_LDR_I | (rt) << 12 | (rn) << 16 \
+				 | (off))
+#define ARM_LDRB_I(rt, rn, off)	(ARM_INST_LDRB_I | (rt) << 12 | (rn) << 16 \
+				 | (off))
+#define ARM_LDRB_R(rt, rn, rm)	(ARM_INST_LDRB_R | (rt) << 12 | (rn) << 16 \
+				 | (rm))
+#define ARM_LDRH_I(rt, rn, off)	(ARM_INST_LDRH_I | (rt) << 12 | (rn) << 16 \
+				 | (((off) & 0xf0) << 4) | ((off) & 0xf))
+
+#define ARM_LDM(rn, regs)	(ARM_INST_LDM | (rn) << 16 | (regs))
+
+#define ARM_LSL_R(rd, rn, rm)	(_AL3_R(ARM_INST_LSL, rd, 0, rn) | (rm) << 8)
+#define ARM_LSL_I(rd, rn, imm)	(_AL3_R(ARM_INST_LSL, rd, 0, rn) | (imm) << 7)
+
+#define ARM_LSR_R(rd, rn, rm)	(_AL3_R(ARM_INST_LSR, rd, 0, rn) | (rm) << 8)
+#define ARM_LSR_I(rd, rn, imm)	(_AL3_R(ARM_INST_LSR, rd, 0, rn) | (imm) << 7)
+
+#define ARM_MOV_R(rd, rm)	_AL3_R(ARM_INST_MOV, rd, 0, rm)
+#define ARM_MOV_I(rd, imm)	_AL3_I(ARM_INST_MOV, rd, 0, imm)
+
+#define ARM_MOVW(rd, imm)	\
+	(ARM_INST_MOVW | ((imm) >> 12) << 16 | (rd) << 12 | ((imm) & 0x0fff))
+
+#define ARM_MOVT(rd, imm)	\
+	(ARM_INST_MOVT | ((imm) >> 12) << 16 | (rd) << 12 | ((imm) & 0x0fff))
+
+#define ARM_MUL(rd, rm, rn)	(ARM_INST_MUL | (rd) << 16 | (rm) << 8 | (rn))
+
+#define ARM_POP(regs)		(ARM_INST_POP | (regs))
+#define ARM_PUSH(regs)		(ARM_INST_PUSH | (regs))
+
+#define ARM_ORR_R(rd, rn, rm)	_AL3_R(ARM_INST_ORR, rd, rn, rm)
+#define ARM_ORR_I(rd, rn, imm)	_AL3_I(ARM_INST_ORR, rd, rn, imm)
+#define ARM_ORR_S(rd, rn, rm, type, rs)	\
+	(ARM_ORR_R(rd, rn, rm) | (type) << 5 | (rs) << 7)
+
+#define ARM_REV(rd, rm)		(ARM_INST_REV | (rd) << 12 | (rm))
+#define ARM_REV16(rd, rm)	(ARM_INST_REV16 | (rd) << 12 | (rm))
+
+#define ARM_SUB_R(rd, rn, rm)	_AL3_R(ARM_INST_SUB, rd, rn, rm)
+#define ARM_SUB_I(rd, rn, imm)	_AL3_I(ARM_INST_SUB, rd, rn, imm)
+
+#define ARM_STR_I(rt, rn, off)	(ARM_INST_STR_I | (rt) << 12 | (rn) << 16 \
+				 | (off))
+
+#define ARM_TST_R(rn, rm)	_AL3_R(ARM_INST_TST, 0, rn, rm)
+#define ARM_TST_I(rn, imm)	_AL3_I(ARM_INST_TST, 0, rn, imm)
+
+#define ARM_UDIV(rd, rn, rm)	(ARM_INST_UDIV | (rd) << 16 | (rn) | (rm) << 8)
+
+#define ARM_UMULL(rd_lo, rd_hi, rn, rm)	(ARM_INST_UMULL | (rd_hi) << 16 \
+					 | (rd_lo) << 12 | (rm) << 8 | rn)
+
+#endif /* PFILTER_OPCODES_ARM_H */
-- 
1.7.7.3

^ permalink raw reply related

* Re: [PATCH] tcp: detect loss above high_seq in recovery
From: Ilpo Järvinen @ 2011-12-28 15:42 UTC (permalink / raw)
  To: Yuchung Cheng; +Cc: David Miller, ncardwell, nanditad, Netdev
In-Reply-To: <1325033182-25905-1-git-send-email-ycheng@google.com>

On Tue, 27 Dec 2011, Yuchung Cheng wrote:

> Correctly implement a loss detection heuristic: New sequences (above
> high_seq) sent during the fast recovery are deemed lost when higher
> sequences are SACKed.
> 
> Current code does not catch these losses, because tcp_mark_head_lost()
> does not check packets beyond high_seq. The fix is straight-forward by
> checking packets until the highest sacked packet. In addition, all the
> FLAG_DATA_LOST logic are in-effective and redundant and can be removed.

I agree that FLAG_DATA_LOST never did anything very useful... I've never 
figure out why it was there (perhaps some earlier change made it broken or 
so before I've been tracking TCP dev).

> The new sequences sent during fast-recovery maybe marked as lost
> and/or retransmitted. It is possible these (new) losses are recovered
> within the current fast recovery and the state moves back to CA_Open
> without entering another fast recovery / cwnd redunction. This is
> especially possible for light losses.  Note RFC 3517 (implicitly)
> allows this as well.

No it doesn't! It does not allow you to avoid the second cwnd reduction. 
They're from different RTT. What you now claim is that we never need to 
do cwnd reduction until we visit CA_Open in between. That's very very 
dangerous if the congestion suddently spikes because nobody reduces twice 
causing everyone to get losses and continues in the recovery forever.

> Update the loss heuristic comments. The algorithm above is documented
> as heuristic B, but it is redundant too because heuristic A already
> covers B.
> 
> Signed-off-by: Yuchung Cheng <ycheng@google.com>
> ---
>  include/linux/snmp.h |    1 -
>  net/ipv4/proc.c      |    1 -
>  net/ipv4/tcp_input.c |   41 +++++++++++++++--------------------------
>  3 files changed, 15 insertions(+), 28 deletions(-)
> 
> diff --git a/include/linux/snmp.h b/include/linux/snmp.h
> index e16557a..c1241c42 100644
> --- a/include/linux/snmp.h
> +++ b/include/linux/snmp.h
> @@ -192,7 +192,6 @@ enum
>  	LINUX_MIB_TCPPARTIALUNDO,		/* TCPPartialUndo */
>  	LINUX_MIB_TCPDSACKUNDO,			/* TCPDSACKUndo */
>  	LINUX_MIB_TCPLOSSUNDO,			/* TCPLossUndo */
> -	LINUX_MIB_TCPLOSS,			/* TCPLoss */
>  	LINUX_MIB_TCPLOSTRETRANSMIT,		/* TCPLostRetransmit */
>  	LINUX_MIB_TCPRENOFAILURES,		/* TCPRenoFailures */
>  	LINUX_MIB_TCPSACKFAILURES,		/* TCPSackFailures */
> diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c
> index 3569d8e..6afc807 100644
> --- a/net/ipv4/proc.c
> +++ b/net/ipv4/proc.c
> @@ -216,7 +216,6 @@ static const struct snmp_mib snmp4_net_list[] = {
>  	SNMP_MIB_ITEM("TCPPartialUndo", LINUX_MIB_TCPPARTIALUNDO),
>  	SNMP_MIB_ITEM("TCPDSACKUndo", LINUX_MIB_TCPDSACKUNDO),
>  	SNMP_MIB_ITEM("TCPLossUndo", LINUX_MIB_TCPLOSSUNDO),
> -	SNMP_MIB_ITEM("TCPLoss", LINUX_MIB_TCPLOSS),

I don't think you can remove MIBs as they're userspace visible.


-- 
 i.

^ permalink raw reply

* [Announce] LARTC wiki available
From: Niccolò Belli @ 2011-12-28 15:52 UTC (permalink / raw)
  To: lartc; +Cc: netfilter@vger.kernel.org,
	Linux Networking Developer Mailing List

Hi,
I still didn't find a viable solution for the LARTC wiki, so I decided 
to start hosting it on my own server. Later we can easily switch 
somewhere else if we keep using the same wiki engine (and maybe even 
with another wiki engine).
I decided to use wikimedia because it's the only one I know of, so if 
someone knows a better alternative please let me know, we are still in 
time for a change.
Since I never used a wiki seriously I will probably need someone else 
who can help me maintaining it, please let me know if you are 
experienced and willing to help.

Here is the wiki: http://lartc.linuxsystems.it/
And here is the new mailing list for those who still don't know: 
http://vger.kernel.org/vger-lists.html#lartc

I just copy-pasted the Linux Advanced Routing & Traffic Control HOWTO 
atm, it still needs to be wikified and we still need to choose how to 
organize the contents.

Cheers,
Niccolò

^ permalink raw reply

* Re: [PATCH v3] ARM: net: JIT compiler for packet filters
From: Mircea Gherzan @ 2011-12-28 15:51 UTC (permalink / raw)
  To: David Miller; +Cc: linux-arm-kernel, netdev, linux
In-Reply-To: <20111220.135526.82432216865290827.davem@davemloft.net>

Am 20.12.2011 19:55, schrieb David Miller:
> From: Mircea Gherzan<mgherzan@gmail.com>
> Date: Tue, 20 Dec 2011 19:04:23 +0100
>
>> Based of Matt Evans's PPC64 implementation.
>>
>> The compiler generates ARM instructions but interworking is
>> supported for Thumb2 kernels.
>>
>> Supports both little and big endian. Unaligned loads are emitted
>> for ARMv6+. Not all the BPF opcodes that deal with ancillary data
>> are supported. The scratch memory of the filter lives on the stack.
>> Hardware integer division is used if it is available.
>>
>> Enabled in the same way as for x86-64 and PPC64:
>>
>> 	echo 1>  /proc/sys/net/core/bpf_jit_enable
>>
>> A value greater than 1 enables opcode output.
>>
>> Signed-off-by: Mircea Gherzan<mgherzan@gmail.com>
>
> I'm happy with this going in via the ARM tree once all the details
> are worked out:

I do not really care that much about the tree where this is going, as 
long as it gets into 3.3 :) If you want to take it via the -net tree, 
please synchronize with Russell (I've uploaded the patch to his tracking 
system [1]).

Thanks,
Mircea

[1] http://www.arm.linux.org.uk/developer/patche/viewpatch.php?id=7259/1

> Acked-by: David S. Miller<davem@davemloft.net>

^ permalink raw reply

* Re: [PATCH 1/1] via-rhine: Fix hanging with high CPU load on low-end broads.
From: Ben Hutchings @ 2011-12-28 17:13 UTC (permalink / raw)
  To: Bjarke Istrup Pedersen; +Cc: linux-kernel, netdev, Roger Luethi
In-Reply-To: <CACPM=kWpGnBOuKPUK9MgzyETe4VmvEtVXkYtPA_+Xgn=COkCPw@mail.gmail.com>

On Wed, 2011-12-28 at 16:14 +0100, Bjarke Istrup Pedersen wrote:
> 2011/12/28 Ben Hutchings <bhutchings@solarflare.com>:
> > On Wed, 2011-12-28 at 12:28 +0000, Bjarke Istrup Pedersen wrote:
> >> Working around problem causing high CPU load and hanging system when
> >> there is alot of network trafic.
> >>
> >> It is kind of an ugly way to work around it, but it allows the Soekris
> >> net5501 to have trafic between two of it's NICs without hanging so much
> >> that the watchdog kicks in and does a hard reboot of the system.
> >>
> >> There is more info on the problem here:
> >> http://http://lists.soekris.com/pipermail/soekris-tech/2010-October/016889.html
> >>
> >> Tested with positive results on two Soekris net5501-70 boxes.
> >
> > This is completely wrong.  In a UP configuration the extra spinlock
> > calls have no effect (except perhaps a small delay).  In an SMP
> > configuration they will cause rhine_tx() to deadlock when it also tries
> > to acquire the spinlock.
> >
> > Ben.
> 
> Okay, the Soekris net5501-70 boxes are single-core, and I haven't got
> any SMP boxes with that nic.
> Is there a better solution for the problem then, to avoid it hanging
> the box on a non-smp machine with a slow (500mhz) cpu?

If the system actually hangs then I assume there is some bug in the
driver.  I would guess the actual problem is that the interrupt and NAPI
handlers are running constantly so that user processes never run (which
I think counts as soft lockup).

If the hardware supports it, interrupt moderation may help a little by
slightly reducing the per-packet processing cost, but it isn't a full
solution.  Or you can use a real-time kernel, which schedules interrupt
and NAPI handlers as tasks, and adjust priorities so that user processes
can still run.  But that brings its own problems (including generally
lower throughput).

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.

^ permalink raw reply

* Re: [PATCH] skge: restore multicast rx filter on resume
From: Stephen Hemminger @ 2011-12-28 17:27 UTC (permalink / raw)
  To: Florian Zumbiehl; +Cc: David Miller, netdev
In-Reply-To: <20111228074143.GA8041@florz.florz.dyndns.org>

On Wed, 28 Dec 2011 08:41:43 +0100
Florian Zumbiehl <florz@florz.de> wrote:

> Hi,
> 
> > > > diff --git a/drivers/net/skge.c b/drivers/net/skge.c
> > > > index f4be5c7..cd968e5 100644
> > > > --- a/drivers/net/skge.c
> > > > +++ b/drivers/net/skge.c
> > > > @@ -4046,6 +4046,7 @@ static int skge_resume(struct device *dev)
> > > >  				dev_close(dev);
> > > >  				goto out;
> > > >  			}
> > > > +			skge_set_multicast(dev);
> > > >  		}
> > > >  	}
> > > >  out:
> > 
> > Right idea, but it needs to be done in a different spot to catch
> > all the other device restart cases like changing settings.
> 
> Roughly like this maybe? (Successfully tested with an MTU change ...)
> 
> Florian
> 
> ---------------------------------------------------------------------------
> skge: restore rx multicast filter on resume
> 
> Signed-off-by: Florian Zumbiehl <florz@florz.de>
> 
> diff --git a/drivers/net/skge.c b/drivers/net/skge.c
> index f4be5c7..8f985a8 100644
> --- a/drivers/net/skge.c
> +++ b/drivers/net/skge.c
> @@ -2587,6 +2587,9 @@ static int skge_up(struct net_device *dev)
>  	spin_unlock_irq(&hw->hw_lock);
>  
>  	napi_enable(&skge->napi);
> +
> +	skge_set_multicast(dev);
> +
>  	return 0;
>  
>   free_rx_ring:

Looks good. That is (basically) the same as the patch I was testing.

Acked-by: Stephen Hemminger <shemminger@vyatta.com>

^ permalink raw reply

* Re: [PATCH 1/1] via-rhine: Fix hanging with high CPU load on low-end broads.
From: Bjarke Istrup Pedersen @ 2011-12-28 17:30 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: linux-kernel, netdev, Roger Luethi
In-Reply-To: <1325092423.2327.70.camel@deadeye>

2011/12/28 Ben Hutchings <bhutchings@solarflare.com>:
> On Wed, 2011-12-28 at 16:14 +0100, Bjarke Istrup Pedersen wrote:
>> 2011/12/28 Ben Hutchings <bhutchings@solarflare.com>:
>> > On Wed, 2011-12-28 at 12:28 +0000, Bjarke Istrup Pedersen wrote:
>> >> Working around problem causing high CPU load and hanging system when
>> >> there is alot of network trafic.
>> >>
>> >> It is kind of an ugly way to work around it, but it allows the Soekris
>> >> net5501 to have trafic between two of it's NICs without hanging so much
>> >> that the watchdog kicks in and does a hard reboot of the system.
>> >>
>> >> There is more info on the problem here:
>> >> http://http://lists.soekris.com/pipermail/soekris-tech/2010-October/016889.html
>> >>
>> >> Tested with positive results on two Soekris net5501-70 boxes.
>> >
>> > This is completely wrong.  In a UP configuration the extra spinlock
>> > calls have no effect (except perhaps a small delay).  In an SMP
>> > configuration they will cause rhine_tx() to deadlock when it also tries
>> > to acquire the spinlock.
>> >
>> > Ben.
>>
>> Okay, the Soekris net5501-70 boxes are single-core, and I haven't got
>> any SMP boxes with that nic.
>> Is there a better solution for the problem then, to avoid it hanging
>> the box on a non-smp machine with a slow (500mhz) cpu?
>
> If the system actually hangs then I assume there is some bug in the
> driver.  I would guess the actual problem is that the interrupt and NAPI
> handlers are running constantly so that user processes never run (which
> I think counts as soft lockup).
>
> If the hardware supports it, interrupt moderation may help a little by
> slightly reducing the per-packet processing cost, but it isn't a full
> solution.  Or you can use a real-time kernel, which schedules interrupt
> and NAPI handlers as tasks, and adjust priorities so that user processes
> can still run.  But that brings its own problems (including generally
> lower throughput).
>
> Ben.

That would be an option, but I don't think the hardware supports it,
and it doesn't fix the problems in the driver, just hides them.
From what I can read in the thread I linked to in the patch, the
problem only exists in the Linux driver - *BSD isn't affected by this,
on the same hardware.

Since the hack here fixes the problem on non-smp machines, it seems
like there are some race conditions in the interrupt code in the
driver, like you mention.
I'm not well enough into this driver to be able to pinpoint what's
causing it, but if somebody else is, and got some ideas, I'll be more
than willing to test :)

/Bjarke

> --
> Ben Hutchings, Staff Engineer, Solarflare
> Not speaking for my employer; that's the marketing department's job.
> They asked us to note that Solarflare product names are trademarked.
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* RE: bonding device in balance-alb mode shows packet loss in kernel 3.2-rc6
From: Narendra_K @ 2011-12-28 17:59 UTC (permalink / raw)
  To: nicolas.2p.debian; +Cc: netdev, fubar, Shyam_Iyer, Surya_Prabhakar
In-Reply-To: <E31FB011129F30488D5861F38390491520C52BCE6C@BLRX7MCDC201.AMER.DELL.COM>


> > -----Original Message-----
> > From: netdev-owner@vger.kernel.org [mailto:netdev-
> > owner@vger.kernel.org] On Behalf Of Nicolas de Pesloüan
> > Sent: Wednesday, December 28, 2011 1:26 AM
> > To: K, Narendra
> > Cc: netdev@vger.kernel.org; fubar@us.ibm.com
> > Subject: Re: bonding device in balance-alb mode shows packet loss in
> > kernel
> > 3.2-rc6
> >
> > Le 27/12/2011 15:31, Narendra_K@Dell.com a écrit :
> > > Hello,
> > >
> > > On kernel version 3.2-rc6, when a bonding device is configured in
> > > 'balance-alb' mode, ping reported packet losses. By looking at
> > > protocol trance, it seemed like the lost packets had the destination
> > > MAC id
> > of inactive slave.
> > >
> >
> > [snip]
> >
> > Can you provide the output of the following command?
> >
> > grep . /sys/classs/net/bond0/bonding/*
> 
> Hi, thanks for the response. Please find the output here -
> 
> # grep . /sys/class/net/bond0/bonding/*
> /sys/class/net/bond0/bonding/active_slave:em2
> /sys/class/net/bond0/bonding/ad_select:stable 0
> /sys/class/net/bond0/bonding/all_slaves_active:0
> /sys/class/net/bond0/bonding/arp_interval:0
> /sys/class/net/bond0/bonding/arp_validate:none 0
> /sys/class/net/bond0/bonding/downdelay:0
> /sys/class/net/bond0/bonding/fail_over_mac:none 0
> /sys/class/net/bond0/bonding/lacp_rate:slow 0
> /sys/class/net/bond0/bonding/miimon:100
> /sys/class/net/bond0/bonding/mii_status:up
> /sys/class/net/bond0/bonding/min_links:0
> /sys/class/net/bond0/bonding/mode:balance-alb 6
> /sys/class/net/bond0/bonding/num_grat_arp:1
> /sys/class/net/bond0/bonding/num_unsol_na:1
> /sys/class/net/bond0/bonding/primary_reselect:always 0
> /sys/class/net/bond0/bonding/queue_id:em2:0 em3:0 em4:0
> /sys/class/net/bond0/bonding/resend_igmp:1
> /sys/class/net/bond0/bonding/slaves:em2 em3 em4
> /sys/class/net/bond0/bonding/updelay:0
> /sys/class/net/bond0/bonding/use_carrier:1
> /sys/class/net/bond0/bonding/xmit_hash_policy:layer2 0

Hi, this information might be useful. As setting the 'inactive slave' to promisc mode stopped the packet drops and "eth_type_trans" showed only ARP broadcasts on the inactive slave,  I manually  assigned the 'permanent HW address' to the inactive slave and the packet drop stopped .

Ifconfig em3 hw ether <perm HW addr>

With regards,
Narendra K

^ permalink raw reply

* Re: [PATCH] tcp: detect loss above high_seq in recovery
From: David Miller @ 2011-12-28 18:07 UTC (permalink / raw)
  To: ilpo.jarvinen; +Cc: ycheng, ncardwell, nanditad, netdev
In-Reply-To: <alpine.DEB.2.00.1112281736480.7752@melkinpaasi.cs.helsinki.fi>

From: "Ilpo Järvinen" <ilpo.jarvinen@helsinki.fi>
Date: Wed, 28 Dec 2011 17:42:06 +0200 (EET)

> I don't think you can remove MIBs as they're userspace visible.

Because of how they are implemented and how applications use them,
this is actually allowed.

All the MIBs are presented first as a series of strings, then as a
series of values.

So applications parse them both to report the statistics, and this do
get coded in a way that they do not need to assume the presence or
particular ordering of these MIBS.

Therefore, such a change is safe.

^ permalink raw reply

* Re: [PATCH] skge: restore multicast rx filter on resume
From: David Miller @ 2011-12-28 18:09 UTC (permalink / raw)
  To: shemminger; +Cc: florz, netdev
In-Reply-To: <20111228092714.0a6d5f80@nehalam.linuxnetplumber.net>

From: Stephen Hemminger <shemminger@vyatta.com>
Date: Wed, 28 Dec 2011 09:27:14 -0800

> On Wed, 28 Dec 2011 08:41:43 +0100
> Florian Zumbiehl <florz@florz.de> wrote:
> 
>> Hi,
>> 
>> > > > diff --git a/drivers/net/skge.c b/drivers/net/skge.c
>> > > > index f4be5c7..cd968e5 100644
>> > > > --- a/drivers/net/skge.c
>> > > > +++ b/drivers/net/skge.c
>> > > > @@ -4046,6 +4046,7 @@ static int skge_resume(struct device *dev)
>> > > >  				dev_close(dev);
>> > > >  				goto out;
>> > > >  			}
>> > > > +			skge_set_multicast(dev);
>> > > >  		}
>> > > >  	}
>> > > >  out:
>> > 
>> > Right idea, but it needs to be done in a different spot to catch
>> > all the other device restart cases like changing settings.
>> 
>> Roughly like this maybe? (Successfully tested with an MTU change ...)
>> 
>> Florian
>> 
>> ---------------------------------------------------------------------------
>> skge: restore rx multicast filter on resume
>> 
>> Signed-off-by: Florian Zumbiehl <florz@florz.de>
>> 
>> diff --git a/drivers/net/skge.c b/drivers/net/skge.c
>> index f4be5c7..8f985a8 100644
>> --- a/drivers/net/skge.c
>> +++ b/drivers/net/skge.c
>> @@ -2587,6 +2587,9 @@ static int skge_up(struct net_device *dev)
>>  	spin_unlock_irq(&hw->hw_lock);
>>  
>>  	napi_enable(&skge->napi);
>> +
>> +	skge_set_multicast(dev);
>> +
>>  	return 0;
>>  
>>   free_rx_ring:
> 
> Looks good. That is (basically) the same as the patch I was testing.
> 
> Acked-by: Stephen Hemminger <shemminger@vyatta.com>

Florian, please formally resubmit this with proper changelog and
Stephen's ack.

Thanks.

^ permalink raw reply

* Re: [PATCH 1/1] via-rhine: Fix hanging with high CPU load on low-end broads.
From: Stephen Hemminger @ 2011-12-28 18:17 UTC (permalink / raw)
  To: Bjarke Istrup Pedersen; +Cc: Ben Hutchings, linux-kernel, netdev, Roger Luethi
In-Reply-To: <CACPM=kX2hcGt3XXDJkCAcOjv=5oqSHChhhphUAyRhR_PsNFEEQ@mail.gmail.com>

Looks like the hardware isn't really disabling interrupts correctly to support
NAPI. NAPI is supposed to be friendly and under load the work should move to
ksoftirqd. I suspect the IRQ management in this driver is borked.
There is some stupid spin loops in the IRQ handler that happen when looking for
Tx IRQ. I would run with debug set  and see if this is triggering.

^ permalink raw reply

* [PATCH] genetlink: add auto module loading
From: Stephen Hemminger @ 2011-12-28 18:19 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

When testing L2TP support, I discovered that the l2tp module is not autoloaded
as are other netlink interfaces. There is because of lack of hook in genetlink to call
request_module and load the module.

Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>

--- a/net/netlink/genetlink.c	2011-12-24 11:13:44.373214171 -0800
+++ b/net/netlink/genetlink.c	2011-12-24 11:13:59.361360729 -0800
@@ -792,6 +792,15 @@ static int ctrl_getfamily(struct sk_buff
 
 		name = nla_data(info->attrs[CTRL_ATTR_FAMILY_NAME]);
 		res = genl_family_find_byname(name);
+#ifdef CONFIG_MODULES
+		if (res == NULL) {
+			genl_unlock();
+			request_module("net-pf-%d-proto-%d-type-%s",
+				       PF_NETLINK, NETLINK_GENERIC, name);
+			genl_lock();
+			res = genl_family_find_byname(name);
+		}
+#endif
 		err = -ENOENT;
 	}
 

^ permalink raw reply

* Re: [PATCH 1/1] via-rhine: Fix hanging with high CPU load on low-end broads.
From: David Miller @ 2011-12-28 18:34 UTC (permalink / raw)
  To: shemminger; +Cc: gurligebis, bhutchings, linux-kernel, netdev, rl
In-Reply-To: <20111228101710.51c4893f@nehalam.linuxnetplumber.net>

From: Stephen Hemminger <shemminger@vyatta.com>
Date: Wed, 28 Dec 2011 10:17:10 -0800

> Looks like the hardware isn't really disabling interrupts correctly to support
> NAPI. NAPI is supposed to be friendly and under load the work should move to
> ksoftirqd. I suspect the IRQ management in this driver is borked.
> There is some stupid spin loops in the IRQ handler that happen when looking for
> Tx IRQ. I would run with debug set  and see if this is triggering.

This could be simply a side effect of the fact that via-rhine only does
RX work in it's NAPI handler.

If all the work (or at least both TX and RX) were moved into the NAPI
handler, it'd probably be easier to properly shut off all IRQs during
NAPI processing, and thus avoid the high CPU load in the IRQ handler
being seen here.

This "shut off only some interrupts" scheme is just asking for trouble.

^ permalink raw reply

* Re: [PATCH 0/5] netfilter updates for net-next (2nd round)
From: David Miller @ 2011-12-28 18:37 UTC (permalink / raw)
  To: pablo; +Cc: netfilter-devel, netdev, jengelh, kaber
In-Reply-To: <1325079573-6120-1-git-send-email-pablo@netfilter.org>

From: pablo@netfilter.org
Date: Wed, 28 Dec 2011 14:39:28 +0100

> From: Pablo Neira Ayuso <pablo@netfilter.org>
> 
> Hi Dave,
> 
> These are a couple of late updates from Jan Engelhardt, who has been
> kind enough to recover the unmerged ECN support for IPv6. The IPv6
> ECN support is original work from Patrick McHardy.
> 
> This includes one patch to obsolete the /proc/net/[nf|ip]_conntrack
> outputs in favour of conntrack(8). Basically, it adds some compilation
> time option to disable it. Later on, we can schedule it for removal.
> 
> You can pull this changes from:
> 
> git://1984.lsi.us.es/net-next nf-next

Pulled, thanks a lot.

^ permalink raw reply

* Re: The mystery of optimistic ipv6 DAD handling
From: David Miller @ 2011-12-28 18:39 UTC (permalink / raw)
  To: nhorman; +Cc: netdev
In-Reply-To: <20111228151928.GA12025@neilslaptop.think-freely.org>

From: Neil Horman <nhorman@tuxdriver.com>
Date: Wed, 28 Dec 2011 10:19:28 -0500

> Yeah, If the premise that the test never triggers is true (and it seems like
> you've confirmed that), then theres no harm in removing it.  It still
> seems like we might still need the test somewhere (maybe on a manual route add).
> I've family in town, so I'm not able to look at this closely today, but I'd say this
> is at least a harmless change right now.  Lets take this change, and I'll
> re-read the RFC and look for appropriate code paths where we may need this
> test in a few days.  Thanks Dave!
> 
> Acked-By: Neil Horman <nhorman@tuxdriver.com>

Thanks for reviewing Neil, I've applied this patch to net-next.

^ 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