l2tp: Add L2TPv3 IP encapsulation (no UDP) support

This patch adds a new L2TPIP socket family and modifies the core to
handle the case where there is no UDP header in the L2TP
packet. L2TP/IP uses IP protocol 115. Since L2TP/UDP and L2TP/IP
packets differ in layout, the datapath packet handling code needs
changes too. Userspace uses an L2TPIP socket instead of a UDP socket
when IP encapsulation is required.

We can't use raw sockets for this because the semantics of raw sockets
don't lend themselves to the socket-per-tunnel model - we need to

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
James Chapman 2010-04-02 06:19:00 +00:00 committed by David S. Miller
parent e0d4435f93
commit 0d76751fad
7 changed files with 850 additions and 62 deletions

38
include/linux/l2tp.h Normal file
View file

@ -0,0 +1,38 @@
/*
* L2TP-over-IP socket for L2TPv3.
*
* 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>
#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
*/
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)];
};
#endif

View file

@ -51,3 +51,20 @@ config L2TP_V3
If you are connecting to L2TPv3 equipment, or you want to If you are connecting to L2TPv3 equipment, or you want to
tunnel raw ethernet frames using L2TP, say Y here. If tunnel raw ethernet frames using L2TP, say Y here. If
unsure, say N. unsure, say N.
config L2TP_IP
tristate "L2TP IP encapsulation for L2TPv3"
depends on L2TP_V3
help
Support for L2TP-over-IP socket family.
The L2TPv3 protocol defines two possible encapsulations for
L2TP frames, namely UDP and plain IP (without UDP). This
driver provides a new L2TPIP socket family with which
userspace L2TPv3 daemons may create L2TP/IP tunnel sockets
when UDP encapsulation is not required. When L2TP is carried
in IP packets, it used IP protocol number 115, so this port
must be enabled in firewalls.
To compile this driver as a module, choose M here. The module
will be called l2tp_ip.

View file

@ -6,3 +6,4 @@ obj-$(CONFIG_L2TP) += l2tp_core.o
# Build l2tp as modules if L2TP is M # Build l2tp as modules if L2TP is M
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_L2TP_IP)) += l2tp_ip.o

View file

@ -36,8 +36,10 @@
#include <linux/inetdevice.h> #include <linux/inetdevice.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/in.h>
#include <linux/ip.h> #include <linux/ip.h>
#include <linux/udp.h> #include <linux/udp.h>
#include <linux/l2tp.h>
#include <linux/hash.h> #include <linux/hash.h>
#include <linux/sort.h> #include <linux/sort.h>
#include <linux/file.h> #include <linux/file.h>
@ -48,6 +50,7 @@
#include <net/ip.h> #include <net/ip.h>
#include <net/udp.h> #include <net/udp.h>
#include <net/xfrm.h> #include <net/xfrm.h>
#include <net/protocol.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/atomic.h> #include <asm/atomic.h>
@ -849,15 +852,21 @@ static int l2tp_build_l2tpv2_header(struct l2tp_session *session, void *buf)
static int l2tp_build_l2tpv3_header(struct l2tp_session *session, void *buf) static int l2tp_build_l2tpv3_header(struct l2tp_session *session, void *buf)
{ {
struct l2tp_tunnel *tunnel = session->tunnel;
char *bufp = buf; char *bufp = buf;
char *optr = bufp; char *optr = bufp;
u16 flags = L2TP_HDR_VER_3;
/* Setup L2TP header. */ /* Setup L2TP header. The header differs slightly for UDP and
*((__be16 *) bufp) = htons(flags); * IP encapsulations. For UDP, there is 4 bytes of flags.
bufp += 2; */
*((__be16 *) bufp) = 0; if (tunnel->encap == L2TP_ENCAPTYPE_UDP) {
bufp += 2; u16 flags = L2TP_HDR_VER_3;
*((__be16 *) bufp) = htons(flags);
bufp += 2;
*((__be16 *) bufp) = 0;
bufp += 2;
}
*((__be32 *) bufp) = htonl(session->peer_session_id); *((__be32 *) bufp) = htonl(session->peer_session_id);
bufp += 4; bufp += 4;
if (session->cookie_len) { if (session->cookie_len) {
@ -902,10 +911,11 @@ int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, size_t dat
if (session->debug & L2TP_MSG_DATA) { if (session->debug & L2TP_MSG_DATA) {
int i; int i;
unsigned char *datap = skb->data + sizeof(struct udphdr); int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0;
unsigned char *datap = skb->data + uhlen;
printk(KERN_DEBUG "%s: xmit:", session->name); printk(KERN_DEBUG "%s: xmit:", session->name);
for (i = 0; i < (len - sizeof(struct udphdr)); i++) { for (i = 0; i < (len - uhlen); i++) {
printk(" %02X", *datap++); printk(" %02X", *datap++);
if (i == 31) { if (i == 31) {
printk(" ..."); printk(" ...");
@ -956,21 +966,23 @@ static inline void l2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len) int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len)
{ {
int data_len = skb->len; int data_len = skb->len;
struct sock *sk = session->tunnel->sock; struct l2tp_tunnel *tunnel = session->tunnel;
struct sock *sk = tunnel->sock;
struct udphdr *uh; struct udphdr *uh;
unsigned int udp_len;
struct inet_sock *inet; struct inet_sock *inet;
__wsum csum; __wsum csum;
int old_headroom; int old_headroom;
int new_headroom; int new_headroom;
int headroom; int headroom;
int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0;
int udp_len;
/* Check that there's enough headroom in the skb to insert IP, /* Check that there's enough headroom in the skb to insert IP,
* UDP and L2TP headers. If not enough, expand it to * UDP and L2TP headers. If not enough, expand it to
* make room. Adjust truesize. * make room. Adjust truesize.
*/ */
headroom = NET_SKB_PAD + sizeof(struct iphdr) + headroom = NET_SKB_PAD + sizeof(struct iphdr) +
sizeof(struct udphdr) + hdr_len; uhlen + hdr_len;
old_headroom = skb_headroom(skb); old_headroom = skb_headroom(skb);
if (skb_cow_head(skb, headroom)) if (skb_cow_head(skb, headroom))
goto abort; goto abort;
@ -981,18 +993,8 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
/* Setup L2TP header */ /* Setup L2TP header */
session->build_header(session, __skb_push(skb, hdr_len)); session->build_header(session, __skb_push(skb, hdr_len));
udp_len = sizeof(struct udphdr) + hdr_len + data_len;
/* Setup UDP header */
inet = inet_sk(sk);
__skb_push(skb, sizeof(*uh));
skb_reset_transport_header(skb);
uh = udp_hdr(skb);
uh->source = inet->inet_sport;
uh->dest = inet->inet_dport;
uh->len = htons(udp_len);
uh->check = 0;
/* Reset skb netfilter state */
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt)); memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED | IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
IPSKB_REROUTED); IPSKB_REROUTED);
@ -1001,29 +1003,48 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len
/* Get routing info from the tunnel socket */ /* Get routing info from the tunnel socket */
skb_dst_drop(skb); skb_dst_drop(skb);
skb_dst_set(skb, dst_clone(__sk_dst_get(sk))); skb_dst_set(skb, dst_clone(__sk_dst_get(sk)));
l2tp_skb_set_owner_w(skb, sk);
/* Calculate UDP checksum if configured to do so */ switch (tunnel->encap) {
if (sk->sk_no_check == UDP_CSUM_NOXMIT) case L2TP_ENCAPTYPE_UDP:
skb->ip_summed = CHECKSUM_NONE; /* Setup UDP header */
else if ((skb_dst(skb) && skb_dst(skb)->dev) && inet = inet_sk(sk);
(!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) { __skb_push(skb, sizeof(*uh));
skb->ip_summed = CHECKSUM_COMPLETE; skb_reset_transport_header(skb);
csum = skb_checksum(skb, 0, udp_len, 0); uh = udp_hdr(skb);
uh->check = csum_tcpudp_magic(inet->inet_saddr, uh->source = inet->inet_sport;
inet->inet_daddr, uh->dest = inet->inet_dport;
udp_len, IPPROTO_UDP, csum); udp_len = uhlen + hdr_len + data_len;
if (uh->check == 0) uh->len = htons(udp_len);
uh->check = CSUM_MANGLED_0; uh->check = 0;
} else {
skb->ip_summed = CHECKSUM_PARTIAL; /* Calculate UDP checksum if configured to do so */
skb->csum_start = skb_transport_header(skb) - skb->head; if (sk->sk_no_check == UDP_CSUM_NOXMIT)
skb->csum_offset = offsetof(struct udphdr, check); skb->ip_summed = CHECKSUM_NONE;
uh->check = ~csum_tcpudp_magic(inet->inet_saddr, else if ((skb_dst(skb) && skb_dst(skb)->dev) &&
inet->inet_daddr, (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) {
udp_len, IPPROTO_UDP, 0); skb->ip_summed = CHECKSUM_COMPLETE;
csum = skb_checksum(skb, 0, udp_len, 0);
uh->check = csum_tcpudp_magic(inet->inet_saddr,
inet->inet_daddr,
udp_len, IPPROTO_UDP, 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_tcpudp_magic(inet->inet_saddr,
inet->inet_daddr,
udp_len, IPPROTO_UDP, 0);
}
break;
case L2TP_ENCAPTYPE_IP:
break;
} }
l2tp_skb_set_owner_w(skb, sk);
l2tp_xmit_core(session, skb, data_len); l2tp_xmit_core(session, skb, data_len);
abort: abort:
@ -1053,9 +1074,15 @@ void l2tp_tunnel_destruct(struct sock *sk)
/* Close all sessions */ /* Close all sessions */
l2tp_tunnel_closeall(tunnel); l2tp_tunnel_closeall(tunnel);
/* No longer an encapsulation socket. See net/ipv4/udp.c */ switch (tunnel->encap) {
(udp_sk(sk))->encap_type = 0; case L2TP_ENCAPTYPE_UDP:
(udp_sk(sk))->encap_rcv = NULL; /* No longer an encapsulation socket. See net/ipv4/udp.c */
(udp_sk(sk))->encap_type = 0;
(udp_sk(sk))->encap_rcv = NULL;
break;
case L2TP_ENCAPTYPE_IP:
break;
}
/* Remove hooks into tunnel socket */ /* Remove hooks into tunnel socket */
tunnel->sock = NULL; tunnel->sock = NULL;
@ -1168,6 +1195,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
struct socket *sock = NULL; struct socket *sock = NULL;
struct sock *sk = NULL; struct sock *sk = NULL;
struct l2tp_net *pn; struct l2tp_net *pn;
enum l2tp_encap_type encap = L2TP_ENCAPTYPE_UDP;
/* Get the tunnel socket from the fd, which was opened by /* Get the tunnel socket from the fd, which was opened by
* the userspace L2TP daemon. * the userspace L2TP daemon.
@ -1182,18 +1210,27 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
sk = sock->sk; sk = sock->sk;
if (cfg != NULL)
encap = cfg->encap;
/* Quick sanity checks */ /* Quick sanity checks */
err = -EPROTONOSUPPORT; switch (encap) {
if (sk->sk_protocol != IPPROTO_UDP) { case L2TP_ENCAPTYPE_UDP:
printk(KERN_ERR "tunl %hu: fd %d wrong protocol, got %d, expected %d\n", err = -EPROTONOSUPPORT;
tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP); if (sk->sk_protocol != IPPROTO_UDP) {
goto err; printk(KERN_ERR "tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
} tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP);
err = -EAFNOSUPPORT; goto err;
if (sock->ops->family != AF_INET) { }
printk(KERN_ERR "tunl %hu: fd %d wrong family, got %d, expected %d\n", break;
tunnel_id, fd, sock->ops->family, AF_INET); case L2TP_ENCAPTYPE_IP:
goto err; err = -EPROTONOSUPPORT;
if (sk->sk_protocol != IPPROTO_L2TP) {
printk(KERN_ERR "tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
tunnel_id, fd, sk->sk_protocol, IPPROTO_L2TP);
goto err;
}
break;
} }
/* Check if this socket has already been prepped */ /* Check if this socket has already been prepped */
@ -1223,12 +1260,16 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32
tunnel->l2tp_net = net; tunnel->l2tp_net = net;
pn = l2tp_pernet(net); pn = l2tp_pernet(net);
if (cfg) if (cfg != NULL)
tunnel->debug = cfg->debug; tunnel->debug = cfg->debug;
/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */ /* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
udp_sk(sk)->encap_type = UDP_ENCAP_L2TPINUDP; tunnel->encap = encap;
udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv; if (encap == L2TP_ENCAPTYPE_UDP) {
/* 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;
}
sk->sk_user_data = tunnel; sk->sk_user_data = tunnel;
@ -1318,7 +1359,9 @@ void l2tp_session_set_header_len(struct l2tp_session *session, int version)
if (session->send_seq) if (session->send_seq)
session->hdr_len += 4; session->hdr_len += 4;
} else { } else {
session->hdr_len = 8 + session->cookie_len + session->l2specific_len + session->offset; session->hdr_len = 4 + session->cookie_len + session->l2specific_len + session->offset;
if (session->tunnel->encap == L2TP_ENCAPTYPE_UDP)
session->hdr_len += 4;
} }
} }

View file

@ -48,6 +48,11 @@ enum l2tp_l2spec_type {
L2TP_L2SPECTYPE_DEFAULT, L2TP_L2SPECTYPE_DEFAULT,
}; };
enum l2tp_encap_type {
L2TP_ENCAPTYPE_UDP,
L2TP_ENCAPTYPE_IP,
};
struct sk_buff; struct sk_buff;
struct l2tp_stats { struct l2tp_stats {
@ -155,6 +160,7 @@ struct l2tp_session {
struct l2tp_tunnel_cfg { struct l2tp_tunnel_cfg {
int debug; /* bitmask of debug message int debug; /* bitmask of debug message
* categories */ * categories */
enum l2tp_encap_type encap;
}; };
struct l2tp_tunnel { struct l2tp_tunnel {
@ -170,6 +176,7 @@ struct l2tp_tunnel {
char name[20]; /* for logging */ char name[20]; /* for logging */
int debug; /* bitmask of debug message int debug; /* bitmask of debug message
* categories */ * categories */
enum l2tp_encap_type encap;
struct l2tp_stats stats; struct l2tp_stats stats;
struct list_head list; /* Keep a list of all tunnels */ struct list_head list; /* Keep a list of all tunnels */

679
net/l2tp/l2tp_ip.c Normal file
View file

@ -0,0 +1,679 @@
/*
* L2TPv3 IP encapsulation support
*
* Copyright (c) 2008,2009,2010 Katalix Systems Ltd
*
* 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 <linux/icmp.h>
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/random.h>
#include <linux/socket.h>
#include <linux/l2tp.h>
#include <linux/in.h>
#include <net/sock.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/udp.h>
#include <net/inet_common.h>
#include <net/inet_hashtables.h>
#include <net/tcp_states.h>
#include <net/protocol.h>
#include <net/xfrm.h>
#include "l2tp_core.h"
struct l2tp_ip_sock {
/* inet_sock has to be the first member of l2tp_ip_sock */
struct inet_sock inet;
__u32 conn_id;
__u32 peer_conn_id;
__u64 tx_packets;
__u64 tx_bytes;
__u64 tx_errors;
__u64 rx_packets;
__u64 rx_bytes;
__u64 rx_errors;
};
static DEFINE_RWLOCK(l2tp_ip_lock);
static struct hlist_head l2tp_ip_table;
static struct hlist_head l2tp_ip_bind_table;
static inline struct l2tp_ip_sock *l2tp_ip_sk(const struct sock *sk)
{
return (struct l2tp_ip_sock *)sk;
}
static struct sock *__l2tp_ip_bind_lookup(struct net *net, __be32 laddr, int dif, u32 tunnel_id)
{
struct hlist_node *node;
struct sock *sk;
sk_for_each_bound(sk, node, &l2tp_ip_bind_table) {
struct inet_sock *inet = inet_sk(sk);
struct l2tp_ip_sock *l2tp = l2tp_ip_sk(sk);
if (l2tp == NULL)
continue;
if ((l2tp->conn_id == tunnel_id) &&
#ifdef CONFIG_NET_NS
(sk->sk_net == net) &&
#endif
!(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) &&
!(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif))
goto found;
}
sk = NULL;
found:
return sk;
}
static inline struct sock *l2tp_ip_bind_lookup(struct net *net, __be32 laddr, int dif, u32 tunnel_id)
{
struct sock *sk = __l2tp_ip_bind_lookup(net, laddr, dif, tunnel_id);
if (sk)
sock_hold(sk);
return sk;
}
/* When processing receive frames, there are two cases to
* consider. Data frames consist of a non-zero session-id and an
* optional cookie. Control frames consist of a regular L2TP header
* preceded by 32-bits of zeros.
*
* L2TPv3 Session Header Over IP
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Session ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Cookie (optional, maximum 64 bits)...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* L2TPv3 Control Message Header Over IP
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | (32 bits of zeros) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* |T|L|x|x|S|x|x|x|x|x|x|x| Ver | Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Control Connection ID |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Ns | Nr |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* All control frames are passed to userspace.
*/
static int l2tp_ip_recv(struct sk_buff *skb)
{
struct sock *sk;
u32 session_id;
u32 tunnel_id;
unsigned char *ptr, *optr;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel = NULL;
int length;
int offset;
/* Point to L2TP header */
optr = ptr = skb->data;
if (!pskb_may_pull(skb, 4))
goto discard;
session_id = ntohl(*((__be32 *) ptr));
ptr += 4;
/* RFC3931: L2TP/IP packets have the first 4 bytes containing
* the session_id. If it is 0, the packet is a L2TP control
* frame and the session_id value can be discarded.
*/
if (session_id == 0) {
__skb_pull(skb, 4);
goto pass_up;
}
/* Ok, this is a data packet. Lookup the session. */
session = l2tp_session_find(&init_net, NULL, session_id);
if (session == NULL)
goto discard;
tunnel = session->tunnel;
if (tunnel == NULL)
goto discard;
/* Trace packet contents, if enabled */
if (tunnel->debug & L2TP_MSG_DATA) {
length = min(32u, skb->len);
if (!pskb_may_pull(skb, length))
goto discard;
printk(KERN_DEBUG "%s: ip recv: ", tunnel->name);
offset = 0;
do {
printk(" %02X", ptr[offset]);
} while (++offset < length);
printk("\n");
}
l2tp_recv_common(session, skb, ptr, optr, 0, skb->len, tunnel->recv_payload_hook);
return 0;
pass_up:
/* Get the tunnel_id from the L2TP header */
if (!pskb_may_pull(skb, 12))
goto discard;
if ((skb->data[0] & 0xc0) != 0xc0)
goto discard;
tunnel_id = ntohl(*(__be32 *) &skb->data[4]);
tunnel = l2tp_tunnel_find(&init_net, tunnel_id);
if (tunnel != NULL)
sk = tunnel->sock;
else {
struct iphdr *iph = (struct iphdr *) skb_network_header(skb);
read_lock_bh(&l2tp_ip_lock);
sk = __l2tp_ip_bind_lookup(&init_net, iph->daddr, 0, tunnel_id);
read_unlock_bh(&l2tp_ip_lock);
}
if (sk == NULL)
goto discard;
sock_hold(sk);
if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
goto discard_put;
nf_reset(skb);
return sk_receive_skb(sk, skb, 1);
discard_put:
sock_put(sk);
discard:
kfree_skb(skb);
return 0;
}
static int l2tp_ip_open(struct sock *sk)
{
/* Prevent autobind. We don't have ports. */
inet_sk(sk)->inet_num = IPPROTO_L2TP;
write_lock_bh(&l2tp_ip_lock);
sk_add_node(sk, &l2tp_ip_table);
write_unlock_bh(&l2tp_ip_lock);
return 0;
}
static void l2tp_ip_close(struct sock *sk, long timeout)
{
write_lock_bh(&l2tp_ip_lock);
hlist_del_init(&sk->sk_bind_node);
hlist_del_init(&sk->sk_node);
write_unlock_bh(&l2tp_ip_lock);
sk_common_release(sk);
}
static void l2tp_ip_destroy_sock(struct sock *sk)
{
struct sk_buff *skb;
while ((skb = __skb_dequeue_tail(&sk->sk_write_queue)) != NULL)
kfree_skb(skb);
sk_refcnt_debug_dec(sk);
}
static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
struct inet_sock *inet = inet_sk(sk);
struct sockaddr_l2tpip *addr = (struct sockaddr_l2tpip *) uaddr;
int ret = -EINVAL;
int chk_addr_ret;
ret = -EADDRINUSE;
read_lock_bh(&l2tp_ip_lock);
if (__l2tp_ip_bind_lookup(&init_net, addr->l2tp_addr.s_addr, sk->sk_bound_dev_if, addr->l2tp_conn_id))
goto out_in_use;
read_unlock_bh(&l2tp_ip_lock);
lock_sock(sk);
if (sk->sk_state != TCP_CLOSE || addr_len < sizeof(struct sockaddr_l2tpip))
goto out;
chk_addr_ret = inet_addr_type(&init_net, addr->l2tp_addr.s_addr);
ret = -EADDRNOTAVAIL;
if (addr->l2tp_addr.s_addr && chk_addr_ret != RTN_LOCAL &&
chk_addr_ret != RTN_MULTICAST && chk_addr_ret != RTN_BROADCAST)
goto out;
inet->inet_rcv_saddr = inet->inet_saddr = addr->l2tp_addr.s_addr;
if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
inet->inet_saddr = 0; /* Use device */
sk_dst_reset(sk);
l2tp_ip_sk(sk)->conn_id = addr->l2tp_conn_id;
write_lock_bh(&l2tp_ip_lock);
sk_add_bind_node(sk, &l2tp_ip_bind_table);
sk_del_node_init(sk);
write_unlock_bh(&l2tp_ip_lock);
ret = 0;
out:
release_sock(sk);
return ret;
out_in_use:
read_unlock_bh(&l2tp_ip_lock);
return ret;
}
static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
int rc;
struct inet_sock *inet = inet_sk(sk);
struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *) uaddr;
struct rtable *rt;
__be32 saddr;
int oif;
rc = -EINVAL;
if (addr_len < sizeof(*lsa))
goto out;
rc = -EAFNOSUPPORT;
if (lsa->l2tp_family != AF_INET)
goto out;
sk_dst_reset(sk);
oif = sk->sk_bound_dev_if;
saddr = inet->inet_saddr;
rc = -EINVAL;
if (ipv4_is_multicast(lsa->l2tp_addr.s_addr))
goto out;
rc = ip_route_connect(&rt, lsa->l2tp_addr.s_addr, saddr,
RT_CONN_FLAGS(sk), oif,
IPPROTO_L2TP,
0, 0, sk, 1);
if (rc) {
if (rc == -ENETUNREACH)
IP_INC_STATS_BH(&init_net, IPSTATS_MIB_OUTNOROUTES);
goto out;
}
rc = -ENETUNREACH;
if (rt->rt_flags & (RTCF_MULTICAST | RTCF_BROADCAST)) {
ip_rt_put(rt);
goto out;
}
l2tp_ip_sk(sk)->peer_conn_id = lsa->l2tp_conn_id;
if (!inet->inet_saddr)
inet->inet_saddr = rt->rt_src;
if (!inet->inet_rcv_saddr)
inet->inet_rcv_saddr = rt->rt_src;
inet->inet_daddr = rt->rt_dst;
sk->sk_state = TCP_ESTABLISHED;
inet->inet_id = jiffies;
sk_dst_set(sk, &rt->u.dst);
write_lock_bh(&l2tp_ip_lock);
hlist_del_init(&sk->sk_bind_node);
sk_add_bind_node(sk, &l2tp_ip_bind_table);
write_unlock_bh(&l2tp_ip_lock);
rc = 0;
out:
return rc;
}
static int l2tp_ip_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
struct sock *sk = sock->sk;
struct inet_sock *inet = inet_sk(sk);
struct l2tp_ip_sock *lsk = l2tp_ip_sk(sk);
struct sockaddr_l2tpip *lsa = (struct sockaddr_l2tpip *)uaddr;
memset(lsa, 0, sizeof(*lsa));
lsa->l2tp_family = AF_INET;
if (peer) {
if (!inet->inet_dport)
return -ENOTCONN;
lsa->l2tp_conn_id = lsk->peer_conn_id;
lsa->l2tp_addr.s_addr = inet->inet_daddr;
} else {
__be32 addr = inet->inet_rcv_saddr;
if (!addr)
addr = inet->inet_saddr;
lsa->l2tp_conn_id = lsk->conn_id;
lsa->l2tp_addr.s_addr = addr;
}
*uaddr_len = sizeof(*lsa);
return 0;
}
static int l2tp_ip_backlog_recv(struct sock *sk, struct sk_buff *skb)
{
int rc;
if (!xfrm4_policy_check(sk, XFRM_POLICY_IN, skb))
goto drop;
nf_reset(skb);
/* Charge it to the socket, dropping if the queue is full. */
rc = sock_queue_rcv_skb(sk, skb);
if (rc < 0)
goto drop;
return 0;
drop:
IP_INC_STATS(&init_net, IPSTATS_MIB_INDISCARDS);
kfree_skb(skb);
return -1;
}
/* Userspace will call sendmsg() on the tunnel socket to send L2TP
* control frames.
*/
static int l2tp_ip_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, size_t len)
{
struct sk_buff *skb;
int rc;
struct l2tp_ip_sock *lsa = l2tp_ip_sk(sk);
struct inet_sock *inet = inet_sk(sk);
struct ip_options *opt = inet->opt;
struct rtable *rt = NULL;
int connected = 0;
__be32 daddr;
if (sock_flag(sk, SOCK_DEAD))
return -ENOTCONN;
/* Get and verify the address. */
if (msg->msg_name) {
struct sockaddr_l2tpip *lip = (struct sockaddr_l2tpip *) msg->msg_name;
if (msg->msg_namelen < sizeof(*lip))
return -EINVAL;
if (lip->l2tp_family != AF_INET) {
if (lip->l2tp_family != AF_UNSPEC)
return -EAFNOSUPPORT;
}
daddr = lip->l2tp_addr.s_addr;
} else {
if (sk->sk_state != TCP_ESTABLISHED)
return -EDESTADDRREQ;
daddr = inet->inet_daddr;
connected = 1;
}
/* Allocate a socket buffer */
rc = -ENOMEM;
skb = sock_wmalloc(sk, 2 + NET_SKB_PAD + sizeof(struct iphdr) +
4 + len, 0, GFP_KERNEL);
if (!skb)
goto error;
/* Reserve space for headers, putting IP header on 4-byte boundary. */
skb_reserve(skb, 2 + NET_SKB_PAD);
skb_reset_network_header(skb);
skb_reserve(skb, sizeof(struct iphdr));
skb_reset_transport_header(skb);
/* Insert 0 session_id */
*((__be32 *) skb_put(skb, 4)) = 0;
/* Copy user data into skb */
rc = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
if (rc < 0) {
kfree_skb(skb);
goto error;
}
if (connected)
rt = (struct rtable *) __sk_dst_check(sk, 0);
if (rt == NULL) {
/* Use correct destination address if we have options. */
if (opt && opt->srr)
daddr = opt->faddr;
{
struct flowi fl = { .oif = sk->sk_bound_dev_if,
.nl_u = { .ip4_u = {
.daddr = daddr,
.saddr = inet->inet_saddr,
.tos = RT_CONN_FLAGS(sk) } },
.proto = sk->sk_protocol,
.flags = inet_sk_flowi_flags(sk),
.uli_u = { .ports = {
.sport = inet->inet_sport,
.dport = inet->inet_dport } } };
/* If this fails, retransmit mechanism of transport layer will
* keep trying until route appears or the connection times
* itself out.
*/
security_sk_classify_flow(sk, &fl);
if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0))
goto no_route;
}
sk_setup_caps(sk, &rt->u.dst);
}
skb_dst_set(skb, dst_clone(&rt->u.dst));
/* Queue the packet to IP for output */
rc = ip_queue_xmit(skb, 0);
error:
/* Update stats */
if (rc >= 0) {
lsa->tx_packets++;
lsa->tx_bytes += len;
rc = len;
} else {
lsa->tx_errors++;
}
return rc;
no_route:
IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);
kfree_skb(skb);
return -EHOSTUNREACH;
}
static int l2tp_ip_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int noblock, int flags, int *addr_len)
{
struct inet_sock *inet = inet_sk(sk);
struct l2tp_ip_sock *lsk = l2tp_ip_sk(sk);
size_t copied = 0;
int err = -EOPNOTSUPP;
struct sockaddr_in *sin = (struct sockaddr_in *)msg->msg_name;
struct sk_buff *skb;
if (flags & MSG_OOB)
goto out;
if (addr_len)
*addr_len = sizeof(*sin);
skb = skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
goto out;
copied = skb->len;
if (len < copied) {
msg->msg_flags |= MSG_TRUNC;
copied = len;
}
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
if (err)
goto done;
sock_recv_timestamp(msg, sk, skb);
/* Copy the address. */
if (sin) {
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
sin->sin_port = 0;
memset(&sin->sin_zero, 0, sizeof(sin->sin_zero));
}
if (inet->cmsg_flags)
ip_cmsg_recv(msg, skb);
if (flags & MSG_TRUNC)
copied = skb->len;
done:
skb_free_datagram(sk, skb);
out:
if (err) {
lsk->rx_errors++;
return err;
}
lsk->rx_packets++;
lsk->rx_bytes += copied;
return copied;
}
struct proto l2tp_ip_prot = {
.name = "L2TP/IP",
.owner = THIS_MODULE,
.init = l2tp_ip_open,
.close = l2tp_ip_close,
.bind = l2tp_ip_bind,
.connect = l2tp_ip_connect,
.disconnect = udp_disconnect,
.ioctl = udp_ioctl,
.destroy = l2tp_ip_destroy_sock,
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
.sendmsg = l2tp_ip_sendmsg,
.recvmsg = l2tp_ip_recvmsg,
.backlog_rcv = l2tp_ip_backlog_recv,
.hash = inet_hash,
.unhash = inet_unhash,
.obj_size = sizeof(struct l2tp_ip_sock),
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_ip_setsockopt,
.compat_getsockopt = compat_ip_getsockopt,
#endif
};
static const struct proto_ops l2tp_ip_ops = {
.family = PF_INET,
.owner = THIS_MODULE,
.release = inet_release,
.bind = inet_bind,
.connect = inet_dgram_connect,
.socketpair = sock_no_socketpair,
.accept = sock_no_accept,
.getname = l2tp_ip_getname,
.poll = datagram_poll,
.ioctl = inet_ioctl,
.listen = sock_no_listen,
.shutdown = inet_shutdown,
.setsockopt = sock_common_setsockopt,
.getsockopt = sock_common_getsockopt,
.sendmsg = inet_sendmsg,
.recvmsg = sock_common_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
#ifdef CONFIG_COMPAT
.compat_setsockopt = compat_sock_common_setsockopt,
.compat_getsockopt = compat_sock_common_getsockopt,
#endif
};
static struct inet_protosw l2tp_ip_protosw = {
.type = SOCK_DGRAM,
.protocol = IPPROTO_L2TP,
.prot = &l2tp_ip_prot,
.ops = &l2tp_ip_ops,
.no_check = 0,
};
static struct net_protocol l2tp_ip_protocol __read_mostly = {
.handler = l2tp_ip_recv,
};
static int __init l2tp_ip_init(void)
{
int err;
printk(KERN_INFO "L2TP IP encapsulation support (L2TPv3)\n");
err = proto_register(&l2tp_ip_prot, 1);
if (err != 0)
goto out;
err = inet_add_protocol(&l2tp_ip_protocol, IPPROTO_L2TP);
if (err)
goto out1;
inet_register_protosw(&l2tp_ip_protosw);
return 0;
out1:
proto_unregister(&l2tp_ip_prot);
out:
return err;
}
static void __exit l2tp_ip_exit(void)
{
inet_unregister_protosw(&l2tp_ip_protosw);
inet_del_protocol(&l2tp_ip_protocol, IPPROTO_L2TP);
proto_unregister(&l2tp_ip_prot);
}
module_init(l2tp_ip_init);
module_exit(l2tp_ip_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("L2TP over IP");
MODULE_VERSION("1.0");
MODULE_ALIAS_NET_PF_PROTO_TYPE(PF_INET, SOCK_DGRAM, IPPROTO_L2TP);

View file

@ -305,6 +305,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
struct l2tp_session *session; struct l2tp_session *session;
struct l2tp_tunnel *tunnel; struct l2tp_tunnel *tunnel;
struct pppol2tp_session *ps; struct pppol2tp_session *ps;
int uhlen;
error = -ENOTCONN; error = -ENOTCONN;
if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED)) if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
@ -321,10 +322,12 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
if (tunnel == NULL) if (tunnel == NULL)
goto error_put_sess; goto error_put_sess;
uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0;
/* Allocate a socket buffer */ /* Allocate a socket buffer */
error = -ENOMEM; error = -ENOMEM;
skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) + skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) +
sizeof(struct udphdr) + session->hdr_len + uhlen + session->hdr_len +
sizeof(ppph) + total_len, sizeof(ppph) + total_len,
0, GFP_KERNEL); 0, GFP_KERNEL);
if (!skb) if (!skb)
@ -335,7 +338,7 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
skb_reset_network_header(skb); skb_reset_network_header(skb);
skb_reserve(skb, sizeof(struct iphdr)); skb_reserve(skb, sizeof(struct iphdr));
skb_reset_transport_header(skb); skb_reset_transport_header(skb);
skb_reserve(skb, sizeof(struct udphdr)); skb_reserve(skb, uhlen);
/* Add PPP header */ /* Add PPP header */
skb->data[0] = ppph[0]; skb->data[0] = ppph[0];