* 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
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox