Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH] xfrm: cache bundle lookup results in flow cache
From: Herbert Xu @ 2010-03-19  0:32 UTC (permalink / raw)
  To: Timo Teräs; +Cc: netdev
In-Reply-To: <4BA27F72.40901@iki.fi>

On Thu, Mar 18, 2010 at 09:30:58PM +0200, Timo Teräs wrote:
>
>>> I don't see why we can't maintain the policy use time if we did
>>> this, all you need is a back-pointer from the top xfrm_dst.
>>
>> Sure.
>
> Actually no. As the pmtu case showed, it's more likely that
> xfrm_dst needs to be regenerated, but the policy stays the
> same since policy db isn't touched that often. If we keep
> them separately we can almost most of the time avoid doing
> policy lookup which is also O(n). Also the currently cache
> entry validation is needs to check policy's bundles_genid
> before allowing touching of xfrm_dst. Otherwise we would have
> to keep global bundle_genid, and we'd lose the parent pointer
> on cache miss.

A back-pointer does not require an O(n) lookup.

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

^ permalink raw reply

* Re: [PATCH] xfrm: cache bundle lookup results in flow cache
From: Herbert Xu @ 2010-03-19  0:31 UTC (permalink / raw)
  To: Timo Teräs; +Cc: netdev
In-Reply-To: <4BA27F72.40901@iki.fi>

On Thu, Mar 18, 2010 at 09:30:58PM +0200, Timo Teräs wrote:
>
> Now, these created xfrm_dst gets cached in policy->bundles
> with ref count zero and not deleted until garbage collection
> is required. That's how xfrm_find_bundle tries to reuse them
> (but it does O(n) lookup).

Yes but the way you're caching it in the policy means that it
only helps if two consecutive packets happen to match the same
bundle.  If your traffic mix was such that each packet required
a different bundle, then we're back to where we started.

That's why I was asking for you to directly cache the xfrm_dst
objects in the flow cache.

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

^ permalink raw reply

* Re: [PATCH 1/3] netlink: fix NETLINK_RECV_NO_ENOBUFS in netlink_set_err()
From: Pablo Neira Ayuso @ 2010-03-19  0:24 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, davem
In-Reply-To: <4BA26138.6070709@trash.net>

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

Patrick McHardy wrote:
> Generally the logic seems inverted, you should return an error
> to conntrack if userspace wasn't notified of the error.

Indeed, thanks. Are you OK with this patch instead?


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

netlink: fix NETLINK_RECV_NO_ENOBUFS in netlink_set_err()

Currently, ENOBUFS errors are reported to the socket via
netlink_set_err() even if NETLINK_RECV_NO_ENOBUFS is set. However,
that should not happen. This fixes this problem and it changes the
prototype of netlink_set_err() to return the number of sockets that
have set the NETLINK_RECV_NO_ENOBUFS socket option. This return
value is used in the next patch in these bugfix series.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
---
 include/linux/netlink.h  |    2 +-
 net/netlink/af_netlink.c |   17 ++++++++++++++---
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/include/linux/netlink.h b/include/linux/netlink.h
index fde27c0..6eaca5e 100644
--- a/include/linux/netlink.h
+++ b/include/linux/netlink.h
@@ -188,7 +188,7 @@ extern int netlink_has_listeners(struct sock *sk, unsigned int group);
 extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int nonblock);
 extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 pid,
 			     __u32 group, gfp_t allocation);
-extern void netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code);
+extern int netlink_set_err(struct sock *ssk, __u32 pid, __u32 group, int code);
 extern int netlink_register_notifier(struct notifier_block *nb);
 extern int netlink_unregister_notifier(struct notifier_block *nb);
 
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 4c5972b..0052d3c 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1093,6 +1093,7 @@ static inline int do_one_set_err(struct sock *sk,
 				 struct netlink_set_err_data *p)
 {
 	struct netlink_sock *nlk = nlk_sk(sk);
+	int ret = 0;
 
 	if (sk == p->exclude_sk)
 		goto out;
@@ -1104,10 +1105,15 @@ static inline int do_one_set_err(struct sock *sk,
 	    !test_bit(p->group - 1, nlk->groups))
 		goto out;
 
+	if (p->code == ENOBUFS && nlk->flags & NETLINK_RECV_NO_ENOBUFS) {
+		ret = 1;
+		goto out;
+	}
+
 	sk->sk_err = p->code;
 	sk->sk_error_report(sk);
 out:
-	return 0;
+	return ret;
 }
 
 /**
@@ -1116,12 +1122,16 @@ out:
  * @pid: the PID of a process that we want to skip (if any)
  * @groups: the broadcast group that will notice the error
  * @code: error code, must be negative (as usual in kernelspace)
+ *
+ * This function returns the number of broadcast listeners that have set the
+ * NETLINK_RECV_NO_ENOBUFS socket option.
  */
-void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
+int netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
 {
 	struct netlink_set_err_data info;
 	struct hlist_node *node;
 	struct sock *sk;
+	int ret = 0;
 
 	info.exclude_sk = ssk;
 	info.pid = pid;
@@ -1132,9 +1142,10 @@ void netlink_set_err(struct sock *ssk, u32 pid, u32 group, int code)
 	read_lock(&nl_table_lock);
 
 	sk_for_each_bound(sk, node, &nl_table[ssk->sk_protocol].mc_list)
-		do_one_set_err(sk, &info);
+		ret += do_one_set_err(sk, &info);
 
 	read_unlock(&nl_table_lock);
+	return ret;
 }
 EXPORT_SYMBOL(netlink_set_err);
 

^ permalink raw reply related

* Re: [PATCH v7] rps: Receive Packet Steering
From: Stephen Hemminger @ 2010-03-18 21:23 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: Changli Gao, Tom Herbert, David Miller, netdev
In-Reply-To: <1268944621.2894.160.camel@edumazet-laptop>

On Thu, 18 Mar 2010 21:37:01 +0100
Eric Dumazet <eric.dumazet@gmail.com> wrote:

> Le jeudi 18 mars 2010 à 14:48 +0800, Changli Gao a écrit :
> > sigh! How about adding file for each cpu weight setting.
> > 
> > .../rx-0/rps_cpu0...n
> > 
> > BTW: I think exporting the hook of hash function will help in some
> > case. So users can choose which hash to use depend on their
> > applications. I know FreeBSD supports hash based on flow, source or
> > CPU. Some network application have multiple instances for taking full
> > advantage of the SMP/C hardware, and each instance binds to a special
> > CPU/Core, so they need some kind of load distributing algorithm for
> > load balancing.
> > 
> 
> exporting skb->rxhash would not be that interesting, but the cpu number
> of last cpu handling the skb and queuing it on socket might be usefull.
> 
> > For example, memcached uses hash based on key, and its developer may
> > implement a hash function for RPS. Then it apply the following
> > iptables rule:
> > 
> > iptables -A PREROUTING -t nat -m cpu --cpuid 0 -m tcp --dport 1234
> > --REDIRECT 8081
> > iptables -A PREROUTING -t nat -m cpu --cpuid 0 -m tcp --dport 1234
> > --REDIRECT 8082
> 
> Well, this would work only if load is evenly distributed to all cpus.
> But you understand this kind of setup has nothing to do with RPS.
> Going through REDIRECT (and conntrack) would kill performance, and would
> not work for unpriviledged users (iptables changes forbidden).
> It wont scale for future machines with 64 or 128 cores.
> 
> maybe some extension of REDIRECT target, being able to add cpu number to
> destination port :
> 
> iptables -A PREROUTING -t nat -m tcp --dport 1234 --REDIRECT 1234+cpu
> 
> 
> > ...
> > 
> > No other things to change, it can take full advantage of the
> > underlying hardware transparently.
> > 
> 
> Coming to mind would be a new socket operation, "bind to cpu", like the
> "bind to device" operation.
> 
> This would work without need for netfilter (and permission to change its
> rules)
> 
> But it would require changes to applications, to fully exploit SMP
> capabilities of machine.

Let's not make a useful feature (RPS) unusable by making it so complex
that mortals can't understand it.


-- 

^ permalink raw reply

* [PATCH] TCP: check min TTL on received ICMP packets
From: Stephen Hemminger @ 2010-03-18 21:27 UTC (permalink / raw)
  To: David Miller; +Cc: netdev, Pekka Savola

This adds RFC5082 checks for TTL on received ICMP packets.
It adds some security against spoofed ICMP packets
disrupting GTSM protected sessions.

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

---
Please apply to 2.6.33 since it basically a "follow correct RFC"
fix to original GTSM patch.

--- a/net/ipv4/tcp_ipv4.c	2010-03-18 11:10:14.396384060 -0700
+++ b/net/ipv4/tcp_ipv4.c	2010-03-18 11:13:09.115759682 -0700
@@ -370,6 +370,11 @@ void tcp_v4_err(struct sk_buff *icmp_skb
 	if (sk->sk_state == TCP_CLOSE)
 		goto out;
 
+	if (unlikely(iph->ttl < inet_sk(sk)->min_ttl)) {
+		NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
+		goto out;
+	}
+
 	icsk = inet_csk(sk);
 	tp = tcp_sk(sk);
 	seq = ntohl(th->seq);

^ permalink raw reply

* [PATCH net-next-2.6] atm: Use kasprintf
From: Eric Dumazet @ 2010-03-18 23:48 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

Use kasprintf in atm_proc_dev_register()

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 net/atm/proc.c |   10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/net/atm/proc.c b/net/atm/proc.c
index 7a96b23..f188a39 100644
--- a/net/atm/proc.c
+++ b/net/atm/proc.c
@@ -406,7 +406,6 @@ EXPORT_SYMBOL(atm_proc_root);
 
 int atm_proc_dev_register(struct atm_dev *dev)
 {
-	int digits, num;
 	int error;
 
 	/* No proc info */
@@ -414,16 +413,9 @@ int atm_proc_dev_register(struct atm_dev *dev)
 		return 0;
 
 	error = -ENOMEM;
-	digits = 0;
-	for (num = dev->number; num; num /= 10)
-		digits++;
-	if (!digits)
-		digits++;
-
-	dev->proc_name = kmalloc(strlen(dev->type) + digits + 2, GFP_KERNEL);
+	dev->proc_name = kasprintf(GFP_KERNEL, "%s:%d", dev->type, dev->number);
 	if (!dev->proc_name)
 		goto err_out;
-	sprintf(dev->proc_name, "%s:%d", dev->type, dev->number);
 
 	dev->proc_entry = proc_create_data(dev->proc_name, 0, atm_proc_root,
 					   &proc_atm_dev_ops, dev);



^ permalink raw reply related

* [PATCH net-next-2.6] net: speedup netdev_set_master()
From: Eric Dumazet @ 2010-03-18 23:37 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

We currently force a synchronize_net() in netdev_set_master()

This seems necessary only when a slave had a master and we dismantle it.

In the other case ("ifenslave bond0 ethO"), we dont need this long
delay.

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
diff --git a/net/core/dev.c b/net/core/dev.c
index 17b1686..ff578ff 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -3733,11 +3733,10 @@ int netdev_set_master(struct net_device *slave, struct net_device *master)
 
 	slave->master = master;
 
-	synchronize_net();
-
-	if (old)
+	if (old) {
+		synchronize_net();
 		dev_put(old);
-
+	}
 	if (master)
 		slave->flags |= IFF_SLAVE;
 	else



^ permalink raw reply related

* [PATCH net-2.6] net: Potential null skb->dev dereference
From: Eric Dumazet @ 2010-03-18 23:19 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

When doing "ifenslave -d bond0 eth0", there is chance to get NULL
dereference in netif_receive_skb(), because dev->master suddenly becomes
NULL after we tested it.

We should use ACCESS_ONCE() to avoid this (or rcu_dereference())

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
 include/linux/netdevice.h |    8 ++++----
 net/8021q/vlan_core.c     |    4 ++--
 net/core/dev.c            |    8 +++++---
 3 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index c79a88b..fa8b476 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2059,12 +2059,12 @@ static inline void skb_bond_set_mac_by_master(struct sk_buff *skb,
  * duplicates except for 802.3ad ETH_P_SLOW, alb non-mcast/bcast, and
  * ARP on active-backup slaves with arp_validate enabled.
  */
-static inline int skb_bond_should_drop(struct sk_buff *skb)
+static inline int skb_bond_should_drop(struct sk_buff *skb,
+				       struct net_device *master)
 {
-	struct net_device *dev = skb->dev;
-	struct net_device *master = dev->master;
-
 	if (master) {
+		struct net_device *dev = skb->dev;
+
 		if (master->priv_flags & IFF_MASTER_ARPMON)
 			dev->last_rx = jiffies;
 
diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c
index c0316e0..c584a0a 100644
--- a/net/8021q/vlan_core.c
+++ b/net/8021q/vlan_core.c
@@ -11,7 +11,7 @@ int __vlan_hwaccel_rx(struct sk_buff *skb, struct vlan_group *grp,
 	if (netpoll_rx(skb))
 		return NET_RX_DROP;
 
-	if (skb_bond_should_drop(skb))
+	if (skb_bond_should_drop(skb, ACCESS_ONCE(skb->dev->master)))
 		goto drop;
 
 	skb->skb_iif = skb->dev->ifindex;
@@ -83,7 +83,7 @@ vlan_gro_common(struct napi_struct *napi, struct vlan_group *grp,
 {
 	struct sk_buff *p;
 
-	if (skb_bond_should_drop(skb))
+	if (skb_bond_should_drop(skb, ACCESS_ONCE(skb->dev->master)))
 		goto drop;
 
 	skb->skb_iif = skb->dev->ifindex;
diff --git a/net/core/dev.c b/net/core/dev.c
index bcc490c..59d4394 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2483,6 +2483,7 @@ int netif_receive_skb(struct sk_buff *skb)
 {
 	struct packet_type *ptype, *pt_prev;
 	struct net_device *orig_dev;
+	struct net_device *master;
 	struct net_device *null_or_orig;
 	struct net_device *null_or_bond;
 	int ret = NET_RX_DROP;
@@ -2503,11 +2504,12 @@ int netif_receive_skb(struct sk_buff *skb)
 
 	null_or_orig = NULL;
 	orig_dev = skb->dev;
-	if (orig_dev->master) {
-		if (skb_bond_should_drop(skb))
+	master = ACCESS_ONCE(orig_dev->master);
+	if (master) {
+		if (skb_bond_should_drop(skb, master))
 			null_or_orig = orig_dev; /* deliver only exact match */
 		else
-			skb->dev = orig_dev->master;
+			skb->dev = master;
 	}
 
 	__get_cpu_var(netdev_rx_stat).total++;




^ permalink raw reply related

* Re: [Bugme-new] [Bug 15507] New: kernel misses 3rd part of tcp handshake (ACK), stays in SYN_RECV state
From: Eric Dumazet @ 2010-03-18 23:12 UTC (permalink / raw)
  To: Andrew Morton; +Cc: netdev, bugzilla-daemon, bugme-daemon, roysjosh
In-Reply-To: <20100318160103.cc28c367.akpm@linux-foundation.org>

Le jeudi 18 mars 2010 à 16:01 -0700, Andrew Morton a écrit :
> (switched to email.  Please respond via emailed reply-to-all, not via the
> bugzilla web interface).
> 
> On Wed, 10 Mar 2010 14:50:51 GMT
> bugzilla-daemon@bugzilla.kernel.org wrote:
> 
> > http://bugzilla.kernel.org/show_bug.cgi?id=15507
> > 
> >            Summary: kernel misses 3rd part of tcp handshake (ACK), stays
> >                     in SYN_RECV state
> >            Product: Networking
> >            Version: 2.5
> >     Kernel Version: 2.6.32.9-67.fc12
> >           Platform: All
> >         OS/Version: Linux
> >               Tree: Fedora
> >             Status: NEW
> >           Severity: normal
> >           Priority: P1
> >          Component: IPV4
> >         AssignedTo: shemminger@linux-foundation.org
> >         ReportedBy: roysjosh@gmail.com
> >         Regression: Yes
> > 
> > 
> > Created an attachment (id=25450)
> >  --> (http://bugzilla.kernel.org/attachment.cgi?id=25450)
> > tcpdump from virtual guest over ::1 showing dup synacks
> > 
> > Setup:
> > 64-bit dual-core, latest F12.  (I've experienced this on .31 and .32 kernels.)
> > 
> > I have also experienced this on 2 other machines, all 64-bit.  One was a
> > single-core virtual guest.
> > 
> > 1. Install httpd.
> > 2. Start it.  Don't bother configuring anything unless you need to.
> > 3. tcpdump .....
> > 4. connect from localhost or (possibly) a nearby host.  I'm not sure if this is
> > timing related yet.
> > 5. note duplicate SYN-ACKs sent out at the exponential backoff.  Some setups
> > exhibit this more/worse than others, and will actually time out the connection.
> >  (Others will only send one or two duplicate SYN-ACKs, and then enter the
> > ESTABLISHED state.)  However, if any data is sent the kernel enters
> > ESTABLISHED.
> > 
> > Note that this isn't ipv4 specific.  I have seen it under ipv4 and ipv6 (via
> > loopback).
> > 
> 
> --

I would say this is expected if httpd server set DEFER_ACCEPT socket
option.




^ permalink raw reply

* Re: [Bugme-new] [Bug 15507] New: kernel misses 3rd part of tcp handshake (ACK), stays in SYN_RECV state
From: Andrew Morton @ 2010-03-18 23:01 UTC (permalink / raw)
  To: netdev; +Cc: bugzilla-daemon, bugme-daemon, roysjosh
In-Reply-To: <bug-15507-10286@http.bugzilla.kernel.org/>


(switched to email.  Please respond via emailed reply-to-all, not via the
bugzilla web interface).

On Wed, 10 Mar 2010 14:50:51 GMT
bugzilla-daemon@bugzilla.kernel.org wrote:

> http://bugzilla.kernel.org/show_bug.cgi?id=15507
> 
>            Summary: kernel misses 3rd part of tcp handshake (ACK), stays
>                     in SYN_RECV state
>            Product: Networking
>            Version: 2.5
>     Kernel Version: 2.6.32.9-67.fc12
>           Platform: All
>         OS/Version: Linux
>               Tree: Fedora
>             Status: NEW
>           Severity: normal
>           Priority: P1
>          Component: IPV4
>         AssignedTo: shemminger@linux-foundation.org
>         ReportedBy: roysjosh@gmail.com
>         Regression: Yes
> 
> 
> Created an attachment (id=25450)
>  --> (http://bugzilla.kernel.org/attachment.cgi?id=25450)
> tcpdump from virtual guest over ::1 showing dup synacks
> 
> Setup:
> 64-bit dual-core, latest F12.  (I've experienced this on .31 and .32 kernels.)
> 
> I have also experienced this on 2 other machines, all 64-bit.  One was a
> single-core virtual guest.
> 
> 1. Install httpd.
> 2. Start it.  Don't bother configuring anything unless you need to.
> 3. tcpdump .....
> 4. connect from localhost or (possibly) a nearby host.  I'm not sure if this is
> timing related yet.
> 5. note duplicate SYN-ACKs sent out at the exponential backoff.  Some setups
> exhibit this more/worse than others, and will actually time out the connection.
>  (Others will only send one or two duplicate SYN-ACKs, and then enter the
> ESTABLISHED state.)  However, if any data is sent the kernel enters
> ESTABLISHED.
> 
> Note that this isn't ipv4 specific.  I have seen it under ipv4 and ipv6 (via
> loopback).
> 


^ permalink raw reply

* Re: Add PGM protocol support to the IP stack
From: Christoph Lameter @ 2010-03-18 21:58 UTC (permalink / raw)
  To: David Miller, netdev; +Cc: linux-kernel
In-Reply-To: <alpine.DEB.2.00.1003181245050.23010@router.home>

Here is what I have so far after a couple of hours.
Something hacked together from openpgm and udplite.

---
 Documentation/networking/pgm/TODO       |    8
 Documentation/networking/pgm/references |    2
 Documentation/networking/pgm/usage      |   91 ++++
 include/linux/in.h                      |    2
 include/linux/pgm.h                     |  720 ++++++++++++++++++++++++++++++++
 net/ipv4/Kconfig                        |   14
 net/ipv4/Makefile                       |    3
 net/ipv4/pgm.c                          |  143 ++++++
 8 files changed, 983 insertions(+)

Index: linux-2.6/include/linux/in.h
===================================================================
--- linux-2.6.orig/include/linux/in.h	2010-03-18 11:05:24.000000000 -0500
+++ linux-2.6/include/linux/in.h	2010-03-18 15:47:59.000000000 -0500
@@ -44,6 +44,7 @@ enum {
   IPPROTO_PIM    = 103,		/* Protocol Independent Multicast	*/

   IPPROTO_COMP   = 108,                /* Compression Header protocol */
+  IPPROTO_PGM	 = 113,		/* Pragmatic General Multicast		*/
   IPPROTO_SCTP   = 132,		/* Stream Control Transport Protocol	*/
   IPPROTO_UDPLITE = 136,	/* UDP-Lite (RFC 3828)			*/

@@ -51,6 +52,7 @@ enum {
   IPPROTO_MAX
 };

+#define IPPROTO_RM IPPROTO_PGM

 /* Internet address. */
 struct in_addr {
Index: linux-2.6/include/linux/pgm.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/include/linux/pgm.h	2010-03-18 16:56:19.000000000 -0500
@@ -0,0 +1,720 @@
+/*
+ * PGM packet formats, RFC 3208.
+ *
+ * Copyright (c) 2006 Miru Limited.
+ * Copyright (c) 2010 Christoph Lameter, The Linux Foundation.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * March 17, 2010 Christoph Lameter
+ *		Basic PGM definitions extracted from openpgm project.
+ * March 18, 2010
+ *		Socket API and document intended usage.
+ *		Basic protocol environment (from udplite.c)
+ */
+
+#ifndef _LINUX_PGM_H
+#define _LINUX_PGM_H
+
+#include <linux/types.h>
+
+/* PGM socket options */
+
+/* Transmitter */
+#define RM_LATEJOIN				1	/* X Not supported on receive so why have it? */
+#define RM_RATE_WINDOW_SIZE			2	/* See struct pgm_send_window */
+#define RM_SEND_WINDOW_ADV_RATE			3	/* X Increase of send window in percentage of window */
+#define RM_SENDER_STATISTICS			4	/* see struct pgm_sender_stats */
+#define RM_SENDER_WINDOW_ADVANCE_METHOD		5	/* X seems obsolete */
+#define RM_SET_MCAST_TTL			6	/* X Can be set via IP_MULTICAST_TTL */
+#define RM_SET_MESSAGE_BOUNDARY			7	/* Fix the size of the messages in bytes */
+#define RM_SET_SEND_IF				8	/* X use IP_MULTICAST_IF etc instead */
+#define RM_USE_FEC				9
+
+/* Receiver */
+#define RM_ADD_RECEIVE_IF			100	/* X ???? IP_MULTICAST_IF instead? */
+#define RM_DEL_RECEIVE_IF			101	/* X IP_MULTICAST_IF */
+#define RM_HIGH_SPEED_INTRANET_OPT		102	/* X PGM should adapt automatically to high speed networks */
+#define RM_RECEIVER_STATISTICS			103	/* See struct pgm_receiver_stats */
+
+/* Socket API structures (established by M$DN) */
+struct pgm_receiver_stats {
+	u64	NumODataPacketsReceived;	/* Number of ODATA (original) sequences */
+	u64	NumRDataPacketsReceived;	/* Number of RDATA (repair) sequences */
+	u64	NumDuplicateDataPackets;	/* Duplicate sequences */
+	u64	DataBytesReceived;
+	u64	TotalBytesReceived;
+	u64	RateKBitsPerSecOverall;		/* Receive rate since start of session X */
+	u64	RateKBitsPerSecLast;		/* Receive rate for last second X*/
+	u64	TrailingEdgeSeqId;		/* Oldest sequence in the receive window */
+	u64	LeadingEdgeSeqId;		/* Newest sequence in the receive window */
+	u64	AverageSequencesInWindow;	/* Average number of sequences in receive window X */
+	u64	MinSequencesInWindow;		/* The mininum number of sequences */
+	u64	MaxSequencesInWindow;		/* The maximum number of sequences */
+	u64	FirstNakSequenceNumber;		/* First outstanding nack sequence number */
+	u64	NumPendingNaks;			/* Number of sequences waiting for NCF */
+	u64	NumOutstandingNaks;		/* Number of sequences waiting for RDATA */
+	u64	NumDataPacketsBuffered;		/* Number of packets currently buffered */
+	u64	TotalSelectiveNaksSent;		/* Number of NAKs sent total */
+	u64	TotalParityNaksSent;		/* Number of parity NAKs sent */
+};
+
+struct pgm_sender_stats {
+	u64	DataBytesSent;
+	u64	TotalBytesSent;
+	u64	NaksReceived;
+	u64	NaksReceivedTooLate;		/* NAKs received after receive window advanced */
+	u64	NumOutstandingNaks;		/* Number of NAKs awaiting response */
+	u64	NumNaksAfterRData;		/* Number of NAKs after RDATA sequences were sent which were ignored */
+	u64	RepairPacketsSent;
+	u64	BufferSpaceAvailable;		/* Number of partial messages dropped */
+	u64	TrailingEdgeSeqId;		/* Oldest sequence id in window */
+	u64	LeadingEdgeSeqId;		/* Newest sequence id in window */
+	u64	RateKBitsPerSecOverall;		/* Rate since start of session X */
+	u64	RateKBitsPerSecLast;		/* Rate in last second X */
+	u64	TotalODataPacketsSent;		/* Total data packets transmitted */
+};
+
+/* Setup of sender RateKbitsPerSec = WindowSizeBytes / WindowSizeMSecs */
+struct pgm_send_window {
+  	u64	RateKbitsPerSec;		/* Allowed rate for the sender in kbits per second */
+	u64	WindowSizeInMSecs;		/* Send window size in time */
+	u64	WindowSizeInBytes;		/* Window size in bytes */
+};
+
+struct pgm_fec_info {
+  	u16	FECBlockSize;			/* Maximum number of packets for a group. Default and max = 255 */
+	u16	FECProActivePackets;		/* Number of proactive packets per group. */
+	u8	FECGroupSize;			/* Number of packets to be treated as a group. Power of two */
+  	int	fFECOnDemandParityEnabled;	/* Allow sender to sent parity repair packets */
+};
+
+/* address family indicator, rfc 1700 (ADDRESS FAMILY NUMBERS) */
+#ifndef AFI_IP
+#define AFI_IP	    1	    /* IP (IP version 4) */
+#define AFI_IP6	    2	    /* IP6 (IP version 6) */
+#endif
+
+/* UDP ports for UDP encapsulation, as per IBM WebSphere MQ */
+#define PGM_DEFAULT_UDP_ENCAP_UCAST_PORT	3055
+#define PGM_DEFAULT_UDP_ENCAP_MCAST_PORT	3056
+
+/* PGM default ports */
+#define PGM_DEFAULT_DATA_DESTINATION_PORT	7500
+#define PGM_DEFAULT_DATA_SOURCE_PORT	0	/* random */
+
+/* DoS limitation to protocol (MS08-036, KB950762) */
+#define PGM_MAX_APDU			UINT16_MAX
+
+/* Cisco default: 24 (max 8200), Juniper & H3C default: 16 */
+#define PGM_MAX_FRAGMENTS		16
+
+enum pgm_type {
+    PGM_SPM = 0x00,	/* 8.1: source path message */
+    PGM_POLL = 0x01,	/* 14.7.1: poll request */
+    PGM_POLR = 0x02,	/* 14.7.2: poll response */
+    PGM_ODATA = 0x04,	/* 8.2: original data */
+    PGM_RDATA = 0x05,	/* 8.2: repair data */
+    PGM_NAK = 0x08,	/* 8.3: NAK or negative acknowledgement */
+    PGM_NNAK = 0x09,	/* 8.3: N-NAK or null negative acknowledgement */
+    PGM_NCF = 0x0a,	/* 8.3: NCF or NAK confirmation */
+    PGM_SPMR = 0x0c,	/* 13.6: SPM request */
+    PGM_MAX = 0xff
+};
+
+#define PGM_OPT_LENGTH		    0x00	/* options length */
+#define PGM_OPT_FRAGMENT	    0x01	/* fragmentation */
+#define PGM_OPT_NAK_LIST	    0x02	/* list of nak entries */
+#define PGM_OPT_JOIN		    0x03	/* late joining */
+#define PGM_OPT_REDIRECT	    0x07	/* redirect */
+#define PGM_OPT_SYN		    0x0d	/* synchronisation */
+#define PGM_OPT_FIN		    0x0e	/* session end */
+#define PGM_OPT_RST		    0x0f	/* session reset */
+
+#define PGM_OPT_PARITY_PRM	    0x08	/* forward error correction parameters */
+#define PGM_OPT_PARITY_GRP	    0x09	/*   group number */
+#define PGM_OPT_CURR_TGSIZE	    0x0a	/*   group size */
+
+#define PGM_OPT_CR		    0x10	/* congestion report */
+#define PGM_OPT_CRQST		    0x11	/* congestion report request */
+
+#define PGM_OPT_NAK_BO_IVL	    0x04	/* nak back-off interval */
+#define PGM_OPT_NAK_BO_RNG	    0x05	/* nak back-off range */
+#define PGM_OPT_NBR_UNREACH	    0x0b	/* neighbour unreachable */
+#define PGM_OPT_PATH_NLA	    0x0c	/* path nla */
+
+#define PGM_OPT_INVALID		    0x7f	/* option invalidated */
+
+/* 8. PGM header */
+struct pgm_header {
+	u16		sport;			/* source port: tsi::sport or UDP port depending on direction */
+	u16		dport;			/* destination port */
+	u8		type;			/* version / packet type */
+	u8		options;		/* options */
+#define PGM_OPT_PARITY		0x80	/* parity packet */
+#define PGM_OPT_VAR_PKTLEN	0x40	/* + variable sized packets */
+#define PGM_OPT_NETWORK		0x02    /* network-significant: must be interpreted by network elements */
+#define PGM_OPT_PRESENT		0x01	/* option extension are present */
+	u16		checksum;		/* checksum */
+	u8		gsi[6];			/* global source id */
+	u16		tsdu_length;		/* tsdu length */
+				/* tpdu length = th length (header + options) + tsdu length */
+};
+
+/* 8.1.  Source Path Messages (SPM) */
+struct pgm_spm {
+	u32		sqn;			/* spm sequence number */
+	u32		trail;			/* trailing edge sequence number */
+	u32		lead;			/* leading edge sequence number */
+	u16		nla_afi;		/* nla afi */
+	u16		reserved;		/* reserved */
+	struct in_addr spm_nla;		/* path nla */
+	/* ... option extensions */
+};
+
+struct pgm_spm6 {
+	u32		sqn;			/* spm sequence number */
+	u32		trail;			/* trailing edge sequence number */
+	u32		lead;			/* leading edge sequence number */
+	u16		nla_afi;		/* nla afi */
+	u16		reserved;		/* reserved */
+	struct in6_addr spm6_nla;		/* path nla */
+	/* ... option extensions */
+};
+
+/* 8.2.  Data Packet */
+struct pgm_data {
+	u32		sqn;			/* data packet sequence number */
+	u32		trail;			/* trailing edge sequence number */
+	/* ... option extensions */
+	/* ... data */
+};
+
+/* 8.3.  Negative Acknowledgments and Confirmations (NAK, N-NAK, & NCF) */
+struct pgm_nak {
+	u32		sqn;			/* requested sequence number */
+	u16		src_nla_afi;		/* nla afi */
+	u16		reserved;		/* reserved */
+	struct in_addr src_nla;		/* source nla */
+	u16		grp_nla_afi;		/* nla afi */
+	u16		reserved2;		/* reserved */
+	struct in_addr grp_nla;		/* multicast group nla */
+	/* ... option extension */
+};
+
+struct pgm_nak6 {
+	u32		sqn;			/* requested sequence number */
+	u16		src_nla_afi;		/* nla afi */
+	u16		reserved;		/* reserved */
+	struct in6_addr src_nla;		/* source nla */
+	u16		grp_nla_afi;		/* nla afi */
+	u16		reserved2;		/* reserved */
+	struct in6_addr grp_nla;		/* multicast group nla */
+	/* ... option extension */
+};
+
+/* 9.  Option header (max 16 per packet) */
+struct pgm_opt_header {
+	u8		type;			/* option type */
+#define PGM_OPT_MASK	0x7f
+#define PGM_OPT_END	0x80		/* end of options flag */
+	u8		length;			/* option length */
+	u8		reserved;
+#define PGM_OP_ENCODED		0x8	/* F-bit */
+#define PGM_OPX_MASK		0x3
+#define PGM_OPX_IGNORE		0x0	/* extensibility bits */
+#define PGM_OPX_INVALIDATE	0x1
+#define PGM_OPX_DISCARD		0x2
+#define PGM_OP_ENCODED_NULL	0x80	/* U-bit */
+};
+
+/* 9.1.  Option extension length - OPT_LENGTH */
+struct pgm_opt_length {
+	u8		type;			/* include header as total length overwrites reserved/OPX bits */
+	u8		length;
+	u16		total_length;	    	/* total length of all options */
+};
+
+/* 9.2.  Option fragment - OPT_FRAGMENT */
+struct pgm_opt_fragment {
+	u8		reserved;		/* reserved */
+	u32		sqn;			/* first sequence number */
+	u32		frag_off;		/* offset */
+	u32		frag_len;		/* length */
+};
+
+/* 9.3.5.  Option NAK List - OPT_NAK_LIST */
+struct pgm_opt_nak_list {
+	u8		reserved;		/* reserved */
+	u32		sqn[];
+};
+
+/* 9.4.2.  Option Join - OPT_JOIN */
+struct pgm_opt_join {
+	u8		reserved;		    /* reserved */
+	u32		join_min;		    /* minimum sequence number */
+};
+
+/* 9.5.5.  Option Redirect - OPT_REDIRECT */
+struct pgm_opt_redirect {
+	u8		reserved;		/* reserved */
+	u16		nla_afi;		/* nla afi */
+	u16		reserved2;		/* reserved */
+	struct in_addr nla;		/* dlr nla */
+};
+
+struct pgm_opt6_redirect {
+	u8		reserved;		/* reserved */
+	u16		nla_afi;		/* nla afi */
+	u16		reserved2;		/* reserved */
+	struct in6_addr opt6_nla;		/* dlr nla */
+};
+
+/* 9.6.2.  Option Sources - OPT_SYN */
+struct pgm_opt_syn {
+	u8	    	reserved;		/* reserved */
+};
+
+/* 9.7.4.  Option End Session - OPT_FIN */
+struct pgm_opt_fin {
+	u8		reserved;		/* reserved */
+};
+
+/* 9.8.4.  Option Reset - OPT_RST */
+struct pgm_opt_rst {
+	u8		reserved;		/* reserved */
+};
+
+
+/*
+ * Forward Error Correction - FEC
+ */
+
+/* 11.8.1.  Option Parity - OPT_PARITY_PRM */
+struct pgm_opt_parity_prm {
+	u8	reserved;			/* reserved */
+#define PGM_PARITY_PRM_MASK 0x3
+#define PGM_PARITY_PRM_PRO  0x1		/* source provides pro-active parity packets */
+#define PGM_PARITY_PRM_OND  0x2		/*                 on-demand parity packets */
+	u32		tgs;			/* transmission group size */
+};
+
+/* 11.8.2.  Option Parity Group - OPT_PARITY_GRP */
+struct pgm_opt_parity_grp {
+	u8	reserved;			/* reserved */
+	u32	group;				/* parity group number */
+};
+
+/* 11.8.3.  Option Current Transmission Group Size - OPT_CURR_TGSIZE */
+struct pgm_opt_curr_tgsize {
+	u8	reserved;			/* reserved */
+	u32	atgsize;			/* actual transmission group size */
+};
+
+/*
+ * Congestion Control
+ */
+
+/* 12.7.1.  Option Congestion Report - OPT_CR */
+struct pgm_opt_cr {
+	u8		reserved;		/* reserved */
+	u32		cr_lead;		/* congestion report reference sqn */
+	u16		cr_ne_wl;		/* ne worst link */
+	u16		cr_ne_wp;		/* ne worst path */
+	u16		cr_rx_wp;		/* rcvr worst path */
+	u16		reserved2;		/* reserved */
+	u16		nla_afi;		/* nla afi */
+	u16		reserved3;		/* reserved */
+	u32		cr_rcvr;		/* worst receivers nla */
+};
+
+/* 12.7.2.  Option Congestion Report Request - OPT_CRQST */
+struct pgm_opt_crqst {
+	u8	reserved;			/* reserved */
+};
+
+
+/*
+ * SPM Requests
+ */
+
+/* 13.6.  SPM Requests */
+struct pgm_spmr {
+    /* ... option extensions */
+};
+
+
+/*
+ * Poll Mechanism
+ */
+
+/* 14.7.1.  Poll Request */
+struct pgm_poll {
+	u32		sqn;			/* poll sequence number */
+	u16		round;			/* poll round */
+	u16		type;			/* poll sub-type */
+#define PGM_POLL_GENERAL	0x0	/* general poll  */
+#define PGM_POLL_DLR		0x1	/* DLR poll */
+	u16		nla_afi;		/* nla afi */
+	u16		reserved;		/* reserved */
+	struct in_addr nla;			/* path nla */
+	u32		bo_ivl;			/* poll back-off interval */
+	char	rand[4];		/* random string */
+	u32		mask;			/* matching bit-mask */
+	/* ... option extensions */
+};
+
+struct pgm_poll6 {
+	u32		sqn;			/* poll sequence number */
+	u16		round;		    	/* poll round */
+	u16		s_type;			/* poll sub-type */
+	u16		nla_afi;		/* nla afi */
+	u16		reserved;		/* reserved */
+	struct in6_addr nla;		/* path nla */
+	u32		bo_ivl;			/* poll back-off interval */
+	char	rand[4];		/* random string */
+	u32		mask;			/* matching bit-mask */
+	/* ... option extensions */
+};
+
+/* 14.7.2.  Poll Response */
+struct pgm_polr {
+	u32		sqn;			/* polr sequence number */
+	u16		round;			/* polr round */
+	u16		reserved;		/* reserved */
+	/* ... option extensions */
+};
+
+
+/*
+ * Implosion Prevention
+ */
+
+/* 15.4.1.  Option NAK Back-Off Interval - OPT_NAK_BO_IVL */
+struct pgm_opt_nak_bo_ivl {
+	u8		opt_reserved;		/* reserved */
+	u32		opt_nak_bo_ivl;		/* nak back-off interval */
+	u32		opt_nak_bo_ivl_sqn;	/* nak back-off interval sqn */
+};
+
+/* 15.4.2.  Option NAK Back-Off Range - OPT_NAK_BO_RNG */
+struct pgm_opt_nak_bo_rng {
+	u8		opt_reserved;		/* reserved */
+	u32		opt_nak_max_bo_ivl;	/* maximum nak back-off interval */
+	u32		opt_nak_min_bo_ivl;	/* minimum nak back-off interval */
+};
+
+/* 15.4.3.  Option Neighbour Unreachable - OPT_NBR_UNREACH */
+struct pgm_opt_nbr_unreach {
+	u8		opt_reserved;		/* reserved */
+};
+
+/* 15.4.4.  Option Path - OPT_PATH_NLA */
+struct pgm_opt_path_nla {
+ 	u8		reserved;		/* reserved */
+	struct in_addr opt_path_nla;	/* path nla */
+};
+
+struct pgm_opt6_path_nla {
+	u8		reserved;		/* reserved */
+	struct in6_addr opt6_path_nla;	/* path nla */
+};
+
+#ifdef __KERNEL__
+
+#include <net/inet_sock.h>
+#include <linux/skbuff.h>
+#include <net/netns/hash.h>
+#include <linux/rslib.h>
+
+static inline int pgm_is_upstream(u8 type)
+{
+    return (type == PGM_NAK ||		/* unicast */
+	    type == PGM_NNAK ||		/* unicast */
+	    type == PGM_SPMR ||		/* multicast + unicast */
+	    type == PGM_POLR);		/* unicast */
+}
+
+static inline int pgm_is_peer(u8 type)
+{
+    return (type == PGM_SPMR);		/* multicast */
+}
+
+static inline int pgm_is_downstream (u8 type)
+{
+    return (type == PGM_SPM   ||	/* all multicast */
+	    type == PGM_ODATA ||
+	    type == PGM_RDATA ||
+	    type == PGM_POLL  ||
+	    type == PGM_NCF);
+}
+
+int pgm_verify_spm(struct sk_buff *);
+int pgm_verify_spmr(struct sk_buff *);
+int pgm_verify_nak(struct sk_buff *);
+int pgm_verify_nnak(struct sk_buff *);
+int pgm_verify_ncf(struct sk_buff *);
+int pgm_verify_poll(struct sk_buff *);
+int pgm_verify_polr(struct sk_buff *);
+
+/* Global sesssion ID */
+struct pgm_gsi {
+	char gsi[6];
+};
+
+struct pgm_tsi {
+	char	gsi[6];		/* global session identifier */
+	u16	sport;		/* source port: a random number to help detect session re-starts */
+}
+
+/* Receiver data structures */
+
+enum pgm_rxw_state {
+	PGM_PKT_ERROR_STATE,
+	PGM_PKT_BACK_OFF_STATE,	    /* PGM protocol recovery states */
+	PGM_PKT_WAIT_NCF_STATE,
+	PGM_PKT_WAIT_DATA_STATE,
+
+	PGM_PKT_HAVE_DATA_STATE,	    /* data received waiting to commit to application layer */
+
+	PGM_PKT_HAVE_PARITY_STATE,	    /* contains parity information not original data */
+	PGM_PKT_COMMIT_DATA_STATE,	    /* commited data waiting for purging */
+	PGM_PKT_LOST_DATA_STATE,	    /* if recovery fails, but packet has not yet been commited */
+};
+
+enum pgm_rxw_returns {
+	PGM_RXW_OK,
+	PGM_RXW_INSERTED,
+	PGM_RXW_APPENDED,
+	PGM_RXW_UPDATED,
+	PGM_RXW_MISSING,
+	PGM_RXW_DUPLICATE,
+	PGM_RXW_MALFORMED,
+	PGM_RXW_BOUNDS,
+	PGM_RXW_SLOW_CONSUMER,
+	PGM_RXW_UNKNOWN,
+};
+
+struct pgm_rxw_state {
+	unsigned long	nak_rb_expiry;
+	unsigned long	nak_rpt_expiry;
+	unsigned long	nak_rdata_expiry;
+
+        enum pgm_receiver_state state;
+
+	u8		nak_transmit_count;
+	u8		ncf_retry_count;
+	u8		data_retry_count;
+
+/* only valid on tg_sqn::pkt_sqn = 0 */
+	unsigned	is_contiguous:1;	/* transmission group */
+};
+
+struct pgm_rxw {
+	struct pgm_tsi *	tsi;
+
+        struct list_head backoff_queue;
+        struct list_head wait_ncf_queue;
+        struct list_head wait_data_queue;
+
+	/* window context counters */
+	u32		lost_count;		/* failed to repair */
+	u32		fragment_count;		/* incomplete apdu */
+	u32		parity_count;		/* parity for repairs */
+	u32		committed_count;	/* but still in window */
+
+        u16		max_tpdu;               /* maximum packet size */
+        u32		lead, trail;
+        u32		rxw_trail, rxw_trail_init;
+	u32		commit_lead;
+        unsigned        is_constrained:1;
+        unsigned        is_defined:1;
+	unsigned	has_event:1;		/* edge triggered */
+	unsigned	is_fec_available:1;
+	struct rs_t	rs;
+	u32		tg_size;		/* transmission group size for parity recovery */
+	unsigned	tg_sqn_shift;
+
+	u32		min_fill_time;		/* restricted from pgm_time_t */
+	u32		max_fill_time;
+	u32		min_nak_transmit_count;
+	u32		max_nak_transmit_count;
+	u32		cumulative_losses;
+	u32		bytes_delivered;		/* Fix this: Will overflow */
+	u32		msgs_delivered;
+
+	size_t		size;			/* in bytes */
+	unsigned	alloc;			/* in pkts */
+	struct sk_buff *pdata[];
+};
+
+struct pgm_rxw* pgm_rxw_create(pgm_tsi *, u16, u32, unsigned, unsigned);
+void pgm_rxw_destroy(struct pgm_rxw *);
+int pgm_rxw_add(struct pgm_rxw *, struct sk_buf *, u64, u64);
+void pgm_rxw_remove_commit(struct pgm_rxw *);
+size_t pgm_rxw_readv(struct pgm_rxw *, struct kiovec *, unsigned int);
+unsigned int pgm_rxw_remove_trail (struct pgm_rxw *);
+unsigned int pgm_rxw_update(struct pgm_rxw *, u32, u32, u64, u64);
+void pgm_rxw_update_fec(struct pgm_rxw *, unsigned int);
+int pgm_rxw_confirm(struct pgm_rxw *, u32, u64, u64, u64);
+void pgm_rxw_lost(struct  pgm_rxw *, u32);
+void pgm_rxw_state(struct pgm_rxw *, struct sk_buff *, enum pgm_pkt_state);
+struct sk_buff *pgm_rxw_peek(struct pgm_rxw *, u32);
+
+static inline int pgm_rxw_max_length(struct pgm_rxw *window)
+{
+	return window->alloc;
+}
+
+static inline u32 pgm_rxw_length(struct pgm_rxw *window)
+{
+	return ( 1 + window->lead ) - window->trail;
+}
+
+static inline size_t pgm_rxw_size(struct pgm_rxw *window)
+{
+	return window->size;
+}
+
+static inline int pgm_rxw_is_empty(struct pgm_rxw *window)
+{
+	return pgm_rxw_length (window) == 0;
+}
+
+static inline int pgm_rxw_is_full(struct pgm_rxw *window)
+{
+	return pgm_rxw_length (window) == pgm_rxw_max_length (window);
+}
+
+static inline u32 pgm_rxw_lead(struct pgm_rxw *window)
+{
+	return window->lead;
+}
+
+static inline u32 pgm_rxw_next_lead(struct pgm_rxw *window)
+{
+	return pgm_rxw_lead(window) + 1;
+}
+
+/* Transmitter data structures */
+
+struct pgm_txw_state {
+	u32		unfolded_checksum;	/* first 32-bit word must be checksum */
+
+	unsigned	waiting_retransmit:1;	/* in retransmit queue */
+	unsigned	retransmit_count:15;
+	unsigned	nak_elimination_count:16;
+
+        unsigned long	expiry;			/* Advance with time */
+        unsigned long	last_retransmit;	/* NAK elimination */
+};
+
+struct pgm_txw {
+	struct pgm_tsi*		tsi;
+
+/* option: lockless atomics */
+        u32			lead;
+        u32			trail;
+
+        struct list_head	retransmit_queue;
+
+	struct rs_t		rs;
+	unsigned int		tg_sqn_shift;
+	struct sk_buff *	parity_buffer;
+	unsigned		is_fec_enabled:1;
+
+	u32			size;			/* window content size in bytes */
+	u32			alloc;			/* length of pdata[] */
+	struct sk_buff*	pdata[];
+};
+
+struct pgm_txw *pgm_txw_create(pgm_tsi *, u16, u32, unsigned int,
+			unsigned int, int, unsigned int, unsigned int);
+void pgm_txw_shutdown (struct pgm_txw *);
+void pgm_txw_add(struct pgm_txw *, struct sk_buff *);
+struct sk_buff* pgm_txw_peek(struct pgm_txw* , u32);
+int pgm_txw_retransmit_push(struct pgm_txw *, u32, int, unsigned int);
+struct sk_buff* pgm_txw_retransmit_try_peek(struct pgm_txw *);
+void pgm_txw_retransmit_remove_head(struct pgm_txw *);
+
+static inline unsigned int pgm_txw_max_length(struct pgm_txw *window)
+{
+	return window->alloc;
+}
+
+static inline u32 pgm_txw_length(struct pgm_txw *window)
+{
+	return ( 1 + window->lead ) - window->trail;
+}
+
+static inline u32 pgm_txw_size(struct pgm_txw *window)
+{
+	return window->size;
+}
+
+static inline int pgm_txw_is_empty(struct pgm_txw *window)
+{
+	return pgm_txw_length(window) == 0;
+}
+
+static inline int pgm_txw_is_full(struct pgm_txw *window)
+{
+	return pgm_txw_length(window) == pgm_txw_max_length(window);
+}
+
+static inline u32 pgm_txw_lead(struct pgm_txw *window)
+{
+	return window->lead;
+}
+
+static inline u32 pgm_txw_next_lead(struct pgm_txw *window)
+{
+	return pgm_txw_lead (window) + 1;
+}
+
+static inline u32 pgm_txw_trail(struct pgm_txw *window)
+{
+	return window->trail;
+}
+
+static inline u32 pgm_txw_get_unfolded_checksum(struct sk_buff *skb)
+{
+	struct pgm_txw_state *state = (void *)&skb->cb;
+
+	return state->unfolded_checksum;
+}
+
+static inline void pgm_txw_set_unfolded_checksum(struct sk_buff* skb, u32 csum)
+{
+	struct pgm_txw_state *state = (void *)&skb->cb;
+
+	state->unfolded_checksum = csum;
+}
+
+static inline void pgm_txw_inc_retransmit_count(struct sk_buff * skb)
+{
+	struct pgm_txw_state *state = (void *)&skb->cb;
+
+	state->retransmit_count++;
+}
+
+static inline int pgm_txw_retransmit_is_empty(struct pgm_txw *window)
+{
+	return list_empty(&window->retransmit_queue);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_PGM_H */
Index: linux-2.6/Documentation/networking/pgm/TODO
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/Documentation/networking/pgm/TODO	2010-03-18 13:14:59.000000000 -0500
@@ -0,0 +1,8 @@
+- Define Socket API
+- Define /proc and sys api
+- Implement base logic
+- PGM over UDP
+- FEC Forward Error correction
+- Verify interaction with Cisco and other switches
+- Verify interaction with IBM Websphere, TIBCO, openpgm etc.
+
Index: linux-2.6/Documentation/networking/pgm/references
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/Documentation/networking/pgm/references	2010-03-18 13:14:59.000000000 -0500
@@ -0,0 +1,2 @@
+RFC3208
+
Index: linux-2.6/Documentation/networking/pgm/usage
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/Documentation/networking/pgm/usage	2010-03-18 15:55:17.000000000 -0500
@@ -0,0 +1,91 @@
+1. Opening a socket
+
+	A. Native PGM
+
+		fd = socket(AF_INET, SOCK_RDM, IPPROTO_PGM)
+
+	B. PGM over UDP
+
+		fd = socket(AF_INET, SOCK_RDM, IPPROTO_UDP)
+
+	C. PGM over SHM (?)
+
+		fd = socket(AF_UNIX, SOCK_RDM, 0)
+
+
+2. Binding to a multicast address
+
+	A. Sender
+
+		Connect the socket to a MC address and port using connect().
+
+		Note that the port is significant since multiple streams on different
+		ports can be run over the same MC addr.
+
+	B. Receiver
+
+		I. Bind the socket to the MC address and port of interest.
+
+		II. Listen to the socket.
+
+			Process will wait until a PGM packet destined to the port of interest
+			is received.
+
+		III. Accept a connection.
+
+			Establishes a session. Data can then be received.
+
+
+3. Sending and receiving
+
+	Use the usual socket read and write operations and the various flavors of waiting
+	for a packet via select, poll, epoll etc.
+
+	Packet sizes are determined by the number of  packets in a single sendmsg() unless
+	overridden by the RM_SET_MESSAGE_BOUNDARY socket option.
+
+	The sender will block when the send window is full unless a non blocking write is performed.
+
+	The receiver shows the usual wait semantics. If the stream is set to unreliable then
+	packets may arrive in random order. If the set is set to RM_LISTEN_ONLY then packets may
+	just be missing.
+
+4. 	Transmitter Socket Options
+
+
+	A. Setting the window size / rate.
+
+		struct pgm_send_window x;
+		x.RateKbitsPerSec = 56;
+		x.WindowSizeInMsecs = 60000;
+		x.WindowSizeinBytes = 10000000;
+
+		setsockopt(fd, SOCK_RDM, RM_RATE_WINDOW_SIZE, &x, sizeof(x));
+
+		Default is sending at 56Kbps with a buffer of 10 Megabytes and buffering for a minute.
+
+	B. FEC mode
+
+		struct pgm_fec_info x;
+
+		x.FECBlocksize = 255;
+		x.FECProActivePackets = 0;
+		x.FECGroupSize = 0;
+		x.fFECOnDemandParityEnabled = 1;
+
+		setsockopt(fd, SOCK_RDM, RM_FEC_MODE, &x, sizeof(x));
+
+
+5.	Receiver Socket Options
+
+	None?
+
+
+Possible Extensions
+
+	RM_UNORDERED	accept unordered packet avoiding delays when packets arrive out of sequence.
+			packet is still NAKed.
+
+	RM_RECEIVE_ONLY	Simply ignore missed packets. Do not send any replies.
+
+
Index: linux-2.6/net/ipv4/pgm.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6/net/ipv4/pgm.c	2010-03-18 16:37:17.000000000 -0500
@@ -0,0 +1,143 @@
+/*
+ *  PGM		An implementation of the PGM (Pragmatic General Multicast)
+ *              protocol (RFC 3208).
+ *
+ *  Authors:    Christoph Lameter      <cl@linux-foundation.org>
+ *
+ *  Changes:
+ *  Fixes:
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ */
+#include "udp_impl.h"
+
+struct udp_table 	pgm_table __read_mostly;
+EXPORT_SYMBOL(pgm_table);
+
+static int pgm_rcv(struct sk_buff *skb)
+{
+	/* TBD */
+	return __udp4_lib_rcv(skb, &pgm_table, IPPROTO_UDPLITE);
+}
+
+static void pgm_err(struct sk_buff *skb, u32 info)
+{
+	__udp4_lib_err(skb, info, &pgm_table);
+}
+
+static const struct net_protocol pgm_protocol = {
+	.handler	= pgm_rcv,
+	.err_handler	= pgm_err,
+	.no_policy	= 1,
+	.netns_ok	= 1,
+};
+
+struct proto 	pgm_prot = {
+	.name		   = "PGM",
+	.owner		   = THIS_MODULE,
+	.close		   = udp_lib_close,
+	.connect	   = ip4_datagram_connect,
+	.disconnect	   = udp_disconnect,
+	.ioctl		   = udp_ioctl,
+	.init		   = pgm_sk_init,
+	.destroy	   = udp_destroy_sock,
+	.setsockopt	   = pgm_setsockopt,
+	.getsockopt	   = pgm_getsockopt,
+	.sendmsg	   = pgm_sendmsg,
+	.recvmsg	   = pgm_recvmsg,
+	.sendpage	   = pgm_sendpage,
+	.backlog_rcv	   = udp_queue_rcv_skb,
+	.hash		   = udp_lib_hash,
+	.unhash		   = udp_lib_unhash,
+	.get_port	   = udp_v4_get_port,
+	.obj_size	   = sizeof(struct udp_sock),
+	.slab_flags	   = SLAB_DESTROY_BY_RCU,
+	.h.udp_table	   = &pgm_table,
+#ifdef CONFIG_COMPAT
+	.compat_setsockopt = compat_pgm_setsockopt,
+	.compat_getsockopt = compat_pgm_getsockopt,
+#endif
+};
+
+static struct inet_protosw pgm_ip_protosw = {
+	.type		=  SOCK_RDM,
+	.protocol	=  IPPROTO_PGM,
+	.prot		=  &pgm_ip_prot,
+	.ops		=  &inet_pgm_ops,
+	.no_check	=  0,		/* must checksum (RFC 3828) */
+	.flags		=  INET_PROTOSW_PERMANENT,
+};
+
+static struct inet_protosw pgm_udp_protosw = {
+	.type		=  SOCK_RDM,
+	.protocol	=  IPPROTO_UDP,
+	.prot		=  &pgm_udp_prot,
+	.ops		=  &inet_pgm_ops,
+	.no_check	=  0,		/* must checksum (RFC 3828) */
+	.flags		=  INET_PROTOSW_PERMANENT,
+};
+
+#ifdef CONFIG_PROC_FS
+static struct udp_seq_afinfo pgm_seq_afinfo = {
+	.name		= "pgm",
+	.family		= AF_INET,
+	.udp_table 	= &pgm_table,
+	.seq_fops	= {
+		.owner	=	THIS_MODULE,
+	},
+	.seq_ops	= {
+		.show		= udp4_seq_show,
+	},
+};
+
+static int __net_init pgm_proc_init_net(struct net *net)
+{
+	return udp_proc_register(net, &pgm_seq_afinfo);
+}
+
+static void __net_exit pgm_proc_exit_net(struct net *net)
+{
+	udp_proc_unregister(net, &pgm_seq_afinfo);
+}
+
+static struct pernet_operations pgm4_net_ops = {
+	.init = pgm_proc_init_net,
+	.exit = pgm_proc_exit_net,
+};
+
+static __init int pgm_proc_init(void)
+{
+	return register_pernet_subsys(&pgm_net_ops);
+}
+#else
+static inline int pgm_proc_init(void)
+{
+	return 0;
+}
+#endif
+
+void __init pgm_register(void)
+{
+	udp_table_init(&pgm_table, "PGM");
+	if (proto_register(&pgm_prot, 1))
+		goto out_register_err;
+
+	if (inet_add_protocol(&pgm_protocol, IPPROTO_PGM) < 0)
+		goto out_unregister_proto;
+
+	inet_register_protosw(&pgm_ip_protosw);
+	inet_register_protosw(&pgm_udp_protosw);
+
+	if (pgm_proc_init())
+		printk(KERN_ERR "%s: Cannot register /proc!\n", __func__);
+	return;
+
+out_unregister_proto:
+	proto_unregister(&pgm_prot);
+out_register_err:
+	printk(KERN_CRIT "%s: Cannot add PGM protocol.\n", __func__);
+}
+
+EXPORT_SYMBOL(pgm_prot);
Index: linux-2.6/net/ipv4/Kconfig
===================================================================
--- linux-2.6.orig/net/ipv4/Kconfig	2010-03-18 16:16:34.000000000 -0500
+++ linux-2.6/net/ipv4/Kconfig	2010-03-18 16:39:36.000000000 -0500
@@ -14,6 +14,20 @@ config IP_MULTICAST
 	  <file:Documentation/networking/multicast.txt>. For most people, it's
 	  safe to say N.

+config IP_PGM
+	bool "IP: Pragmatic General Multicast (RFC3208) support"
+	depends on IP_MULTICAST && EXPERIMENTAL
+	help
+	   This is an implementation of reliable multicasting following
+	   RFC3208. PGM is used for publisher-subscriber based information
+	   services on private networks. The PGM protocol allows for recovery
+	   of lost packets through resent requests (NAKs) and through the
+	   recovery of missing packets via FEC. PGM is supported by router
+	   vendors through logic that allows correlation of NAKs to avoid
+	   flooding the network with NAK (aka NAK-storm). PGM is widely used
+	   in the financial industry and various commercial applications
+	   support this protocol.
+
 config IP_ADVANCED_ROUTER
 	bool "IP: advanced router"
 	---help---
Index: linux-2.6/net/ipv4/Makefile
===================================================================
--- linux-2.6.orig/net/ipv4/Makefile	2010-03-18 16:16:07.000000000 -0500
+++ linux-2.6/net/ipv4/Makefile	2010-03-18 16:24:04.000000000 -0500
@@ -52,3 +52,6 @@ obj-$(CONFIG_NETLABEL) += cipso_ipv4.o

 obj-$(CONFIG_XFRM) += xfrm4_policy.o xfrm4_state.o xfrm4_input.o \
 		      xfrm4_output.o
+
+obj-$(CONFIG_IP_PGM)	+= pgm.o
+

^ permalink raw reply

* Re: [PATCH] net: dev_getfirstbyhwtype() optimization
From: Laurent Chavey @ 2010-03-18 21:54 UTC (permalink / raw)
  To: Eric Dumazet; +Cc: David Miller, netdev
In-Reply-To: <1268947645.2894.166.camel@edumazet-laptop>

reviewed_by: Laurent Chavey <chavey@google.com>


On Thu, Mar 18, 2010 at 2:27 PM, Eric Dumazet <eric.dumazet@gmail.com> wrote:
> Use RCU to avoid RTNL use in dev_getfirstbyhwtype()
>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> ---
> diff --git a/net/core/dev.c b/net/core/dev.c
> index 17b1686..0f2e9fc 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c
> @@ -772,14 +772,17 @@ EXPORT_SYMBOL(__dev_getfirstbyhwtype);
>
>  struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type)
>  {
> -       struct net_device *dev;
> +       struct net_device *dev, *ret = NULL;
>
> -       rtnl_lock();
> -       dev = __dev_getfirstbyhwtype(net, type);
> -       if (dev)
> -               dev_hold(dev);
> -       rtnl_unlock();
> -       return dev;
> +       rcu_read_lock();
> +       for_each_netdev_rcu(net, dev)
> +               if (dev->type == type) {
> +                       dev_hold(dev);
> +                       ret = dev;
> +                       break;
> +               }
> +       rcu_read_unlock();
> +       return ret;
>  }
>  EXPORT_SYMBOL(dev_getfirstbyhwtype);
>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* [PATCH] net: dev_getfirstbyhwtype() optimization
From: Eric Dumazet @ 2010-03-18 21:27 UTC (permalink / raw)
  To: David Miller; +Cc: netdev

Use RCU to avoid RTNL use in dev_getfirstbyhwtype()

Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
---
diff --git a/net/core/dev.c b/net/core/dev.c
index 17b1686..0f2e9fc 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -772,14 +772,17 @@ EXPORT_SYMBOL(__dev_getfirstbyhwtype);
 
 struct net_device *dev_getfirstbyhwtype(struct net *net, unsigned short type)
 {
-	struct net_device *dev;
+	struct net_device *dev, *ret = NULL;
 
-	rtnl_lock();
-	dev = __dev_getfirstbyhwtype(net, type);
-	if (dev)
-		dev_hold(dev);
-	rtnl_unlock();
-	return dev;
+	rcu_read_lock();
+	for_each_netdev_rcu(net, dev)
+		if (dev->type == type) {
+			dev_hold(dev);
+			ret = dev;
+			break;
+		}
+	rcu_read_unlock();
+	return ret;
 }
 EXPORT_SYMBOL(dev_getfirstbyhwtype);
 



^ permalink raw reply related

* Re: [Bug 14470] New: freez in TCP stack
From: Andrew Morton @ 2010-03-18 21:04 UTC (permalink / raw)
  To: David Miller
  Cc: ilpo.jarvinen, eric.dumazet, shemminger, netdev, kolo,
	bugzilla-daemon
In-Reply-To: <20091202.222446.10943991.davem@davemloft.net>

On Wed, 02 Dec 2009 22:24:46 -0800 (PST)
David Miller <davem@davemloft.net> wrote:

> From: "Ilpo J__rvinen" <ilpo.jarvinen@helsinki.fi>
> Date: Thu, 26 Nov 2009 23:54:53 +0200 (EET)
> 
> > [PATCH] tcp: clear hints to avoid a stale one (nfs only affected?)
> 
> Ok, since Linus just released 2.6.32 I'm tossing this into net-next-2.6
> so it gets wider exposure.
> 
> I still want to see test results from the bug reporter, and if it fixes
> things we can toss this into -stable too.

Despite my request to take this to email, quite a few people have been
jumping onto this report via bugzilla:
http://bugzilla.kernel.org/show_bug.cgi?id=14470

Bit of a pita, but it'd be worth someone taking a look to ensure that
we're all talking about the same bug.


^ permalink raw reply

* Re: [PATCH v7] rps: Receive Packet Steering
From: Eric Dumazet @ 2010-03-18 20:37 UTC (permalink / raw)
  To: Changli Gao; +Cc: Tom Herbert, David Miller, netdev
In-Reply-To: <412e6f7f1003172348s1f113734h882779d9acd08ddc@mail.gmail.com>

Le jeudi 18 mars 2010 à 14:48 +0800, Changli Gao a écrit :
> sigh! How about adding file for each cpu weight setting.
> 
> .../rx-0/rps_cpu0...n
> 
> BTW: I think exporting the hook of hash function will help in some
> case. So users can choose which hash to use depend on their
> applications. I know FreeBSD supports hash based on flow, source or
> CPU. Some network application have multiple instances for taking full
> advantage of the SMP/C hardware, and each instance binds to a special
> CPU/Core, so they need some kind of load distributing algorithm for
> load balancing.
> 

exporting skb->rxhash would not be that interesting, but the cpu number
of last cpu handling the skb and queuing it on socket might be usefull.

> For example, memcached uses hash based on key, and its developer may
> implement a hash function for RPS. Then it apply the following
> iptables rule:
> 
> iptables -A PREROUTING -t nat -m cpu --cpuid 0 -m tcp --dport 1234
> --REDIRECT 8081
> iptables -A PREROUTING -t nat -m cpu --cpuid 0 -m tcp --dport 1234
> --REDIRECT 8082

Well, this would work only if load is evenly distributed to all cpus.
But you understand this kind of setup has nothing to do with RPS.
Going through REDIRECT (and conntrack) would kill performance, and would
not work for unpriviledged users (iptables changes forbidden).
It wont scale for future machines with 64 or 128 cores.

maybe some extension of REDIRECT target, being able to add cpu number to
destination port :

iptables -A PREROUTING -t nat -m tcp --dport 1234 --REDIRECT 1234+cpu


> ...
> 
> No other things to change, it can take full advantage of the
> underlying hardware transparently.
> 

Coming to mind would be a new socket operation, "bind to cpu", like the
"bind to device" operation.

This would work without need for netfilter (and permission to change its
rules)

But it would require changes to applications, to fully exploit SMP
capabilities of machine.




^ permalink raw reply

* pull request: wireless-2.6 2010-03-18
From: John W. Linville @ 2010-03-18 20:35 UTC (permalink / raw)
  To: davem; +Cc: linux-wireless, netdev, linux-kernel

Dave,

Three more intended for 2.6.34...  The biggest one is for ath9k, which
removes some unused code along with the one-liner that makes the code
unused.  (Did you follow that?)  The others are a null-pointer fix and a
"hush dawg" for a noisy warning message.

Please let me know if there are problems!

Thanks,

John

---

The following changes since commit 22001a13d09d82772e831dcdac0553994a4bac5d:
  Tilman Schmidt (1):
        gigaset: fix build failure

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-2.6.git master

Adel Gadllah (1):
      iwlwifi: Silence tfds_in_queue message

Felix Fietkau (1):
      ath9k: fix BUG_ON triggered by PAE frames

Grazvydas Ignotas (1):
      wl1251: fix potential crash

 drivers/net/wireless/ath/ath9k/xmit.c        |   21 +--------------------
 drivers/net/wireless/iwlwifi/iwl-tx.c        |    2 +-
 drivers/net/wireless/wl12xx/wl1251_debugfs.c |    3 ++-
 3 files changed, 4 insertions(+), 22 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c
index b2c8207..294b486 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -1353,25 +1353,6 @@ static enum ath9k_pkt_type get_hw_packet_type(struct sk_buff *skb)
 	return htype;
 }
 
-static bool is_pae(struct sk_buff *skb)
-{
-	struct ieee80211_hdr *hdr;
-	__le16 fc;
-
-	hdr = (struct ieee80211_hdr *)skb->data;
-	fc = hdr->frame_control;
-
-	if (ieee80211_is_data(fc)) {
-		if (ieee80211_is_nullfunc(fc) ||
-		    /* Port Access Entity (IEEE 802.1X) */
-		    (skb->protocol == cpu_to_be16(ETH_P_PAE))) {
-			return true;
-		}
-	}
-
-	return false;
-}
-
 static int get_hw_crypto_keytype(struct sk_buff *skb)
 {
 	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb);
@@ -1696,7 +1677,7 @@ static void ath_tx_start_dma(struct ath_softc *sc, struct ath_buf *bf,
 			goto tx_done;
 		}
 
-		if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && !is_pae(skb)) {
+		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
 			/*
 			 * Try aggregation if it's a unicast data frame
 			 * and the destination is HT capable.
diff --git a/drivers/net/wireless/iwlwifi/iwl-tx.c b/drivers/net/wireless/iwlwifi/iwl-tx.c
index 1ed5206..8c12311 100644
--- a/drivers/net/wireless/iwlwifi/iwl-tx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-tx.c
@@ -124,7 +124,7 @@ void iwl_free_tfds_in_queue(struct iwl_priv *priv,
 	if (priv->stations[sta_id].tid[tid].tfds_in_queue >= freed)
 		priv->stations[sta_id].tid[tid].tfds_in_queue -= freed;
 	else {
-		IWL_ERR(priv, "free more than tfds_in_queue (%u:%d)\n",
+		IWL_DEBUG_TX(priv, "free more than tfds_in_queue (%u:%d)\n",
 			priv->stations[sta_id].tid[tid].tfds_in_queue,
 			freed);
 		priv->stations[sta_id].tid[tid].tfds_in_queue = 0;
diff --git a/drivers/net/wireless/wl12xx/wl1251_debugfs.c b/drivers/net/wireless/wl12xx/wl1251_debugfs.c
index 0ccba57..05e4d68 100644
--- a/drivers/net/wireless/wl12xx/wl1251_debugfs.c
+++ b/drivers/net/wireless/wl12xx/wl1251_debugfs.c
@@ -466,7 +466,8 @@ out:
 
 void wl1251_debugfs_reset(struct wl1251 *wl)
 {
-	memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats));
+	if (wl->stats.fw_stats != NULL)
+		memset(wl->stats.fw_stats, 0, sizeof(*wl->stats.fw_stats));
 	wl->stats.retry_count = 0;
 	wl->stats.excessive_retries = 0;
 }
-- 
John W. Linville		Someday the world will need a hero, and you
linville@tuxdriver.com			might be all we have.  Be ready.

^ permalink raw reply related

* Re: [PATCH] xfrm: cache bundle lookup results in flow cache
From: Timo Teräs @ 2010-03-18 19:30 UTC (permalink / raw)
  To: Herbert Xu; +Cc: netdev
In-Reply-To: <4BA0FBB6.10208@iki.fi>

Timo Teräs wrote:
> Herbert Xu wrote:
>> On Wed, Mar 17, 2010 at 04:16:21PM +0200, Timo Teräs wrote:
>>> The problem is if I have multipoint gre1 and policy that says
>>> "encrypt all gre in transport mode".
>>>
>>> Thus for each public address, I get one bundle. But the
>>> xfrm_lookup() is called for each packet because ipgre_tunnel_xmit()
>>> calls ip_route_output_key() on per-packet basis.
>>>
>>> For my use-case it makes a huge difference.
>>
>> But if your traffic switches between those tunnels on each packet,
>> we're back to square one, right?
> 
> Not to my understanding. Why would it change?

Here's how things go to my understanding.

When we are forwarding packets, each packet goes through
__xfrm_route_forward(). Or if we are sending from ip_gre (like in
my case), the packets go through ip_route_output_key().

Both of these call xfrm_lookup() to get the xfrm_dst instead
of the real rtable dst. This is done on per-packet basis.
Basically, the xfrm_dst is never kept referenced directly
on either code path. Instead it needs to be xfrm'ed per-packet.

Now, these created xfrm_dst gets cached in policy->bundles
with ref count zero and not deleted until garbage collection
is required. That's how xfrm_find_bundle tries to reuse them
(but it does O(n) lookup).

On the gre+esp case (should apply to any forward path too)
the caching of bundle speeds up the output path considerably
as there can be a lot of xfrm_dst's. Especially if it's a
wildcard transport mode policy which basically get's bundles
for each destination. So there can be a lot of xfrm_dst's
all valid, since they refer to unique xfrm_state on
per-destination basis.

Now, even more, since xfrm_dst needs to be regenerated if
the underlying ipv4 rtable entry has expired there can be
a lot of bundles. So the linear search is a major performance
killer.

Btw. it looks like the xfrm_dst garbage collection is broke.
It's only garbage collected if a network device goes down,
or dst_alloc calls it after gc threshold is exceeded. Since
gc threshold got dynamic not long ago, it can be very big.
This causes stale xfrm_dst's to be kept alive, and what is
worse their inner rtable dst is kept referenced. But as they
were expired and dst_free'd the dst core "free still referenced"
dst's goes through that list over and over again and will
kill the whole system performance when the list grows long.

I think as a minimum we should add 'do stale_bundle' check
to all xfrm_dst's every n minutes or so.

>>> Then we cannot maintain policy use time. But if it's not a
>>> requirement, we could drop the policy from cache.
>>
>> I don't see why we can't maintain the policy use time if we did
>> this, all you need is a back-pointer from the top xfrm_dst.
> 
> Sure.

Actually no. As the pmtu case showed, it's more likely that
xfrm_dst needs to be regenerated, but the policy stays the
same since policy db isn't touched that often. If we keep
them separately we can almost most of the time avoid doing
policy lookup which is also O(n). Also the currently cache
entry validation is needs to check policy's bundles_genid
before allowing touching of xfrm_dst. Otherwise we would have
to keep global bundle_genid, and we'd lose the parent pointer
on cache miss.

Caching bundles be another win too. Since if do cache entries
like this, we could track how many cache miss xfrm_dst's
we've had and use that decide when to trigger xfrm_dst
garbage collector instead of (or in addition to) fixed timer.

- Timo

^ permalink raw reply

* [PATCH] smsc95xx: Fix tx checksum offload for small packets
From: Steve Glendinning @ 2010-03-18 19:20 UTC (permalink / raw)
  To: netdev

TX checksum offload does not work properly when transmitting
UDP packets with 0, 1 or 2 bytes of data.  This patch works
around the problem by calculating checksums for these packets
in the driver.

Signed-off-by: Steve Glendinning <steve.glendinning@smsc.com>
---
 drivers/net/usb/smsc95xx.c |   18 +++++++++++++++---
 1 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c
index d222d7e..829963f 100644
--- a/drivers/net/usb/smsc95xx.c
+++ b/drivers/net/usb/smsc95xx.c
@@ -1189,9 +1189,21 @@ static struct sk_buff *smsc95xx_tx_fixup(struct usbnet *dev,
 	}
 
 	if (csum) {
-		u32 csum_preamble = smsc95xx_calc_csum_preamble(skb);
-		skb_push(skb, 4);
-		memcpy(skb->data, &csum_preamble, 4);
+		if (skb->len <= 45) {
+			/* workaround - hardware tx checksum does not work
+			 * properly with extremely small packets */
+			long csstart = skb->csum_start - skb_headroom(skb);
+			__wsum calc = csum_partial(skb->data + csstart,
+				skb->len - csstart, 0);
+			*((__sum16 *)(skb->data + csstart 
+				+ skb->csum_offset)) = csum_fold(calc);
+
+			csum = false;
+		} else {
+			u32 csum_preamble = smsc95xx_calc_csum_preamble(skb);
+			skb_push(skb, 4);
+			memcpy(skb->data, &csum_preamble, 4);
+		}
 	}
 
 	skb_push(skb, 4);
-- 
1.6.6.1


^ permalink raw reply related

* Re: [Bugme-new] [Bug 15474] New: r8169 fails to bring up ethernet
From: Andrew Morton @ 2010-03-18 19:08 UTC (permalink / raw)
  To: Jesse Barnes, Tejun Heo
  Cc: bugzilla-daemon, bugme-daemon, linux-pci, netdev, Francois Romieu,
	rootkit85
In-Reply-To: <bug-15474-10286@http.bugzilla.kernel.org/>


(switched to email.  Please respond via emailed reply-to-all, not via the
bugzilla web interface).

On Sun, 7 Mar 2010 23:57:32 GMT
bugzilla-daemon@bugzilla.kernel.org wrote:

> http://bugzilla.kernel.org/show_bug.cgi?id=15474

Thanks for doing the bisection - it really helps.

Guys, this is a 2.6.32 -> 2.6.33 regression.

>            Summary: r8169 fails to bring up ethernet
>            Product: Drivers
>            Version: 2.5
>     Kernel Version: 2.6.33
>           Platform: All
>         OS/Version: Linux
>               Tree: Mainline
>             Status: NEW
>           Severity: normal
>           Priority: P1
>          Component: Network
>         AssignedTo: drivers_network@kernel-bugs.osdl.org
>         ReportedBy: rootkit85@yahoo.it
>         Regression: Yes
> 
> 
> Created an attachment (id=25399)
>  --> (http://bugzilla.kernel.org/attachment.cgi?id=25399)
> System log
> 
> With the release 2.6.33 the kernel can't bring up my ethernet.
> I have found the commit which broke it:
> 
> root@raver:/usr/src/linux-2.6# git bisect good
> ac1aa47b131416a6ff37eb1005a0a1d2541aad6c is the first bad commit
> commit ac1aa47b131416a6ff37eb1005a0a1d2541aad6c
> Author: Jesse Barnes <jbarnes@virtuousgeek.org>
> Date:   Mon Oct 26 13:20:44 2009 -0700
> 
>     PCI: determine CLS more intelligently
> 
>     Till now, CLS has been determined either by arch code or as
>     L1_CACHE_BYTES.  Only x86 and ia64 set CLS explicitly and x86 doesn't
>     always get it right.  On most configurations, the chance is that
>     firmware configures the correct value during boot.
> 
>     This patch makes pci_init() determine CLS by looking at what firmware
>     has configured.  It scans all devices and if all non-zero values
>     agree, the value is used.  If none is configured or there is a
>     disagreement, pci_dfl_cache_line_size is used.  arch can set the dfl
>     value (via PCI_CACHE_LINE_BYTES or pci_dfl_cache_line_size) or
>     override the actual one.
> 
>     ia64, x86 and sparc64 updated to set the default cls instead of the
>     actual one.                                                                 
> 
>     While at it, declare pci_cache_line_size and pci_dfl_cache_line_size        
>     in pci.h and drop private declarations from arch code.                      
> 
>     Signed-off-by: Tejun Heo <tj@kernel.org>                                    
>     Acked-by: David Miller <davem@davemloft.net>                                
>     Acked-by: Greg KH <gregkh@suse.de>                                          
>     Cc: Ingo Molnar <mingo@elte.hu>                                             
>     Cc: Thomas Gleixner <tglx@linutronix.de>                                    
>     Cc: Tony Luck <tony.luck@intel.com>                                         
>     Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
> 
> :040000 040000 8b20ad60ed3e273b74bfb588dbea6948547e8de1
> 92498585770ca360c2716c0d3c55d5f4a37356a1 M      arch
> :040000 040000 56d1abd61286dd303bb37c2002b699e526988f85
> 3f20bba2d1e107a80a738e3561fc0fa92e0c4024 M      drivers
> :040000 040000 26d85393248c542ca2cea0e3ac4ceabd0ea659aa
> 326cbb98321cd4e490d888f48bc584b3662c8f06 M      include

^ permalink raw reply

* Re: [PATCH 1/3] netlink: fix NETLINK_RECV_NO_ENOBUFS in netlink_set_err()
From: Pablo Neira Ayuso @ 2010-03-18 16:34 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, davem
In-Reply-To: <4BA22463.6050601@trash.net>

Patrick McHardy wrote:
> Pablo Neira Ayuso wrote:
>> Patrick McHardy wrote:
>>> Pablo Neira Ayuso wrote:
>>>> Currently, ENOBUFS errors are reported to the socket via
>>>> netlink_set_err() even if NETLINK_RECV_NO_ENOBUFS is set. However,
>>>> that should not happen. This fixes this problem and it changes the
>>>> prototype of netlink_set_err() to return the number of sockets whose
>>>> error has been set. This allows to know if any error has been set.
>>>> This return value is used in the next patch in these bugfix series.
>>> But that only happens if we have a message allocate error, which is
>>> a different situation than rcvqueue overrun, which I thought the
>>> original patch was supposed to handle (disable netlink congestion
>>> control).
>> Yes, allocation is a different situation but we still report ENOBUFS to
>> user-space. I think that NETLINK_RECV_NO_ENOBUFS is there to a) disable
>> ENOBUFS reports to user-space and b) disable Netlink congestion.
>>
>>> Is there any problem with these errors?
>> Specifically in ctnetlink, if we fail to allocate a message in ctnetlink
>> and NETLINK_RECV_NO_ENOBUFS is set, we still lose an event and that
>> should not happen.
> 
> I assume you mean "not set"? Otherwise I fail to follow :)

OK, I'll try again :-)

Currently, no matter if NETLINK_RECV_NO_ENOBUFS is set or not: if we
fail to allocate the netlink message, then ctnetlink_conntrack_event()
returns 0. Thus, we report ENOBUFS to user-space and we lose the event.

With my patches, if NETLINK_RECV_NO_ENOBUFS is set and we fail to
allocate the message, we don't report ENOBUFS and we don't lose the event.

^ permalink raw reply

* [RFC PATCH 2/2] iproute2: Add support for static L2TPv3 tunnels.
From: James Chapman @ 2010-03-18 18:14 UTC (permalink / raw)
  To: shemminger; +Cc: netdev
In-Reply-To: <20100318181416.6862.60894.stgit@bert.katalix.com>

Requires kernel with L2TPv3 ethernet support.

This patch adds a new series of commands under "ip l2tp", to configure
and show L2TPv3 static tunnels. Only L2TPv3 ethernet pseudowires are
supported at the moment.

Usage: ip l2tp add tunnel
          remote ADDR local ADDR
          tunnel_id ID peer_tunnel_id ID
          [ encap { ip | udp } ]
          [ udp_sport PORT ] [ udp_dport PORT ]
       ip l2tp add session
          tunnel_id ID
          session_id ID peer_session_id ID
          [ cookie HEXSTR ] [ peer_cookie HEXSTR ]
          [ offset OFFSET ] [ peer_offset OFFSET ]
       ip l2tp del tunnel tunnel_id ID
       ip l2tp del session tunnel_id ID session_id ID
       ip l2tp show tunnel [ tunnel_id ID ]
       ip l2tp show session [ tunnel_id ID ] [ session_id ID ]

Where: NAME   := STRING
       ADDR   := { IP_ADDRESS | any }
       PORT   := { 0..65535 }
       ID     := { 1..4294967295 }
       HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }
---
 include/linux/l2tp.h |  164 ++++++++++
 ip/Makefile          |    2 
 ip/ip.c              |    3 
 ip/ip_common.h       |    1 
 ip/ipl2tp.c          |  815 ++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 983 insertions(+), 2 deletions(-)
 create mode 100644 include/linux/l2tp.h
 create mode 100644 ip/ipl2tp.c

diff --git a/include/linux/l2tp.h b/include/linux/l2tp.h
new file mode 100644
index 0000000..c22a119
--- /dev/null
+++ b/include/linux/l2tp.h
@@ -0,0 +1,164 @@
+/*
+ * L2TP public kernel interfaces.
+ *
+ * Author: James Chapman <jchapman@katalix.com>
+ */
+
+#ifndef _LINUX_L2TP_H_
+#define _LINUX_L2TP_H_
+
+#include <linux/types.h>
+#ifdef __KERNEL__
+#include <linux/socket.h>
+#include <linux/in.h>
+#else
+#include <netinet/in.h>
+#endif
+
+#define IPPROTO_L2TP		115
+
+/**
+ * struct sockaddr_l2tpip - the sockaddr structure for L2TP-over-IP sockets
+ * @l2tp_family:  address family number AF_L2TPIP.
+ * @l2tp_addr:    protocol specific address information
+ * @l2tp_conn_id: connection id of tunnel
+ */
+#define __SOCK_SIZE__	16		/* sizeof(struct sockaddr)	*/
+struct sockaddr_l2tpip {
+	/* The first fields must match struct sockaddr_in */
+	sa_family_t	l2tp_family;	/* AF_INET */
+	__be16		l2tp_unused;	/* INET port number (unused) */
+	struct in_addr	l2tp_addr;	/* Internet address */
+
+	__u32		l2tp_conn_id;	/* Connection ID of tunnel */
+
+	/* Pad to size of `struct sockaddr'. */
+	unsigned char	__pad[sizeof(struct sockaddr) - sizeof(sa_family_t) -
+			      sizeof(__be16) - sizeof(struct in_addr) -
+			      sizeof(__u32)];
+};
+
+/*****************************************************************************
+ *  NETLINK_GENERIC netlink family.
+ *****************************************************************************/
+
+/*
+ * Commands.
+ * Valid TLVs of each command are:-
+ * TUNNEL_CREATE	- CONN_ID, pw_type, netns, ifname, ipinfo, udpinfo, udpcsum, vlanid
+ * TUNNEL_DELETE	- CONN_ID
+ * TUNNEL_MODIFY	- CONN_ID, udpcsum
+ * TUNNEL_GETSTATS	- CONN_ID, (stats)
+ * TUNNEL_GET		- CONN_ID, (...)
+ * SESSION_CREATE	- SESSION_ID, PW_TYPE, offset, data_seq, cookie, peer_cookie, offset, l2spec
+ * SESSION_DELETE	- SESSION_ID
+ * SESSION_MODIFY	- SESSION_ID, data_seq
+ * SESSION_GET		- SESSION_ID, (...)
+ * SESSION_GETSTATS	- SESSION_ID, (stats)
+ *
+ */
+enum {
+	L2TP_CMD_NOOP,
+	L2TP_CMD_TUNNEL_CREATE,
+	L2TP_CMD_TUNNEL_DELETE,
+	L2TP_CMD_TUNNEL_MODIFY,
+	L2TP_CMD_TUNNEL_GET,
+	L2TP_CMD_SESSION_CREATE,
+	L2TP_CMD_SESSION_DELETE,
+	L2TP_CMD_SESSION_MODIFY,
+	L2TP_CMD_SESSION_GET,
+	__L2TP_CMD_MAX,
+};
+
+#define  L2TP_CMD_MAX		    	(__L2TP_CMD_MAX - 1)
+
+/*
+ * ATTR types defined for L2TP
+ */
+enum {
+	L2TP_ATTR_NONE,			/* no data */
+	L2TP_ATTR_PW_TYPE,		/* u16, enum l2tp_pwtype */
+	L2TP_ATTR_ENCAP_TYPE,		/* u16, enum l2tp_encap_type */
+	L2TP_ATTR_OFFSET,		/* u16 */
+	L2TP_ATTR_DATA_SEQ,		/* u16 */
+	L2TP_ATTR_L2SPEC_TYPE,		/* u8, enum l2tp_l2spec_type */
+	L2TP_ATTR_L2SPEC_LEN,		/* u8, enum l2tp_l2spec_type */
+	L2TP_ATTR_PROTO_VERSION,	/* u8 */
+	L2TP_ATTR_IFNAME,		/* string */
+	L2TP_ATTR_CONN_ID,		/* u32 */
+	L2TP_ATTR_PEER_CONN_ID,		/* u32 */
+	L2TP_ATTR_SESSION_ID,		/* u32 */
+	L2TP_ATTR_PEER_SESSION_ID,	/* u32 */
+	L2TP_ATTR_UDP_CSUM,		/* flag */
+	L2TP_ATTR_VLAN_ID,		/* u16 */
+	L2TP_ATTR_COOKIE,		/* 0, 4 or 8 bytes */
+	L2TP_ATTR_PEER_COOKIE,		/* 0, 4 or 8 bytes */
+	L2TP_ATTR_DEBUG,		/* u32 */
+	L2TP_ATTR_RECV_SEQ,		/* flag */
+	L2TP_ATTR_SEND_SEQ,		/* flag */
+	L2TP_ATTR_LNS_MODE,		/* flag */
+	L2TP_ATTR_USING_IPSEC,		/* flag */
+	L2TP_ATTR_RECV_TIMEOUT,		/* msec */
+	L2TP_ATTR_FD,			/* int */
+	L2TP_ATTR_IP_SADDR,		/* u32 */
+	L2TP_ATTR_IP_DADDR,		/* u32 */
+	L2TP_ATTR_UDP_SPORT,		/* u16 */
+	L2TP_ATTR_UDP_DPORT,		/* u16 */
+	L2TP_ATTR_MTU,			/* u16 */
+	L2TP_ATTR_MRU,			/* u16 */
+	L2TP_ATTR_STATS,		/* nested */
+	__L2TP_ATTR_MAX,
+};
+
+#define L2TP_ATTR_MAX			(__L2TP_ATTR_MAX - 1)
+
+/* Nested in L2TP_ATTR_STATS */
+enum {
+	L2TP_ATTR_STATS_NONE,		/* no data */
+	L2TP_ATTR_TX_PACKETS,		/* u64 */
+	L2TP_ATTR_TX_BYTES,		/* u64 */
+	L2TP_ATTR_TX_ERRORS,		/* u64 */
+	L2TP_ATTR_RX_PACKETS,		/* u64 */
+	L2TP_ATTR_RX_BYTES,		/* u64 */
+	L2TP_ATTR_RX_SEQ_DISCARDS,	/* u64 */
+	L2TP_ATTR_RX_OOS_PACKETS,	/* u64 */
+	L2TP_ATTR_RX_ERRORS,		/* u64 */
+	__L2TP_ATTR_STATS_MAX,
+};
+
+#define L2TP_ATTR_STATS_MAX		(__L2TP_ATTR_STATS_MAX - 1)
+
+enum l2tp_pwtype {
+	L2TP_PWTYPE_NONE = 0x0000,
+	L2TP_PWTYPE_ETH_VLAN = 0x0004,
+	L2TP_PWTYPE_ETH = 0x0005,
+	L2TP_PWTYPE_PPP = 0x0007,
+	L2TP_PWTYPE_PPP_AC = 0x0008,
+	L2TP_PWTYPE_IP = 0x000b,
+	__L2TP_PWTYPE_MAX
+};
+
+enum l2tp_l2spec_type {
+	L2TP_L2SPECTYPE_NONE,
+	L2TP_L2SPECTYPE_DEFAULT,
+};
+
+enum l2tp_encap_type {
+	L2TP_ENCAPTYPE_UDP,
+	L2TP_ENCAPTYPE_IP,
+};
+
+
+enum l2tp_seqmode {
+	L2TP_SEQ_NONE = 0,
+	L2TP_SEQ_IP = 1,
+	L2TP_SEQ_ALL = 2,
+};
+
+/*
+ * NETLINK_GENERIC related info
+ */
+#define L2TP_GENL_NAME		"l2tp"
+#define L2TP_GENL_VERSION	0x1
+
+#endif
diff --git a/ip/Makefile b/ip/Makefile
index 2f223ca..b167770 100644
--- a/ip/Makefile
+++ b/ip/Makefile
@@ -3,7 +3,7 @@ IPOBJ=ip.o ipaddress.o ipaddrlabel.o iproute.o iprule.o \
     ipmaddr.o ipmonitor.o ipmroute.o ipprefix.o iptuntap.o \
     ipxfrm.o xfrm_state.o xfrm_policy.o xfrm_monitor.o \
     iplink_vlan.o link_veth.o link_gre.o iplink_can.o \
-    iplink_macvlan.o
+    iplink_macvlan.o ipl2tp.o
 
 RTMONOBJ=rtmon.o
 
diff --git a/ip/ip.c b/ip/ip.c
index e0cf175..b1b5fb9 100644
--- a/ip/ip.c
+++ b/ip/ip.c
@@ -42,7 +42,7 @@ static void usage(void)
 "Usage: ip [ OPTIONS ] OBJECT { COMMAND | help }\n"
 "       ip [ -force ] -batch filename\n"
 "where  OBJECT := { link | addr | addrlabel | route | rule | neigh | ntable |\n"
-"                   tunnel | tuntap | maddr | mroute | monitor | xfrm }\n"
+"                   tunnel | tuntap | maddr | mroute | monitor | xfrm | l2tp }\n"
 "       OPTIONS := { -V[ersion] | -s[tatistics] | -d[etails] | -r[esolve] |\n"
 "                    -f[amily] { inet | inet6 | ipx | dnet | link } |\n"
 "                    -o[neline] | -t[imestamp] | -b[atch] [filename] |\n"
@@ -73,6 +73,7 @@ static const struct cmd {
 	{ "tunl",	do_iptunnel },
 	{ "tuntap",	do_iptuntap },
 	{ "tap",	do_iptuntap },
+	{ "l2tp",	do_ipl2tp },
 	{ "monitor",	do_ipmonitor },
 	{ "xfrm",	do_xfrm },
 	{ "mroute",	do_multiroute },
diff --git a/ip/ip_common.h b/ip/ip_common.h
index c857667..e01be73 100644
--- a/ip/ip_common.h
+++ b/ip/ip_common.h
@@ -38,6 +38,7 @@ extern int do_ipmonitor(int argc, char **argv);
 extern int do_multiaddr(int argc, char **argv);
 extern int do_multiroute(int argc, char **argv);
 extern int do_xfrm(int argc, char **argv);
+extern int do_ipl2tp(int argc, char **argv);
 
 static inline int rtm_get_table(struct rtmsg *r, struct rtattr **tb)
 {
diff --git a/ip/ipl2tp.c b/ip/ipl2tp.c
new file mode 100644
index 0000000..520a07d
--- /dev/null
+++ b/ip/ipl2tp.c
@@ -0,0 +1,815 @@
+/*
+ * ipl2tp.c	       "ip l2tp"
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * Authors:	James Chapman <jchapman@katalix.com>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+
+#include <netlink/netlink.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/mngt.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/utils.h>
+
+#include <linux/genetlink.h>
+#include <linux/l2tp.h>
+
+#include "utils.h"
+#include "ip_common.h"
+
+enum {
+	L2TP_ADD,
+	L2TP_CHG,
+	L2TP_DEL,
+	L2TP_GET
+};
+
+struct l2tp_parm {
+	uint32_t tunnel_id;
+	uint32_t peer_tunnel_id;
+	uint32_t session_id;
+	uint32_t peer_session_id;
+	uint32_t offset;
+	uint32_t peer_offset;
+	enum l2tp_encap_type encap;
+	uint16_t local_udp_port;
+	uint16_t peer_udp_port;
+	int cookie_len;
+	uint8_t cookie[8];
+	int peer_cookie_len;
+	uint8_t peer_cookie[8];
+	struct in_addr local_ip;
+	struct in_addr peer_ip;
+
+	uint16_t pw_type;
+	uint16_t mtu;
+	int udp_csum:1;
+	int recv_seq:1;
+	int send_seq:1;
+	int lns_mode:1;
+	int data_seq:2;
+	int tunnel:1;
+	int session:1;
+	int reorder_timeout;
+	char *ifname;
+};
+
+struct l2tp_stats {
+	uint64_t data_rx_packets;
+	uint64_t data_rx_bytes;
+	uint64_t data_rx_errors;
+	uint64_t data_rx_oos_packets;
+	uint64_t data_rx_oos_discards;
+	uint64_t data_tx_packets;
+	uint64_t data_tx_bytes;
+	uint64_t data_tx_errors;
+};
+
+struct l2tp_data {
+	struct l2tp_parm config;
+	struct l2tp_stats stats;
+};
+
+/* netlink socket */
+static struct nl_handle *nl_sock;
+static int nl_family;
+
+/*****************************************************************************
+ * Netlink actions
+ *****************************************************************************/
+
+static int create_tunnel(struct l2tp_parm *p)
+{
+	struct nl_msg *msg;
+	int result = 0;
+
+	msg = nlmsg_alloc();
+
+	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl_family, 0, NLM_F_REQUEST,
+		    L2TP_CMD_TUNNEL_CREATE, L2TP_GENL_VERSION);
+
+	nla_put_u32(msg, L2TP_ATTR_CONN_ID, p->tunnel_id);
+	nla_put_u32(msg, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
+	nla_put_u8(msg, L2TP_ATTR_PROTO_VERSION, 3);
+	nla_put_u16(msg, L2TP_ATTR_ENCAP_TYPE, p->encap);
+
+	nla_put_u32(msg, L2TP_ATTR_IP_SADDR, p->local_ip.s_addr);
+	nla_put_u32(msg, L2TP_ATTR_IP_DADDR, p->peer_ip.s_addr);
+	if (p->encap == L2TP_ENCAPTYPE_UDP) {
+		nla_put_u16(msg, L2TP_ATTR_UDP_SPORT, p->local_udp_port);
+		nla_put_u16(msg, L2TP_ATTR_UDP_DPORT, p->peer_udp_port);
+	}
+
+	nl_send_auto_complete(nl_sock, msg);
+
+	nlmsg_free(msg);
+
+	result = nl_wait_for_ack(nl_sock);
+	if (result > 0) {
+		result = 0;
+	}
+
+	return result;
+}
+
+static int delete_tunnel(struct l2tp_parm *p)
+{
+	struct nl_msg *msg;
+	int result;
+
+	msg = nlmsg_alloc();
+
+	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl_family, 0, NLM_F_REQUEST,
+		    L2TP_CMD_TUNNEL_DELETE, L2TP_GENL_VERSION);
+
+	nla_put_u32(msg, L2TP_ATTR_CONN_ID, p->tunnel_id);
+
+	nl_send_auto_complete(nl_sock, msg);
+
+	nlmsg_free(msg);
+
+	result = nl_wait_for_ack(nl_sock);
+	if (result > 0) {
+		result = 0;
+	}
+
+	return result;
+}
+
+static int create_session(struct l2tp_parm *p)
+{
+	struct nl_msg *msg;
+	int result = 0;
+
+	msg = nlmsg_alloc();
+
+	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl_family, 0, NLM_F_REQUEST,
+		    L2TP_CMD_SESSION_CREATE, L2TP_GENL_VERSION);
+
+	nla_put_u32(msg, L2TP_ATTR_CONN_ID, p->tunnel_id);
+	nla_put_u32(msg, L2TP_ATTR_PEER_CONN_ID, p->peer_tunnel_id);
+	nla_put_u32(msg, L2TP_ATTR_SESSION_ID, p->session_id);
+	nla_put_u32(msg, L2TP_ATTR_PEER_SESSION_ID, p->peer_session_id);
+	nla_put_u16(msg, L2TP_ATTR_PW_TYPE, p->pw_type);
+	if (p->mtu) {
+		nla_put_u16(msg, L2TP_ATTR_MTU, p->mtu);
+	}
+	if (p->recv_seq) {
+		nla_put_flag(msg, L2TP_ATTR_RECV_SEQ);
+	}
+	if (p->send_seq) {
+		nla_put_flag(msg, L2TP_ATTR_SEND_SEQ);
+	}
+	if (p->lns_mode) {
+		nla_put_flag(msg, L2TP_ATTR_LNS_MODE);
+	}
+	if (p->data_seq) {
+		nla_put_u8(msg, L2TP_ATTR_DATA_SEQ, p->data_seq);
+	}
+	if (p->reorder_timeout) {
+		nla_put_msecs(msg, L2TP_ATTR_RECV_TIMEOUT, p->reorder_timeout);
+	}
+	if (p->offset) {
+		nla_put_u16(msg, L2TP_ATTR_OFFSET, p->offset);
+	}
+	if (p->cookie_len) {
+		nla_put(msg, L2TP_ATTR_COOKIE, p->cookie_len, p->cookie);
+	}
+	if (p->peer_cookie_len) {
+		nla_put(msg, L2TP_ATTR_PEER_COOKIE, p->peer_cookie_len, p->peer_cookie);
+	}
+	if (p->ifname && p->ifname[0]) {
+		nla_put_string(msg, L2TP_ATTR_IFNAME, p->ifname);
+	}
+
+	nl_send_auto_complete(nl_sock, msg);
+
+	nlmsg_free(msg);
+
+	result = nl_wait_for_ack(nl_sock);
+	if (result > 0) {
+		result = 0;
+	}
+
+	return result;
+}
+
+static int delete_session(struct l2tp_parm *p)
+{
+	struct nl_msg *msg;
+	int result = 0;
+
+	msg = nlmsg_alloc();
+
+	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl_family, 0, NLM_F_REQUEST,
+		    L2TP_CMD_SESSION_DELETE, L2TP_GENL_VERSION);
+
+	nla_put_u32(msg, L2TP_ATTR_CONN_ID, p->tunnel_id);
+	nla_put_u32(msg, L2TP_ATTR_SESSION_ID, p->session_id);
+
+	nl_send_auto_complete(nl_sock, msg);
+
+	nlmsg_free(msg);
+
+	result = nl_wait_for_ack(nl_sock);
+	if (result > 0) {
+		result = 0;
+	}
+
+	return result;
+}
+
+static void print_cookie(char *name, uint8_t *cookie, int len)
+{
+	printf("  %s %02x%02x%02x%02x", name,
+	       cookie[0], cookie[1],
+	       cookie[2], cookie[3]);
+	if (len == 8)
+		printf("%02x%02x%02x%02x",
+		       cookie[4], cookie[5],
+		       cookie[6], cookie[7]);
+}
+
+static void print_tunnel(struct l2tp_data *data)
+{
+	struct l2tp_parm *p = &data->config;
+
+	printf("Tunnel %u, encap %s\n",
+	       p->tunnel_id,
+	       p->encap == L2TP_ENCAPTYPE_UDP ? "UDP" :
+	       p->encap == L2TP_ENCAPTYPE_IP ? "IP" : "??");
+	printf("  From %s ", inet_ntoa(p->local_ip));
+	printf("to %s\n", inet_ntoa(p->peer_ip));
+	printf("  Peer tunnel %u\n",
+	       p->peer_tunnel_id);
+
+	if (p->encap == L2TP_ENCAPTYPE_UDP)
+		printf("  UDP source / dest ports: %hu/%hu\n",
+		       p->local_udp_port, p->peer_udp_port);
+}
+
+static void print_session(struct l2tp_data *data)
+{
+	struct l2tp_parm *p = &data->config;
+
+	printf("Session %u in tunnel %u\n",
+	       p->session_id, p->tunnel_id);
+	printf("  Peer session %u, tunnel %u\n",
+	       p->peer_session_id, p->peer_tunnel_id);
+
+	if (p->ifname != NULL) {
+		printf("  interface name: %s\n", p->ifname);
+	}
+	printf("  offset %u, peer offset %u\n",
+	       p->offset, p->peer_offset);
+	if (p->cookie_len > 0)
+		print_cookie("cookie", p->cookie, p->cookie_len);
+	if (p->peer_cookie_len > 0)
+		print_cookie("peer cookie", p->peer_cookie, p->peer_cookie_len);
+
+	if (p->reorder_timeout != 0) {
+		printf("  reorder timeout: %u\n", p->reorder_timeout);
+	}
+}
+
+static int nl_get_response(struct nl_msg *msg, void *arg)
+{
+	struct l2tp_data *data = arg;
+	struct l2tp_parm *p = &data->config;
+	struct nlmsghdr *nlh = nlmsg_hdr(msg);
+	struct nlattr *attrs[L2TP_ATTR_MAX + 1];
+	struct nlattr *nla_stats;
+	int result = 0;
+
+	/* Validate message and parse attributes */
+	genlmsg_parse(nlh, 0, attrs, L2TP_ATTR_MAX, NULL);
+	if (nlh->nlmsg_type == NLMSG_ERROR) {
+		result = -EBADMSG;
+		goto out;
+	}
+
+	if (attrs[L2TP_ATTR_PW_TYPE])
+		p->pw_type = nla_get_u16(attrs[L2TP_ATTR_PW_TYPE]);
+	if (attrs[L2TP_ATTR_ENCAP_TYPE])
+		p->encap = nla_get_u16(attrs[L2TP_ATTR_ENCAP_TYPE]);
+	if (attrs[L2TP_ATTR_OFFSET])
+		p->offset = nla_get_u16(attrs[L2TP_ATTR_OFFSET]);
+	if (attrs[L2TP_ATTR_DATA_SEQ])
+		p->data_seq = nla_get_u16(attrs[L2TP_ATTR_DATA_SEQ]);
+	if (attrs[L2TP_ATTR_CONN_ID])
+		p->tunnel_id = nla_get_u32(attrs[L2TP_ATTR_CONN_ID]);
+	if (attrs[L2TP_ATTR_PEER_CONN_ID])
+		p->peer_tunnel_id = nla_get_u32(attrs[L2TP_ATTR_PEER_CONN_ID]);
+	if (attrs[L2TP_ATTR_SESSION_ID])
+		p->session_id = nla_get_u32(attrs[L2TP_ATTR_SESSION_ID]);
+	if (attrs[L2TP_ATTR_PEER_SESSION_ID])
+		p->peer_session_id = nla_get_u32(attrs[L2TP_ATTR_PEER_SESSION_ID]);
+	if (attrs[L2TP_ATTR_UDP_CSUM])
+		p->udp_csum = nla_get_flag(attrs[L2TP_ATTR_UDP_CSUM]);
+	if (attrs[L2TP_ATTR_COOKIE]) {
+		nla_memcpy(&p->cookie[0], attrs[L2TP_ATTR_COOKIE], sizeof(p->cookie));
+		p->cookie_len = nla_len(attrs[L2TP_ATTR_COOKIE]);
+	}
+	if (attrs[L2TP_ATTR_PEER_COOKIE]) {
+		nla_memcpy(&p->peer_cookie[0], attrs[L2TP_ATTR_PEER_COOKIE], sizeof(p->peer_cookie));
+		p->peer_cookie_len = nla_len(attrs[L2TP_ATTR_PEER_COOKIE]);
+	}
+	if (attrs[L2TP_ATTR_RECV_SEQ])
+		p->recv_seq = nla_get_flag(attrs[L2TP_ATTR_RECV_SEQ]);
+	if (attrs[L2TP_ATTR_SEND_SEQ])
+		p->send_seq = nla_get_flag(attrs[L2TP_ATTR_SEND_SEQ]);
+	if (attrs[L2TP_ATTR_RECV_TIMEOUT])
+		p->reorder_timeout = nla_get_msecs(attrs[L2TP_ATTR_RECV_TIMEOUT]);
+	if (attrs[L2TP_ATTR_IP_SADDR])
+		p->local_ip.s_addr = nla_get_u32(attrs[L2TP_ATTR_IP_SADDR]);
+	if (attrs[L2TP_ATTR_IP_DADDR])
+		p->peer_ip.s_addr = nla_get_u32(attrs[L2TP_ATTR_IP_DADDR]);
+	if (attrs[L2TP_ATTR_UDP_SPORT])
+		p->local_udp_port = nla_get_u16(attrs[L2TP_ATTR_UDP_SPORT]);
+	if (attrs[L2TP_ATTR_UDP_DPORT])
+		p->peer_udp_port = nla_get_u16(attrs[L2TP_ATTR_UDP_DPORT]);
+	if (attrs[L2TP_ATTR_MTU])
+		p->mtu = nla_get_u16(attrs[L2TP_ATTR_MTU]);
+	if (attrs[L2TP_ATTR_IFNAME])
+		p->ifname = nla_get_string(attrs[L2TP_ATTR_IFNAME]);
+
+	nla_stats = attrs[L2TP_ATTR_STATS];
+	if (nla_stats) {
+		struct nlattr *tb[L2TP_ATTR_STATS_MAX + 1];
+
+		result = nla_parse_nested(tb, L2TP_ATTR_STATS_MAX, nla_stats, NULL);
+		if (result < 0)
+			goto out;
+
+		if (tb[L2TP_ATTR_TX_PACKETS])
+			data->stats.data_tx_packets = nla_get_u64(tb[L2TP_ATTR_TX_PACKETS]);
+		if (tb[L2TP_ATTR_TX_BYTES])
+			data->stats.data_tx_bytes = nla_get_u64(tb[L2TP_ATTR_TX_BYTES]);
+		if (tb[L2TP_ATTR_TX_ERRORS])
+			data->stats.data_tx_errors = nla_get_u64(tb[L2TP_ATTR_TX_ERRORS]);
+		if (tb[L2TP_ATTR_RX_PACKETS])
+			data->stats.data_rx_packets = nla_get_u64(tb[L2TP_ATTR_RX_PACKETS]);
+		if (tb[L2TP_ATTR_RX_BYTES])
+			data->stats.data_rx_bytes = nla_get_u64(tb[L2TP_ATTR_RX_BYTES]);
+		if (tb[L2TP_ATTR_RX_ERRORS])
+			data->stats.data_rx_errors = nla_get_u64(tb[L2TP_ATTR_RX_ERRORS]);
+		if (tb[L2TP_ATTR_RX_SEQ_DISCARDS])
+			data->stats.data_rx_oos_discards = nla_get_u64(tb[L2TP_ATTR_RX_SEQ_DISCARDS]);
+		if (tb[L2TP_ATTR_RX_OOS_PACKETS])
+			data->stats.data_rx_oos_packets = nla_get_u64(tb[L2TP_ATTR_RX_OOS_PACKETS]);
+	}
+
+	result = 0;
+
+out:
+	return result;
+}
+
+static int nl_session_get_response(struct nl_msg *msg, void *arg)
+{
+	int ret = nl_get_response(msg, arg);
+
+	if (ret == 0)
+		print_session(arg);
+
+	return ret;
+}
+
+static int nl_tunnel_get_response(struct nl_msg *msg, void *arg)
+{
+	int ret = nl_get_response(msg, arg);
+
+	if (ret == 0)
+		print_tunnel(arg);
+
+	return ret;
+}
+
+static int get_session(struct l2tp_data *p)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb;
+	int result = -EPROTONOSUPPORT;
+	enum nl_cb_kind cb_kind = NL_CB_DEFAULT;
+	int flags = NLM_F_DUMP;
+
+	if (nl_family <= 0) {
+		goto out;
+	}
+
+	cb = nl_cb_alloc(cb_kind);
+	if (!cb) {
+		goto out;
+	}
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl_session_get_response, p);
+
+	msg = nlmsg_alloc();
+
+	if (p->config.tunnel_id && p->config.session_id)
+		flags = NLM_F_REQUEST;
+
+	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl_family, 0, flags,
+		    L2TP_CMD_SESSION_GET, L2TP_GENL_VERSION);
+
+	if (p->config.tunnel_id && p->config.session_id) {
+		nla_put_u32(msg, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
+		nla_put_u32(msg, L2TP_ATTR_SESSION_ID, p->config.session_id);
+	}
+
+	nl_send_auto_complete(nl_sock, msg);
+
+	nlmsg_free(msg);
+
+	result = nl_recvmsgs(nl_sock, cb);
+	if (result > 0) {
+		result = nl_wait_for_ack(nl_sock);
+	}
+
+	if (result > 0) {
+		result = 0;
+	}
+
+	nl_cb_put(cb);
+
+out:
+	return result;
+}
+
+static int get_tunnel(struct l2tp_data *p)
+{
+	struct nl_msg *msg;
+	struct nl_cb *cb;
+	int result = -EPROTONOSUPPORT;
+	enum nl_cb_kind cb_kind = NL_CB_DEFAULT;
+	int flags = NLM_F_DUMP;
+
+	if (nl_family <= 0) {
+		goto out;
+	}
+
+	cb = nl_cb_alloc(cb_kind);
+	if (!cb) {
+		goto out;
+	}
+	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, nl_tunnel_get_response, p);
+
+	msg = nlmsg_alloc();
+
+	if (p->config.tunnel_id)
+		flags = NLM_F_REQUEST;
+
+	genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, nl_family, 0, flags,
+		    L2TP_CMD_TUNNEL_GET, L2TP_GENL_VERSION);
+
+	if (p->config.tunnel_id)
+		nla_put_u32(msg, L2TP_ATTR_CONN_ID, p->config.tunnel_id);
+
+	nl_send_auto_complete(nl_sock, msg);
+
+	nlmsg_free(msg);
+
+	result = nl_recvmsgs(nl_sock, cb);
+	if (result > 0) {
+		result = nl_wait_for_ack(nl_sock);
+	}
+
+	if (result > 0) {
+		result = 0;
+	}
+
+	nl_cb_put(cb);
+
+out:
+	return result;
+}
+
+/*****************************************************************************
+ * Command parser
+ *****************************************************************************/
+
+static int hex(char ch)
+{
+	if ((ch >= 'a') && (ch <= 'f'))
+		return ch - 'a' + 10;
+	if ((ch >= '0') && (ch <= '9'))
+		return ch - '0';
+	if ((ch >= 'A') && (ch <= 'F'))
+		return ch - 'A' + 10;
+	return -1;
+}
+
+static int hex2mem(char *buf, uint8_t *mem, int count)
+{
+	int i, j;
+	int c;
+
+	for (i = 0, j = 0; i < count; i++, j += 2) {
+		c = hex(buf[j]);
+		if (c < 0)
+			goto err;
+
+		mem[i] = c << 4;
+
+		c = hex(buf[j + 1]);
+		if (c < 0)
+			goto err;
+
+		mem[i] |= c;
+	}
+
+	return 0;
+
+err:
+	return -1;
+}
+
+static void usage(void) __attribute__((noreturn));
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: ip l2tp add tunnel\n");
+	fprintf(stderr, "          remote ADDR local ADDR\n");
+	fprintf(stderr, "          tunnel_id ID peer_tunnel_id ID\n");
+	fprintf(stderr, "          [ encap { ip | udp } ]\n");
+	fprintf(stderr, "          [ udp_sport PORT ] [ udp_dport PORT ]\n");
+	fprintf(stderr, "Usage: ip l2tp add session\n");
+	fprintf(stderr, "          tunnel_id ID\n");
+	fprintf(stderr, "          session_id ID peer_session_id ID\n");
+	fprintf(stderr, "          [ cookie HEXSTR ] [ peer_cookie HEXSTR ]\n");
+	fprintf(stderr, "          [ offset OFFSET ] [ peer_offset OFFSET ]\n");
+	fprintf(stderr, "       ip l2tp del tunnel tunnel_id ID\n");
+	fprintf(stderr, "       ip l2tp del session tunnel_id ID session_id ID\n");
+	fprintf(stderr, "       ip l2tp show tunnel [ tunnel_id ID ]\n");
+	fprintf(stderr, "       ip l2tp show session [ tunnel_id ID ] [ session_id ID ]\n");
+	fprintf(stderr, "\n");
+	fprintf(stderr, "Where: NAME   := STRING\n");
+	fprintf(stderr, "       ADDR   := { IP_ADDRESS | any }\n");
+	fprintf(stderr, "       PORT   := { 0..65535 }\n");
+	fprintf(stderr, "       ID     := { 1..4294967295 }\n");
+	fprintf(stderr, "       HEXSTR := { 8 or 16 hex digits (4 / 8 bytes) }\n");
+	exit(-1);
+}
+
+static int parse_args(int argc, char **argv, int cmd, struct l2tp_parm *p)
+{
+	memset(p, 0, sizeof(*p));
+
+	if (argc == 0)
+		usage();
+
+	while (argc > 0) {
+		if (strcmp(*argv, "encap") == 0) {
+			NEXT_ARG();
+			if (strcmp(*argv, "ip") == 0) {
+				p->encap = L2TP_ENCAPTYPE_IP;
+			} else if (strcmp(*argv, "udp") == 0) {
+				p->encap = L2TP_ENCAPTYPE_UDP;
+			} else {
+				fprintf(stderr, "Unknown tunnel encapsulation.\n");
+				exit(-1);
+			}
+		} else if (strcmp(*argv, "remote") == 0) {
+			NEXT_ARG();
+			p->peer_ip.s_addr = get_addr32(*argv);
+		} else if (strcmp(*argv, "local") == 0) {
+			NEXT_ARG();
+			p->local_ip.s_addr = get_addr32(*argv);
+		} else if ((strcmp(*argv, "tunnel_id") == 0) ||
+			   (strcmp(*argv, "tid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->tunnel_id = uval;
+		} else if ((strcmp(*argv, "peer_tunnel_id") == 0) ||
+			   (strcmp(*argv, "ptid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->peer_tunnel_id = uval;
+		} else if ((strcmp(*argv, "session_id") == 0) ||
+			   (strcmp(*argv, "sid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->session_id = uval;
+		} else if ((strcmp(*argv, "peer_session_id") == 0) ||
+			   (strcmp(*argv, "psid") == 0)) {
+			__u32 uval;
+			NEXT_ARG();
+			if (get_u32(&uval, *argv, 0))
+				invarg("invalid ID\n", *argv);
+			p->peer_session_id = uval;
+		} else if (strcmp(*argv, "udp_sport") == 0) {
+			__u16 uval;
+			NEXT_ARG();
+			if (get_u16(&uval, *argv, 0))
+				invarg("invalid port\n", *argv);
+			p->local_udp_port = uval;
+		} else if (strcmp(*argv, "udp_dport") == 0) {
+			__u16 uval;
+			NEXT_ARG();
+			if (get_u16(&uval, *argv, 0))
+				invarg("invalid port\n", *argv);
+			p->peer_udp_port = uval;
+		} else if (strcmp(*argv, "offset") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid offset\n", *argv);
+			p->offset = uval;
+		} else if (strcmp(*argv, "peer_offset") == 0) {
+			__u8 uval;
+			NEXT_ARG();
+			if (get_u8(&uval, *argv, 0))
+				invarg("invalid offset\n", *argv);
+			p->peer_offset = uval;
+		} else if (strcmp(*argv, "cookie") == 0) {
+			int slen;
+			NEXT_ARG();
+			slen = strlen(*argv);
+			if ((slen != 8) && (slen != 16))
+				invarg("cookie must be either 8 or 16 hex digits\n", *argv);
+
+			p->cookie_len = slen / 2;
+			if (hex2mem(*argv, p->cookie, p->cookie_len) < 0)
+				invarg("cookie must be a hex string\n", *argv);
+		} else if (strcmp(*argv, "peer_cookie") == 0) {
+			int slen;
+			NEXT_ARG();
+			slen = strlen(*argv);
+			if ((slen != 8) && (slen != 16))
+				invarg("cookie must be either 8 or 16 hex digits\n", *argv);
+
+			p->peer_cookie_len = slen / 2;
+			if (hex2mem(*argv, p->peer_cookie, p->peer_cookie_len) < 0)
+				invarg("cookie must be a hex string\n", *argv);
+		} else if (strcmp(*argv, "tunnel") == 0) {
+			p->tunnel = 1;
+		} else if (strcmp(*argv, "session") == 0) {
+			p->session = 1;
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+		} else {
+			fprintf(stderr, "Unknown command: %s\n", *argv);
+			usage();
+		}
+
+		argc--; argv++;
+	}
+
+	return 0;
+}
+
+
+static int do_add(int argc, char **argv)
+{
+	struct l2tp_parm p;
+	int ret = 0;
+
+	if (parse_args(argc, argv, L2TP_ADD, &p) < 0)
+		return -1;
+
+	if (!p.tunnel && !p.session)
+		missarg("tunnel or session");
+
+	if (p.tunnel_id == 0)
+		missarg("tunnel_id");
+
+	/* session_id and peer_session_id must be provided for sessions */
+	if ((p.session) && (p.peer_session_id == 0))
+		missarg("peer_session_id");
+	if ((p.session) && (p.session_id == 0))
+		missarg("session_id");
+
+	/* peer_tunnel_id is needed for tunnels */
+	if ((p.tunnel) && (p.peer_tunnel_id == 0))
+		missarg("peer_tunnel_id");
+
+	if (p.tunnel) {
+		if (p.local_ip.s_addr == 0)
+			missarg("local");
+
+		if (p.peer_ip.s_addr == 0)
+			missarg("remote");
+
+		if (p.encap == L2TP_ENCAPTYPE_UDP) {
+			if (p.local_udp_port == 0)
+				missarg("udp_sport");
+			if (p.peer_udp_port == 0)
+				missarg("udp_dport");
+		}
+
+		ret = create_tunnel(&p);
+	}
+
+	if (p.session) {
+		/* Only ethernet pseudowires supported */
+		p.pw_type = L2TP_PWTYPE_ETH;
+
+		ret = create_session(&p);
+	}
+
+	return ret;
+}
+
+static int do_del(int argc, char **argv)
+{
+	struct l2tp_parm p;
+
+	if (parse_args(argc, argv, L2TP_DEL, &p) < 0)
+		return -1;
+
+	if (!p.tunnel && !p.session)
+		missarg("tunnel or session");
+
+	if ((p.tunnel) && (p.tunnel_id == 0))
+		missarg("tunnel_id");
+	if ((p.session) && (p.session_id == 0))
+		missarg("session_id");
+
+	if (p.session_id)
+		return delete_session(&p);
+	else
+		return delete_tunnel(&p);
+
+	return -1;
+}
+
+static int do_show(int argc, char **argv)
+{
+	struct l2tp_data data;
+	struct l2tp_parm *p = &data.config;
+
+	if (parse_args(argc, argv, L2TP_GET, p) < 0)
+		return -1;
+
+	if (!p->tunnel && !p->session)
+		missarg("tunnel or session");
+
+	if (p->session)
+		get_session(&data);
+	else
+		get_tunnel(&data);
+
+	return 0;
+}
+
+int do_ipl2tp(int argc, char **argv)
+{
+	nl_sock = nl_handle_alloc();
+	if (!nl_sock) {
+		perror("nl_handle_alloc");
+		return 1;
+	}
+
+	if (nl_connect(nl_sock, NETLINK_GENERIC) < 0) {
+		perror("nl_connect");
+		return 1;
+	}
+
+	nl_family = genl_ctrl_resolve(nl_sock, L2TP_GENL_NAME);
+	if (nl_family < 0) {
+		fprintf(stderr, "L2TP netlink support unavailable.\n");
+		return 1;
+	}
+
+	if (argc > 0) {
+		if (matches(*argv, "add") == 0)
+			return do_add(argc-1, argv+1);
+		if (matches(*argv, "del") == 0)
+			return do_del(argc-1, argv+1);
+		if (matches(*argv, "show") == 0 ||
+		    matches(*argv, "lst") == 0 ||
+		    matches(*argv, "list") == 0)
+			return do_show(argc-1, argv+1);
+		if (matches(*argv, "help") == 0)
+			usage();
+
+		fprintf(stderr, "Command \"%s\" is unknown, try \"ip l2tp help\".\n", *argv);
+		exit(-1);
+	}
+
+	usage();
+}


^ permalink raw reply related

* [RFC PATCH 1/2] iproute2: Add libnl support.
From: James Chapman @ 2010-03-18 18:14 UTC (permalink / raw)
  To: shemminger; +Cc: netdev
In-Reply-To: <20100318181416.6862.60894.stgit@bert.katalix.com>

This patch links the ip utility with libnl, which must be separately
installed. This lets new functionality use libnl to implement its
netlink interfaces.

Package developers will need to add libnl to the iproute2 package
dependencies.
---
 Makefile |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/Makefile b/Makefile
index 77a85c6..c47435d 100644
--- a/Makefile
+++ b/Makefile
@@ -33,7 +33,7 @@ CCOPTS = -D_GNU_SOURCE -O2 -Wstrict-prototypes -Wall
 CFLAGS = $(CCOPTS) -I../include $(DEFINES)
 YACCFLAGS = -d -t -v
 
-LDLIBS += -L../lib -lnetlink -lutil
+LDLIBS += -L../lib -lnetlink -lutil -lnl
 
 SUBDIRS=lib ip tc misc netem genl
 


^ permalink raw reply related

* [RFC PATCH 0/2] iproute2: Introduce new commands for L2TPv3 unmanaged tunnels
From: James Chapman @ 2010-03-18 18:14 UTC (permalink / raw)
  To: shemminger; +Cc: netdev

Add new iproute2 commands for L2TPv3 unmanaged tunnels. It requires
L2TPv3 kernel support, patches for which have been submitted to netdev
for review. These iproute2 changes should not be pulled into iproute2
until the kernel support is accepted. I am posting the iproute2 code
now to let people play with L2TPv3 unmanaged tunnels.

To create an L2TPv3 ethernet pseudowire between local host 192.168.1.1
and peer 192.168.1.2, using IP addresses 10.5.1.1 and 10.5.1.2 for the
tunnel endpoints:-

# modprobe l2tp_eth
# modprobe l2tp_netlink

# ip l2tp add tunnel tunnel_id 1 peer_tunnel_id 1 udp_sport 5000 \
  udp_dport 5000 encap udp local 192.168.1.1 remote 192.168.1.2
# ip l2tp add session tunnel_id 1 session_id 1 peer_session_id 1
# ifconfig -a
# ip addr add 10.5.1.2/32 peer 10.5.1.1/32 dev l2tpeth0
# ifconfig l2tpeth0 up

Choose IP addresses to be the address of a local IP interface and that
of the remote system. The IP addresses of the l2tpeth0 interface can be
anything suitable.

Repeat the above at the peer, with ports, tunnel/session ids and IP
addresses reversed.  The tunnel and session IDs can be any non-zero
32-bit number, but the values must be reversed at the peer.

Host 1                         Host2
udp_sport=5000                 udp_sport=5001
udp_dport=5001                 udp_dport=5000
tunnel_id=42                   tunnel_id=45
peer_tunnel_id=45              peer_tunnel_id=42
session_id=128                 session_id=5196755
peer_session_id=5196755        peer_session_id=128

When done at both ends of the tunnel, it should be possible to send
data over the tunnel. e.g.

# ping 10.5.1.1

TODO:-

- Add more config params to control offset and sequence number options.
- Update man page to cover the new ip l2tp command set.

---

James Chapman (2):
      iproute2: Add libnl support.
      iproute2: Add support for static L2TPv3 tunnels.



 Makefile             |    2 
 include/linux/l2tp.h |  164 ++++++++++
 ip/Makefile          |    2 
 ip/ip.c              |    3 
 ip/ip_common.h       |    1 
 ip/ipl2tp.c          |  815 ++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 984 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/l2tp.h
 create mode 100644 ip/ipl2tp.c




^ permalink raw reply

* Re: [PATCH 1/3] netlink: fix NETLINK_RECV_NO_ENOBUFS in netlink_set_err()
From: Pablo Neira Ayuso @ 2010-03-18 17:01 UTC (permalink / raw)
  To: Patrick McHardy; +Cc: netdev, davem
In-Reply-To: <4BA258F6.1050909@trash.net>

Patrick McHardy wrote:
> Pablo Neira Ayuso wrote:
>> Patrick McHardy wrote:
>>> Pablo Neira Ayuso wrote:
>>>> Yes, allocation is a different situation but we still report ENOBUFS to
>>>> user-space. I think that NETLINK_RECV_NO_ENOBUFS is there to a) disable
>>>> ENOBUFS reports to user-space and b) disable Netlink congestion.
>>>>
>>>>> Is there any problem with these errors?
>>>> Specifically in ctnetlink, if we fail to allocate a message in ctnetlink
>>>> and NETLINK_RECV_NO_ENOBUFS is set, we still lose an event and that
>>>> should not happen.
>>> I assume you mean "not set"? Otherwise I fail to follow :)
>> OK, I'll try again :-)
>>
>> Currently, no matter if NETLINK_RECV_NO_ENOBUFS is set or not: if we
>> fail to allocate the netlink message, then ctnetlink_conntrack_event()
>> returns 0. Thus, we report ENOBUFS to user-space and we lose the event.
>>
>> With my patches, if NETLINK_RECV_NO_ENOBUFS is set and we fail to
>> allocate the message, we don't report ENOBUFS and we don't lose the event.
> 
> That last part is what keeps confusing me. With your patch, if the
> ENOBUFS options is set, we don't report the error to userspace
> and therefore don't return it to conntrack, thus we *do* loose the
> event. Which is correct however.

Sorry, I'm being a bit imprecise myself: we do lose the event anyway.
However, with my patch, if the NO_ENOBUFS option is set, we keep the
event in the ctevent cache, so we can try to deliver it again with the
next packet (this is what I initially meant with "we don't lose the
event", yes, confusing...).

> Did I get it right this time? :)

I think so! :-)

^ permalink raw reply

* Re: [PATCH] tcp: Generalized TTL Security Mechanism
From: Stephen Hemminger @ 2010-03-18 17:59 UTC (permalink / raw)
  To: Pekka Savola; +Cc: David Miller, netdev
In-Reply-To: <alpine.LRH.2.00.1003180833420.24946@netcore.fi>

On Thu, 18 Mar 2010 08:36:48 +0200 (EET)
Pekka Savola <pekkas@netcore.fi> wrote:

> Hi,
> 
> On Sun, 10 Jan 2010, Stephen Hemminger wrote:
> > This patch adds the kernel portions needed to implement
> > RFC 5082 Generalized TTL Security Mechanism (GTSM).
> > It is a lightweight security measure against forged
> > packets causing DoS attacks (for BGP).
> ...
> 
> It's nice to see this added.  However, I must add that a compliant RFC 
> 5082 implementation is required to have similar TTL treatment for ICMP 
> errors which relate to the protected session.  AFAIK this does not 
> support that.
> 
> The experimental, earlier spec (GTSH, RFC3682) did not have this 
> requirement.  Most if not all implementations support only GTSH mode. 
> So a backward-compatibility option may be desirable.

The ICMP receive error handling does need to be updated. 

But any application using GTSM should be setting IP_TTL socket option
to set send TTL. But, not sure if Linux TCP ever sends ICMP 
for existing sessions at all.

^ 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