* [PATCH 4/4] net/l2tp: add support for L2TP over IPv6 UDP encap
@ 2012-04-18 13:44 Benjamin LaHaise
2012-04-18 15:36 ` James Chapman
2012-04-18 18:30 ` David Miller
0 siblings, 2 replies; 3+ messages in thread
From: Benjamin LaHaise @ 2012-04-18 13:44 UTC (permalink / raw)
To: davem; +Cc: netdev, jchapman
Now that encap_rcv() works on IPv6 UDP sockets, wire L2TP up to IPv6.
Support has been tested with and without hardware offloading.
Signed-off-by: Benjamin LaHaise <bcrl@kvack.org>
---
include/linux/if_pppol2tp.h | 28 +++++++++++++-
include/linux/if_pppox.h | 12 ++++++
net/l2tp/l2tp_core.c | 89 +++++++++++++++++++++++++++++++++++++------
net/l2tp/l2tp_ppp.c | 42 ++++++++++++++++++++-
4 files changed, 157 insertions(+), 14 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/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index f6732b6..8cd5f4b 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,31 @@ 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 +1144,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) &&
@@ -1424,6 +1484,11 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP;
udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv;
+#if IS_ENABLED(CONFIG_IPV6)
+ if (sk->sk_family == PF_INET6)
+ udpv6_encap_enable();
+ else
+#endif
udp_encap_enable();
}
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 1addd9f..27b9dec 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -916,7 +916,7 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
}
inet = inet_sk(tunnel->sock);
- if (tunnel->version == 2) {
+ 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);
--
1.7.4.1
--
"Thought is the essence of where you are now."
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH 4/4] net/l2tp: add support for L2TP over IPv6 UDP encap
2012-04-18 13:44 [PATCH 4/4] net/l2tp: add support for L2TP over IPv6 UDP encap Benjamin LaHaise
@ 2012-04-18 15:36 ` James Chapman
2012-04-18 18:30 ` David Miller
1 sibling, 0 replies; 3+ messages in thread
From: James Chapman @ 2012-04-18 15:36 UTC (permalink / raw)
To: Benjamin LaHaise; +Cc: davem, netdev
On 18/04/12 14:44, Benjamin LaHaise wrote:
> Now that encap_rcv() works on IPv6 UDP sockets, wire L2TP up to IPv6.
> Support has been tested with and without hardware offloading.
>
> Signed-off-by: Benjamin LaHaise <bcrl@kvack.org>
...
> diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
> index f6732b6..8cd5f4b 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);
> }
I'm seeing UDP checksum errors with a loopback test. Regular
(non-loopback) sessions and loopback ipv4 sessions are fine. I retested
your previous patch and verified that it has the same problem. Although
loopback is a weird config, does it indicate a possible problem? Here is
the debug trace:
[ 223.260340] lo: hw csum failure
[ 223.260718] Pid: 1146, comm: pppd Not tainted 3.4.0-rc2+ #5
[ 223.261373] Call Trace:
[ 223.261840] [<c1247b55>] netdev_rx_csum_fault+0x29/0x30
[ 223.262467] [<c12430ac>] __skb_checksum_complete_head+0x42/0x57
[ 223.263159] [<d8a41230>] l2tp_udp_encap_recv+0x13a/0x413 [l2tp_core]
[ 223.263892] [<d8a78c46>] ? pppol2tp_recv+0x11e/0x11e [l2tp_ppp]
[ 223.265741] [<c12cb69f>] ? rcu_read_unlock+0x4d/0x4f
[ 223.266902] [<c12c0028>] ? addrconf_notify+0x5c4/0x7e2
[ 223.268066] [<c12cd2d6>] udpv6_queue_rcv_skb+0x4a/0x256
[ 223.269176] [<c12cd9c6>] __udp6_lib_rcv+0x2c8/0x400
[ 223.269733] [<c12cdb10>] udpv6_rcv+0x12/0x16
[ 223.270222] [<c12bb1d3>] ip6_input_finish+0x1be/0x346
[ 223.270793] [<c12bb015>] ? T.1206+0x4d/0x4d
[ 223.271270] [<c12bb393>] T.1207+0x38/0x3f
[ 223.271729] [<c12bafa1>] ? rcu_read_unlock+0x4f/0x4f
[ 223.272334] [<c12bb79e>] ip6_input+0x17/0x19
[ 223.272824] [<c12bb015>] ? T.1206+0x4d/0x4d
[ 223.273302] [<c12bafc5>] ip6_rcv_finish+0x24/0x27
[ 223.276246] [<c12bb393>] T.1207+0x38/0x3f
[ 223.276727] [<c12bb726>] ipv6_rcv+0x38c/0x3ed
[ 223.277242] [<c12bafa1>] ? rcu_read_unlock+0x4f/0x4f
[ 223.277833] [<c124b97b>] __netif_receive_skb+0x46c/0x4a7
[ 223.278459] [<c124ba54>] process_backlog+0x9e/0x16e
[ 223.279068] [<c124c26d>] net_rx_action+0x9d/0x1af
[ 223.279701] [<c102cadd>] __do_softirq+0xb1/0x17f
[ 223.280281] [<c102ca2c>] ? irq_enter+0x5d/0x5d
[ 223.280807] <IRQ> [<c122112d>] ? ppp_channel_push+0x62/0x93
[ 223.281489] [<c102c9a0>] ? _local_bh_enable_ip+0x8e/0xa6
[ 223.282406] [<c102c9c0>] ? local_bh_enable_ip+0x8/0xa
[ 223.283026] [<c12fe2d6>] ? _raw_spin_unlock_bh+0x25/0x28
[ 223.283655] [<c122112d>] ? ppp_channel_push+0x62/0x93
[ 223.284308] [<c1221200>] ? ppp_write+0xa2/0xac
[ 223.284855] [<c122115e>] ? ppp_channel_push+0x93/0x93
[ 223.285451] [<c10e0412>] ? vfs_write+0x80/0xde
[ 223.286059] [<c10e0d2a>] ? fget_light+0x2b/0x8f
[ 223.286593] [<c10e0507>] ? sys_write+0x3b/0x60
[ 223.287121] [<c130431f>] ? sysenter_do_call+0x12/0x38
--
James Chapman
Katalix Systems Ltd
http://www.katalix.com
Catalysts for your Embedded Linux software development
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH 4/4] net/l2tp: add support for L2TP over IPv6 UDP encap
2012-04-18 13:44 [PATCH 4/4] net/l2tp: add support for L2TP over IPv6 UDP encap Benjamin LaHaise
2012-04-18 15:36 ` James Chapman
@ 2012-04-18 18:30 ` David Miller
1 sibling, 0 replies; 3+ messages in thread
From: David Miller @ 2012-04-18 18:30 UTC (permalink / raw)
To: bcrl; +Cc: netdev, jchapman
From: Benjamin LaHaise <bcrl@kvack.org>
Date: Wed, 18 Apr 2012 09:44:38 -0400
> +#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));
Both the csum_ipv6_magic() calls should either use "ulen" or both
should use "skb->len".
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2012-04-18 18:31 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-04-18 13:44 [PATCH 4/4] net/l2tp: add support for L2TP over IPv6 UDP encap Benjamin LaHaise
2012-04-18 15:36 ` James Chapman
2012-04-18 18:30 ` David Miller
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).