All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6
@ 2012-02-14 22:31 Benjamin LaHaise
  2012-02-15  5:06 ` Eric Dumazet
  0 siblings, 1 reply; 8+ messages in thread
From: Benjamin LaHaise @ 2012-02-14 22:31 UTC (permalink / raw)
  To: James Chapman, netdev

Hello folks,

Below is a patch that adds support for L2TPv2 over UDP over IPv6.  It's an 
RFC at present due to 2 outstanding issues: the first is that struct 
pppol2tp_addr and pppol2tpv3_addr have an embedded struct sockaddr_in, and 
these changes have not implemented a replacement of those socket addresses.  
Since the existing pppol2tp{,v3}_addr structures did not place the sockaddr_in 
at the end of the structure, IPv6 addresses cannot be embedded without 
changing the ABI.  The question becomes one of how: just add a version of the 
pppol2tp_addr structure with a sockaddr_in6 embedded in it (easily done, as 
the different in address structures can be detected via sin_family in the 
embedded address), or replace it wholesale with a new pppol2tp_addr that has 
the embedded address at the end of the structure?  That is:

	struct pppol2tp_addr_in6 {
		int			pid;
		int			fd;
		struct sockaddr_in6	addr;
		u16			s_tunnel, s_session;
		u16			d_tunnel, d_session;
	};

or

	struct new_pppol2tp_addr {
		int			pid;
		int			fd;
		u32			s_tunnel, s_session;
		u32			d_tunnel, d_session;
		struct sockaddr_in6	addr;
	};

Thoughts?

The second issue still outstanding is that I still have to test the transmit 
path with IPv6 hardware checksumming.  I've tested with virtio_net and 
pcnet32 with qemu, but would like to verify on real hardware.

This patch works for me as-is by passing in a connected IPv6 socket in the 
fd entry.  If anyone has any comments on the approach to fixing the ABI 
issues, I'll do a fresh spin incorporating whatever approach people are 
comfortable with.  As it stands, I'm leaning towards the first approach 
and keeping the existing socket addresses for IPv4 in order to minimise 
ABI distruption.  Cheers,

		-ben
---
 ipv6/udp.c       |   31 ++++++++++++++++++++++++++++
 l2tp/l2tp_core.c |   59 +++++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 84 insertions(+), 6 deletions(-)
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 8c25419..c0faad5 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -515,6 +515,37 @@ int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
 		goto drop;
 
+	if (up->encap_type) {
+		int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
+
+		/*
+		 * This is an encapsulation socket so pass the skb to
+		 * the socket's udp_encap_rcv() hook. Otherwise, just
+		 * fall through and pass this up the UDP socket.
+		 * up->encap_rcv() returns the following value:
+		 * =0 if skb was successfully passed to the encap
+		 *    handler or was discarded by it.
+		 * >0 if skb should be passed on to UDP.
+		 * <0 if skb should be resubmitted as proto -N
+		 */
+
+		/* if we're overly short, let UDP handle it */
+		encap_rcv = ACCESS_ONCE(up->encap_rcv);
+		if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {
+			int ret;
+
+			ret = encap_rcv(sk, skb);
+			if (ret <= 0) {
+				UDP6_INC_STATS_BH(sock_net(sk),
+						  UDP_MIB_INDATAGRAMS,
+						  is_udplite);
+				return -ret;
+			}
+		}
+
+		/* FALLTHROUGH -- it's a UDP Packet */
+	}
+
 	/*
 	 * UDP-Lite specific tests, ignored on UDP sockets (see net/ipv4/udp.c).
 	 */
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 89ff8c6..7473ae8 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -53,6 +53,7 @@
 #include <net/inet_common.h>
 #include <net/xfrm.h>
 #include <net/protocol.h>
+#include <net/inet6_connection_sock.h>
 
 #include <asm/byteorder.h>
 #include <linux/atomic.h>
@@ -446,15 +447,26 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
 {
 	struct udphdr *uh = udp_hdr(skb);
 	u16 ulen = ntohs(uh->len);
-	struct inet_sock *inet;
 	__wsum psum;
 
-	if (sk->sk_no_check || skb_csum_unnecessary(skb) || !uh->check)
+	if (sk->sk_no_check || skb_csum_unnecessary(skb))
 		return 0;
 
-	inet = inet_sk(sk);
-	psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, ulen,
-				  IPPROTO_UDP, 0);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	if (sk->sk_family == PF_INET6) {
+		psum = csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+				       &ipv6_hdr(skb)->daddr, ulen,
+				       IPPROTO_UDP, 0);
+	} else
+#endif
+	{
+		struct inet_sock *inet;
+		if (!uh->check)
+			return 0;
+		inet = inet_sk(sk);
+		psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr,
+					  ulen, IPPROTO_UDP, 0);
+	}
 
 	if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
 	    !csum_fold(csum_add(psum, skb->csum)))
@@ -988,7 +1000,12 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
 
 	/* Queue the packet to IP for output */
 	skb->local_df = 1;
-	error = ip_queue_xmit(skb, fl);
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+	if (skb->sk->sk_family == PF_INET6)
+		error = inet6_csk_xmit(skb, NULL);
+	else
+#endif
+		error = ip_queue_xmit(skb, fl);
 
 	/* Update stats */
 	if (error >= 0) {
@@ -1021,6 +1038,31 @@ static inline void l2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
 	skb->destructor = l2tp_sock_wfree;
 }
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb, int udp_len)
+{
+	struct ipv6_pinfo *np = inet6_sk(sk);
+	struct udphdr *uh = udp_hdr(skb);
+
+	if (skb_dst(skb) && skb_dst(skb)->dev &&
+	    (!(skb_dst(skb)->dev->features & NETIF_F_V6_CSUM))) {
+		skb->ip_summed = CHECKSUM_COMPLETE;
+		skb->csum = skb_checksum(skb, 0, udp_len, 0);
+		uh->check = csum_ipv6_magic(&np->saddr, &np->daddr, udp_len,
+					    IPPROTO_UDP, skb->csum);
+		if (uh->check == 0)
+			uh->check = CSUM_MANGLED_0;
+	} else {
+		skb->ip_summed = CHECKSUM_PARTIAL;
+		skb->csum_start = skb_transport_header(skb) - skb->head;
+		skb->csum_offset = offsetof(struct udphdr, check);
+		uh->check = csum_ipv6_magic(&np->saddr, &np->daddr,
+					    udp_len, IPPROTO_UDP, 0);
+	}
+
+}
+#endif
+
 /* If caller requires the skb to have a ppp header, the header must be
  * inserted in the skb data before calling this function.
  */
@@ -1089,6 +1131,11 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
 		uh->check = 0;
 
 		/* Calculate UDP checksum if configured to do so */
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+		if (sk->sk_family == PF_INET6)
+			l2tp_xmit_ipv6_csum(sk, skb, udp_len);
+		else
+#endif
 		if (sk->sk_no_check == UDP_CSUM_NOXMIT)
 			skb->ip_summed = CHECKSUM_NONE;
 		else if ((skb_dst(skb) && skb_dst(skb)->dev) &&

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

* Re: [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6
  2012-02-14 22:31 [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6 Benjamin LaHaise
@ 2012-02-15  5:06 ` Eric Dumazet
  2012-02-15  8:26   ` James Chapman
  0 siblings, 1 reply; 8+ messages in thread
From: Eric Dumazet @ 2012-02-15  5:06 UTC (permalink / raw)
  To: Benjamin LaHaise; +Cc: James Chapman, netdev

Le mardi 14 février 2012 à 17:31 -0500, Benjamin LaHaise a écrit :
> Hello folks,
> 
> Below is a patch that adds support for L2TPv2 over UDP over IPv6.  It's an 
> RFC at present due to 2 outstanding issues: the first is that struct 
> pppol2tp_addr and pppol2tpv3_addr have an embedded struct sockaddr_in, and 
> these changes have not implemented a replacement of those socket addresses.  
> Since the existing pppol2tp{,v3}_addr structures did not place the sockaddr_in 
> at the end of the structure, IPv6 addresses cannot be embedded without 
> changing the ABI.  The question becomes one of how: just add a version of the 
> pppol2tp_addr structure with a sockaddr_in6 embedded in it (easily done, as 
> the different in address structures can be detected via sin_family in the 
> embedded address), or replace it wholesale with a new pppol2tp_addr that has 
> the embedded address at the end of the structure?  That is:
> 
> 	struct pppol2tp_addr_in6 {
> 		int			pid;
> 		int			fd;
> 		struct sockaddr_in6	addr;
> 		u16			s_tunnel, s_session;
> 		u16			d_tunnel, d_session;
> 	};
> 
> or
> 
> 	struct new_pppol2tp_addr {
> 		int			pid;
> 		int			fd;
> 		u32			s_tunnel, s_session;
> 		u32			d_tunnel, d_session;
> 		struct sockaddr_in6	addr;
> 	};
> 
My personal choice would be the latter .



> +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
> +	if (sk->sk_family == PF_INET6) {
> +		psum = csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
> +				       &ipv6_hdr(skb)->daddr, ulen,
> +				       IPPROTO_UDP, 0);
> +	} else
> +#endif
> +	{


Please use the modern :

#if IS_ENABLED(CONFIG_IPV6)

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

* Re: [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6
  2012-02-15  5:06 ` Eric Dumazet
@ 2012-02-15  8:26   ` James Chapman
  2012-03-16 12:19     ` James Chapman
  0 siblings, 1 reply; 8+ messages in thread
From: James Chapman @ 2012-02-15  8:26 UTC (permalink / raw)
  To: Eric Dumazet, Benjamin LaHaise; +Cc: netdev

This is very timely - I'm keen to see IPv6 support added for L2TPv2 and 
L2TPv3, but we can work on L2TPv2 first.

On 15/02/12 05:06, Eric Dumazet wrote:
> Le mardi 14 février 2012 à 17:31 -0500, Benjamin LaHaise a écrit :
>> Hello folks,
>>
>> Below is a patch that adds support for L2TPv2 over UDP over IPv6.  It's an
>> RFC at present due to 2 outstanding issues: the first is that struct
>> pppol2tp_addr and pppol2tpv3_addr have an embedded struct sockaddr_in, and
>> these changes have not implemented a replacement of those socket addresses.
>> Since the existing pppol2tp{,v3}_addr structures did not place the sockaddr_in
>> at the end of the structure, IPv6 addresses cannot be embedded without
>> changing the ABI.  The question becomes one of how: just add a version of the
>> pppol2tp_addr structure with a sockaddr_in6 embedded in it (easily done, as
>> the different in address structures can be detected via sin_family in the
>> embedded address), or replace it wholesale with a new pppol2tp_addr that has
>> the embedded address at the end of the structure?  That is:
>>
>> 	struct pppol2tp_addr_in6 {
>> 		int			pid;
>> 		int			fd;
>> 		struct sockaddr_in6	addr;
>> 		u16			s_tunnel, s_session;
>> 		u16			d_tunnel, d_session;
>> 	};
>>
>> or
>>
>> 	struct new_pppol2tp_addr {
>> 		int			pid;
>> 		int			fd;
>> 		u32			s_tunnel, s_session;
>> 		u32			d_tunnel, d_session;
>> 		struct sockaddr_in6	addr;
>> 	};
>>
> My personal choice would be the latter .

Certainly put the sockaddr struct at the end in the new ipv6 struct, 
like new_pppol2tp_addr, but we need to retain the ABI. Code already 
handles two different pppol2tp_addr structs for L2TPv2 and L2TPv3, so it 
should be possible to add two more for IPv6 compatibility and have the 
code detect which form is used similar to that in pppol2tp_connect(). My 
preference for the struct naming is pppol2tp_addr_{in6,new}.

> The second issue still outstanding is that I still have to test the transmit
> path with IPv6 hardware checksumming.  I've tested with virtio_net and
> pcnet32 with qemu, but would like to verify on real hardware.

I'll see if we have suitable hardware here. If I can find some, could 
you share your test code?


-- 
James Chapman
Katalix Systems Ltd
http://www.katalix.com
Catalysts for your Embedded Linux software development

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

* Re: [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6
  2012-02-15  8:26   ` James Chapman
@ 2012-03-16 12:19     ` James Chapman
  2012-03-19  3:28       ` [v2] " Benjamin LaHaise
  0 siblings, 1 reply; 8+ messages in thread
From: James Chapman @ 2012-03-16 12:19 UTC (permalink / raw)
  To: Benjamin LaHaise; +Cc: Eric Dumazet, netdev

Hi Ben,

Do you have an updated patch to test?

On 15/02/12 08:26, James Chapman wrote:
> This is very timely - I'm keen to see IPv6 support added for L2TPv2 and
> L2TPv3, but we can work on L2TPv2 first.
> 
> On 15/02/12 05:06, Eric Dumazet wrote:
>> Le mardi 14 février 2012 à 17:31 -0500, Benjamin LaHaise a écrit :
>>> Hello folks,
>>>
>>> Below is a patch that adds support for L2TPv2 over UDP over IPv6. 
>>> It's an
>>> RFC at present due to 2 outstanding issues: the first is that struct
>>> pppol2tp_addr and pppol2tpv3_addr have an embedded struct
>>> sockaddr_in, and
>>> these changes have not implemented a replacement of those socket
>>> addresses.
>>> Since the existing pppol2tp{,v3}_addr structures did not place the
>>> sockaddr_in
>>> at the end of the structure, IPv6 addresses cannot be embedded without
>>> changing the ABI.  The question becomes one of how: just add a
>>> version of the
>>> pppol2tp_addr structure with a sockaddr_in6 embedded in it (easily
>>> done, as
>>> the different in address structures can be detected via sin_family in
>>> the
>>> embedded address), or replace it wholesale with a new pppol2tp_addr
>>> that has
>>> the embedded address at the end of the structure?  That is:
>>>
>>>     struct pppol2tp_addr_in6 {
>>>         int            pid;
>>>         int            fd;
>>>         struct sockaddr_in6    addr;
>>>         u16            s_tunnel, s_session;
>>>         u16            d_tunnel, d_session;
>>>     };
>>>
>>> or
>>>
>>>     struct new_pppol2tp_addr {
>>>         int            pid;
>>>         int            fd;
>>>         u32            s_tunnel, s_session;
>>>         u32            d_tunnel, d_session;
>>>         struct sockaddr_in6    addr;
>>>     };
>>>
>> My personal choice would be the latter .
> 
> Certainly put the sockaddr struct at the end in the new ipv6 struct,
> like new_pppol2tp_addr, but we need to retain the ABI. Code already
> handles two different pppol2tp_addr structs for L2TPv2 and L2TPv3, so it
> should be possible to add two more for IPv6 compatibility and have the
> code detect which form is used similar to that in pppol2tp_connect(). My
> preference for the struct naming is pppol2tp_addr_{in6,new}.
> 
>> The second issue still outstanding is that I still have to test the
>> transmit
>> path with IPv6 hardware checksumming.  I've tested with virtio_net and
>> pcnet32 with qemu, but would like to verify on real hardware.
> 
> I'll see if we have suitable hardware here. If I can find some, could
> you share your test code?
> 
> 

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

* [v2] Re: [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6
  2012-03-16 12:19     ` James Chapman
@ 2012-03-19  3:28       ` Benjamin LaHaise
  2012-04-10 10:47         ` James Chapman
  0 siblings, 1 reply; 8+ messages in thread
From: Benjamin LaHaise @ 2012-03-19  3:28 UTC (permalink / raw)
  To: James Chapman; +Cc: Eric Dumazet, netdev

On Fri, Mar 16, 2012 at 12:19:02PM +0000, James Chapman wrote:
> Hi Ben,
> 
> Do you have an updated patch to test?

Yes, here's an updated version.  There is still work to do, as I've not 
added IPv6 support to L2TPv3 pseudo-wires yet.  I have managed to test 
this with a couple of VMs as well as on IPv6 checksumming capable hardware, 
and the checksum offloading code works for me.  I'm not sure if the dst 
handling is 100% correct, so close review of this area would be greatly 
appreciated.

A simple test program for wiring pppd up to an L2TP socket that I've used 
for testing is available at http://www.kvack.org/~bcrl/pppol2tp/l2tp_test.c 
(just pass --local=<ip> --remote=<ip> /usr/sbin/pppd nodetach ... to get it 
to run a connection) .  I'm planning on adding support to the Babylon stack 
to use the native Linux L2TP stack, but I just haven't had much time to 
work on this during the last few weeks.  Comments on this version?

		-ben

 include/linux/if_pppol2tp.h |   28 ++++++++++++++
 include/linux/if_pppox.h    |   12 ++++++
 net/ipv6/udp.c              |   31 ++++++++++++++++
 net/l2tp/l2tp_core.c        |   83 +++++++++++++++++++++++++++++++++++++-------
 net/l2tp/l2tp_ppp.c         |   44 ++++++++++++++++++++++-
 5 files changed, 183 insertions(+), 15 deletions(-)
diff --git a/include/linux/if_pppol2tp.h b/include/linux/if_pppol2tp.h
index 23cefa1..b477541 100644
--- a/include/linux/if_pppol2tp.h
+++ b/include/linux/if_pppol2tp.h
@@ -19,10 +19,11 @@
 
 #ifdef __KERNEL__
 #include <linux/in.h>
+#include <linux/in6.h>
 #endif
 
 /* Structure used to connect() the socket to a particular tunnel UDP
- * socket.
+ * socket over IPv4.
  */
 struct pppol2tp_addr {
 	__kernel_pid_t	pid;		/* pid that owns the fd.
@@ -35,6 +36,20 @@ struct pppol2tp_addr {
 	__u16 d_tunnel, d_session;	/* For sending outgoing packets */
 };
 
+/* Structure used to connect() the socket to a particular tunnel UDP
+ * socket over IPv6.
+ */
+struct pppol2tpin6_addr {
+	__kernel_pid_t	pid;		/* pid that owns the fd.
+					 * 0 => current */
+	int	fd;			/* FD of UDP socket to use */
+
+	__u16 s_tunnel, s_session;	/* For matching incoming packets */
+	__u16 d_tunnel, d_session;	/* For sending outgoing packets */
+
+	struct sockaddr_in6 addr;	/* IP address and port to send to */
+};
+
 /* The L2TPv3 protocol changes tunnel and session ids from 16 to 32
  * bits. So we need a different sockaddr structure.
  */
@@ -49,6 +64,17 @@ struct pppol2tpv3_addr {
 	__u32 d_tunnel, d_session;	/* For sending outgoing packets */
 };
 
+struct pppol2tpv3in6_addr {
+	__kernel_pid_t	pid;		/* pid that owns the fd.
+					 * 0 => current */
+	int	fd;			/* FD of UDP or IP socket to use */
+
+	__u32 s_tunnel, s_session;	/* For matching incoming packets */
+	__u32 d_tunnel, d_session;	/* For sending outgoing packets */
+
+	struct sockaddr_in6 addr;	/* IP address and port to send to */
+};
+
 /* Socket options:
  * DEBUG	- bitmask of debug message categories
  * SENDSEQ	- 0 => don't send packets with sequence numbers
diff --git a/include/linux/if_pppox.h b/include/linux/if_pppox.h
index b5f927f..6720d57 100644
--- a/include/linux/if_pppox.h
+++ b/include/linux/if_pppox.h
@@ -83,6 +83,12 @@ struct sockaddr_pppol2tp {
 	struct pppol2tp_addr pppol2tp;
 } __attribute__((packed));
 
+struct sockaddr_pppol2tpin6 {
+	__kernel_sa_family_t sa_family; /* address family, AF_PPPOX */
+	unsigned int    sa_protocol;    /* protocol identifier */
+	struct pppol2tpin6_addr pppol2tp;
+} __attribute__((packed));
+
 /* The L2TPv3 protocol changes tunnel and session ids from 16 to 32
  * bits. So we need a different sockaddr structure.
  */
@@ -92,6 +98,12 @@ struct sockaddr_pppol2tpv3 {
 	struct pppol2tpv3_addr pppol2tp;
 } __attribute__((packed));
 
+struct sockaddr_pppol2tpv3in6 {
+	__kernel_sa_family_t sa_family; /* address family, AF_PPPOX */
+	unsigned int    sa_protocol;    /* protocol identifier */
+	struct pppol2tpv3in6_addr pppol2tp;
+} __attribute__((packed));
+
 /*********************************************************************
  *
  * ioctl interface for defining forwarding of connections
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
index 8c25419..c0faad5 100644
--- a/net/ipv6/udp.c
+++ b/net/ipv6/udp.c
@@ -515,6 +515,37 @@ int udpv6_queue_rcv_skb(struct sock * sk, struct sk_buff *skb)
 	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
 		goto drop;
 
+	if (up->encap_type) {
+		int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
+
+		/*
+		 * This is an encapsulation socket so pass the skb to
+		 * the socket's udp_encap_rcv() hook. Otherwise, just
+		 * fall through and pass this up the UDP socket.
+		 * up->encap_rcv() returns the following value:
+		 * =0 if skb was successfully passed to the encap
+		 *    handler or was discarded by it.
+		 * >0 if skb should be passed on to UDP.
+		 * <0 if skb should be resubmitted as proto -N
+		 */
+
+		/* if we're overly short, let UDP handle it */
+		encap_rcv = ACCESS_ONCE(up->encap_rcv);
+		if (skb->len > sizeof(struct udphdr) && encap_rcv != NULL) {
+			int ret;
+
+			ret = encap_rcv(sk, skb);
+			if (ret <= 0) {
+				UDP6_INC_STATS_BH(sock_net(sk),
+						  UDP_MIB_INDATAGRAMS,
+						  is_udplite);
+				return -ret;
+			}
+		}
+
+		/* FALLTHROUGH -- it's a UDP Packet */
+	}
+
 	/*
 	 * UDP-Lite specific tests, ignored on UDP sockets (see net/ipv4/udp.c).
 	 */
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 89ff8c6..b45f5b4 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -53,6 +53,9 @@
 #include <net/inet_common.h>
 #include <net/xfrm.h>
 #include <net/protocol.h>
+#include <net/inet6_connection_sock.h>
+#include <net/inet_ecn.h>
+#include <net/ip6_route.h>
 
 #include <asm/byteorder.h>
 #include <linux/atomic.h>
@@ -446,21 +449,43 @@ static inline int l2tp_verify_udp_checksum(struct sock *sk,
 {
 	struct udphdr *uh = udp_hdr(skb);
 	u16 ulen = ntohs(uh->len);
-	struct inet_sock *inet;
 	__wsum psum;
 
-	if (sk->sk_no_check || skb_csum_unnecessary(skb) || !uh->check)
-		return 0;
-
-	inet = inet_sk(sk);
-	psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, ulen,
-				  IPPROTO_UDP, 0);
-
-	if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
-	    !csum_fold(csum_add(psum, skb->csum)))
+	if (sk->sk_no_check || skb_csum_unnecessary(skb))
 		return 0;
 
-	skb->csum = psum;
+#if IS_ENABLED(CONFIG_IPV6)
+	if (sk->sk_family == PF_INET6) {
+		if (!uh->check) {
+			LIMIT_NETDEBUG(KERN_INFO "L2TP: IPv6: checksum is 0\n");
+			return 1;
+		}
+		if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
+		    !csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+				     &ipv6_hdr(skb)->daddr, ulen,
+				     IPPROTO_UDP, skb->csum)) {
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+			return 0;
+		}
+		skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+							 &ipv6_hdr(skb)->daddr,
+							 skb->len, IPPROTO_UDP,
+							 0));
+	} else
+#endif
+	{
+		struct inet_sock *inet;
+		if (!uh->check)
+			return 0;
+		inet = inet_sk(sk);
+		psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr,
+					  ulen, IPPROTO_UDP, 0);
+
+		if ((skb->ip_summed == CHECKSUM_COMPLETE) &&
+		    !csum_fold(csum_add(psum, skb->csum)))
+			return 0;
+		skb->csum = psum;
+	}
 
 	return __skb_checksum_complete(skb);
 }
@@ -988,7 +1013,12 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,
 
 	/* Queue the packet to IP for output */
 	skb->local_df = 1;
-	error = ip_queue_xmit(skb, fl);
+#if IS_ENABLED(CONFIG_IPV6)
+	if (skb->sk->sk_family == PF_INET6)
+		error = inet6_csk_xmit(skb, NULL);
+	else
+#endif
+		error = ip_queue_xmit(skb, fl);
 
 	/* Update stats */
 	if (error >= 0) {
@@ -1021,6 +1051,30 @@ static inline void l2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
 	skb->destructor = l2tp_sock_wfree;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb, int udp_len)
+{
+	struct ipv6_pinfo *np = inet6_sk(sk);
+	struct udphdr *uh = udp_hdr(skb);
+
+	if (!skb_dst(skb) || !skb_dst(skb)->dev ||
+	    !(skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) {
+		skb->ip_summed = CHECKSUM_COMPLETE;
+		skb->csum = skb_checksum(skb, 0, udp_len, 0);
+		uh->check = csum_ipv6_magic(&np->saddr, &np->daddr, udp_len,
+					    IPPROTO_UDP, skb->csum);
+		if (uh->check == 0)
+			uh->check = CSUM_MANGLED_0;
+	} else {
+		skb->ip_summed = CHECKSUM_PARTIAL;
+		skb->csum_start = skb_transport_header(skb) - skb->head;
+		skb->csum_offset = offsetof(struct udphdr, check);
+		uh->check = ~csum_ipv6_magic(&np->saddr, &np->daddr,
+					     udp_len, IPPROTO_UDP, 0);
+	}
+}
+#endif
+
 /* If caller requires the skb to have a ppp header, the header must be
  * inserted in the skb data before calling this function.
  */
@@ -1089,6 +1143,11 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
 		uh->check = 0;
 
 		/* Calculate UDP checksum if configured to do so */
+#if IS_ENABLED(CONFIG_IPV6)
+		if (sk->sk_family == PF_INET6)
+			l2tp_xmit_ipv6_csum(sk, skb, udp_len);
+		else
+#endif
 		if (sk->sk_no_check == UDP_CSUM_NOXMIT)
 			skb->ip_summed = CHECKSUM_NONE;
 		else if ((skb_dst(skb) && skb_dst(skb)->dev) &&
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 8a90d75..c871ced 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -915,8 +915,8 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
 		goto end_put_sess;
 	}
 
-	inet = inet_sk(sk);
-	if (tunnel->version == 2) {
+	inet = inet_sk(tunnel->sock);
+	if ((tunnel->version == 2) && (tunnel->sock->sk_family == AF_INET)) {
 		struct sockaddr_pppol2tp sp;
 		len = sizeof(sp);
 		memset(&sp, 0, len);
@@ -932,6 +932,46 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
 		sp.pppol2tp.addr.sin_port = inet->inet_dport;
 		sp.pppol2tp.addr.sin_addr.s_addr = inet->inet_daddr;
 		memcpy(uaddr, &sp, len);
+#if IS_ENABLED(CONFIG_IPV6)
+	} else if ((tunnel->version == 2) &&
+		   (tunnel->sock->sk_family == AF_INET6)) {
+		struct ipv6_pinfo *np = inet6_sk(tunnel->sock);
+		struct sockaddr_pppol2tpin6 sp;
+		len = sizeof(sp);
+		memset(&sp, 0, len);
+		sp.sa_family	= AF_PPPOX;
+		sp.sa_protocol	= PX_PROTO_OL2TP;
+		sp.pppol2tp.fd  = tunnel->fd;
+		sp.pppol2tp.pid = pls->owner;
+		sp.pppol2tp.s_tunnel = tunnel->tunnel_id;
+		sp.pppol2tp.d_tunnel = tunnel->peer_tunnel_id;
+		sp.pppol2tp.s_session = session->session_id;
+		sp.pppol2tp.d_session = session->peer_session_id;
+		sp.pppol2tp.addr.sin6_family = AF_INET6;
+		sp.pppol2tp.addr.sin6_port = inet->inet_dport;
+		memcpy(&sp.pppol2tp.addr.sin6_addr, &np->daddr,
+		       sizeof(np->daddr));
+		memcpy(uaddr, &sp, len);
+	} else if ((tunnel->version == 3) &&
+		   (tunnel->sock->sk_family == AF_INET6)) {
+		struct ipv6_pinfo *np = inet6_sk(tunnel->sock);
+		struct sockaddr_pppol2tpv3in6 sp;
+		len = sizeof(sp);
+		memset(&sp, 0, len);
+		sp.sa_family	= AF_PPPOX;
+		sp.sa_protocol	= PX_PROTO_OL2TP;
+		sp.pppol2tp.fd  = tunnel->fd;
+		sp.pppol2tp.pid = pls->owner;
+		sp.pppol2tp.s_tunnel = tunnel->tunnel_id;
+		sp.pppol2tp.d_tunnel = tunnel->peer_tunnel_id;
+		sp.pppol2tp.s_session = session->session_id;
+		sp.pppol2tp.d_session = session->peer_session_id;
+		sp.pppol2tp.addr.sin6_family = AF_INET6;
+		sp.pppol2tp.addr.sin6_port = inet->inet_dport;
+		memcpy(&sp.pppol2tp.addr.sin6_addr, &np->daddr,
+		       sizeof(np->daddr));
+		memcpy(uaddr, &sp, len);
+#endif
 	} else if (tunnel->version == 3) {
 		struct sockaddr_pppol2tpv3 sp;
 		len = sizeof(sp);

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

* Re: [v2] Re: [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6
  2012-03-19  3:28       ` [v2] " Benjamin LaHaise
@ 2012-04-10 10:47         ` James Chapman
  2012-04-10 18:49           ` Benjamin LaHaise
  0 siblings, 1 reply; 8+ messages in thread
From: James Chapman @ 2012-04-10 10:47 UTC (permalink / raw)
  To: Benjamin LaHaise; +Cc: Eric Dumazet, netdev

On 19/03/12 03:28, Benjamin LaHaise wrote:
> On Fri, Mar 16, 2012 at 12:19:02PM +0000, James Chapman wrote:
>> Hi Ben,
>>
>> Do you have an updated patch to test?
> 
> Yes, here's an updated version.  

We've tested this in several L2TP environments - looks good. Will you be
submitting a version soon?

We have additional patches ready for review which add IPv6 for L2TPv3,
applying on top of your L2TPv2 patch. We also have a patch for iproute2
to add support for unmanaged L2TPv3 ethernet pseudowires over IPv6. They
can be posted when your L2TPv2 patch is submitted, or as an RFC if you
aren't ready to submit your patch yet.

James

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

* Re: [v2] Re: [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6
  2012-04-10 10:47         ` James Chapman
@ 2012-04-10 18:49           ` Benjamin LaHaise
  2012-04-10 23:40             ` Benjamin LaHaise
  0 siblings, 1 reply; 8+ messages in thread
From: Benjamin LaHaise @ 2012-04-10 18:49 UTC (permalink / raw)
  To: James Chapman; +Cc: Eric Dumazet, netdev

On Tue, Apr 10, 2012 at 11:47:39AM +0100, James Chapman wrote:
> We've tested this in several L2TP environments - looks good. Will you be
> submitting a version soon?

Yes.  I'll try to get that out tonight now that net-next is open again.

Also, I found a bug in OpenL2TPd (1.8 I think): certain types of AVPs are 
not accepted if they are hidden, despite the fact that RFC 2661 permits 
hiding these AVPs.  The AVPs in question are: Assigned Tunnel ID, Challege, 
Challenge Response and Assigned Session ID.

		-ben
-- 
"Thought is the essence of where you are now."

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

* Re: [v2] Re: [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6
  2012-04-10 18:49           ` Benjamin LaHaise
@ 2012-04-10 23:40             ` Benjamin LaHaise
  0 siblings, 0 replies; 8+ messages in thread
From: Benjamin LaHaise @ 2012-04-10 23:40 UTC (permalink / raw)
  To: James Chapman; +Cc: Eric Dumazet, netdev

On Tue, Apr 10, 2012 at 02:49:16PM -0400, Benjamin LaHaise wrote:
...
> Also, I found a bug in OpenL2TPd (1.8 I think): certain types of AVPs are 
> not accepted if they are hidden, despite the fact that RFC 2661 permits 
> hiding these AVPs.  The AVPs in question are: Assigned Tunnel ID, Challege, 
> Challenge Response and Assigned Session ID.

Here's the fix for OpenL2TPd 1.8.  The use-after-realloc() in l2tp_avp_hide() 
doesn't always work if the memory gets moved and glibc scribbles on 
orig_buffer.  There's also an interesting typo in l2tp_avp_message_decode().

		-ben

-- 
"Thought is the essence of where you are now."


--- openl2tp-1.8/l2tp_avp.c.xxx	2008-05-08 15:05:26.000000000 -0400
+++ openl2tp-1.8/l2tp_avp.c	2012-04-10 18:07:05.866208480 -0400
@@ -535,16 +535,18 @@ static int l2tp_avp_hide(void **buffer, 
 	 * and we just need to shift the data up 2 bytes.
 	 */
 	new_buffer_len = orig_buffer_len + 2 + pad + 16;
-	new_buffer = realloc(orig_buffer, new_buffer_len + L2TP_AVP_HEADER_LEN);
+	new_buffer = malloc(new_buffer_len + L2TP_AVP_HEADER_LEN);
 	if (new_buffer == NULL) {
 		return -ENOMEM;
 	}
-	memmove(new_buffer + L2TP_AVP_HEADER_LEN + 2, orig_buffer + L2TP_AVP_HEADER_LEN, orig_buffer_len - L2TP_AVP_HEADER_LEN);
+	memcpy(new_buffer, orig_buffer, L2TP_AVP_HEADER_LEN);
+	memcpy(new_buffer + L2TP_AVP_HEADER_LEN + 2, orig_buffer + L2TP_AVP_HEADER_LEN, orig_buffer_len - L2TP_AVP_HEADER_LEN);
 	orig_len = new_buffer + L2TP_AVP_HEADER_LEN;
 	*orig_len = htons(orig_buffer_len - L2TP_AVP_HEADER_LEN);
 	if (new_buffer != orig_buffer) {
 		*buffer = new_buffer;
 	}
+	free(orig_buffer);
 	flag_len = new_buffer;
 	tmp = ntohs(*flag_len);
 	*flag_len = htons(tmp + 2 + pad);
@@ -1995,7 +1997,7 @@ int l2tp_avp_message_decode(int msg_len,
 				result = l2tp_avp_unhide(avp, &unhidden_avp_len, 
 							 (unsigned char *const) secret, secret_len,
 							 (unsigned char *const) data[TYPE(RANDOM_VECTOR)].value, 
-							 data[TYPE(RANDOM_VECTOR].value_len));
+							 data[TYPE(RANDOM_VECTOR)].value_len);
 				if (result < 0) {
 					l2tp_tunnel_log(tunnel, L2TP_AVPHIDE, LOG_ERR, "AVPHIDE: tunl %hu: avp unhide error: %s", 
 							l2tp_tunnel_id(tunnel), l2tp_strerror(-result));

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

end of thread, other threads:[~2012-04-10 23:40 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-02-14 22:31 [RFC] l2tp/ipv6: support for L2TPv2 over UDP over IPv6 Benjamin LaHaise
2012-02-15  5:06 ` Eric Dumazet
2012-02-15  8:26   ` James Chapman
2012-03-16 12:19     ` James Chapman
2012-03-19  3:28       ` [v2] " Benjamin LaHaise
2012-04-10 10:47         ` James Chapman
2012-04-10 18:49           ` Benjamin LaHaise
2012-04-10 23:40             ` Benjamin LaHaise

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.