Commit fd558d18 authored by James Chapman's avatar James Chapman Committed by David S. Miller

l2tp: Split pppol2tp patch into separate l2tp and ppp parts

This patch splits the pppol2tp driver into separate L2TP and PPP parts
to prepare for L2TPv3 support. In L2TPv3, protocols other than PPP can
be carried, so this split creates a common L2TP core that will handle
the common L2TP bits which protocol support modules such as PPP will
use.

Note that the existing pppol2tp module is split into l2tp_core and
l2tp_ppp by this change.

There are no feature changes here. Internally, however, there are
significant changes, mostly to handle the separation of PPP-specific
data from the L2TP session and to provide hooks in the core for
modules like PPP to access.
Signed-off-by: default avatarJames Chapman <jchapman@katalix.com>
Reviewed-by: default avatarRandy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 21b4aaa1
......@@ -3158,17 +3158,12 @@ config PPPOATM
config PPPOL2TP
tristate "PPP over L2TP (EXPERIMENTAL)"
depends on EXPERIMENTAL && PPP && INET
depends on EXPERIMENTAL && L2TP && PPP
help
Support for PPP-over-L2TP socket family. L2TP is a protocol
used by ISPs and enterprises to tunnel PPP traffic over UDP
tunnels. L2TP is replacing PPTP for VPN uses.
This kernel component handles only L2TP data packets: a
userland daemon handles L2TP the control protocol (tunnel
and session setup). One such daemon is OpenL2TP
(http://openl2tp.sourceforge.net/).
config SLIP
tristate "SLIP (serial line) support"
---help---
......
......@@ -2,7 +2,7 @@
* Linux PPP over L2TP (PPPoL2TP) Socket Implementation (RFC 2661)
*
* This file supplies definitions required by the PPP over L2TP driver
* (pppol2tp.c). All version information wrt this file is located in pppol2tp.c
* (l2tp_ppp.c). All version information wrt this file is located in l2tp_ppp.c
*
* License:
* This program is free software; you can redistribute it and/or
......
......@@ -186,6 +186,7 @@ source "net/sctp/Kconfig"
source "net/rds/Kconfig"
source "net/tipc/Kconfig"
source "net/atm/Kconfig"
source "net/l2tp/Kconfig"
source "net/802/Kconfig"
source "net/bridge/Kconfig"
source "net/dsa/Kconfig"
......
......@@ -40,7 +40,7 @@ obj-$(CONFIG_BT) += bluetooth/
obj-$(CONFIG_SUNRPC) += sunrpc/
obj-$(CONFIG_AF_RXRPC) += rxrpc/
obj-$(CONFIG_ATM) += atm/
obj-$(CONFIG_PPPOL2TP) += l2tp/
obj-$(CONFIG_L2TP) += l2tp/
obj-$(CONFIG_DECNET) += decnet/
obj-$(CONFIG_ECONET) += econet/
obj-$(CONFIG_PHONET) += phonet/
......
#
# Layer Two Tunneling Protocol (L2TP)
#
menuconfig L2TP
tristate "Layer Two Tunneling Protocol (L2TP)"
depends on INET
---help---
Layer Two Tunneling Protocol
From RFC 2661 <http://www.ietf.org/rfc/rfc2661.txt>.
L2TP facilitates the tunneling of packets across an
intervening network in a way that is as transparent as
possible to both end-users and applications.
L2TP is often used to tunnel PPP traffic over IP
tunnels. One IP tunnel may carry thousands of individual PPP
connections. L2TP is also used as a VPN protocol, popular
with home workers to connect to their offices.
The kernel component handles only L2TP data packets: a
userland daemon handles L2TP the control protocol (tunnel
and session setup). One such daemon is OpenL2TP
(http://openl2tp.org/).
If you don't need L2TP, say N. To compile all L2TP code as
modules, choose M here.
......@@ -2,4 +2,7 @@
# Makefile for the L2TP.
#
obj-$(CONFIG_PPPOL2TP) += pppol2tp.o
obj-$(CONFIG_L2TP) += l2tp_core.o
# Build l2tp as modules if L2TP is M
obj-$(subst y,$(CONFIG_L2TP),$(CONFIG_PPPOL2TP)) += l2tp_ppp.o
/*
* L2TP core.
*
* Copyright (c) 2008,2009,2010 Katalix Systems Ltd
*
* This file contains some code of the original L2TPv2 pppol2tp
* driver, which has the following copyright:
*
* Authors: Martijn van Oosterhout <kleptog@svana.org>
* James Chapman (jchapman@katalix.com)
* Contributors:
* Michal Ostrowski <mostrows@speakeasy.net>
* Arnaldo Carvalho de Melo <acme@xconectiva.com.br>
* David S. Miller (davem@redhat.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/jiffies.h>
#include <linux/netdevice.h>
#include <linux/net.h>
#include <linux/inetdevice.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/hash.h>
#include <linux/sort.h>
#include <linux/file.h>
#include <linux/nsproxy.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/dst.h>
#include <net/ip.h>
#include <net/udp.h>
#include <net/xfrm.h>
#include <asm/byteorder.h>
#include <asm/atomic.h>
#include "l2tp_core.h"
#define L2TP_DRV_VERSION "V2.0"
/* L2TP header constants */
#define L2TP_HDRFLAG_T 0x8000
#define L2TP_HDRFLAG_L 0x4000
#define L2TP_HDRFLAG_S 0x0800
#define L2TP_HDRFLAG_O 0x0200
#define L2TP_HDRFLAG_P 0x0100
#define L2TP_HDR_VER_MASK 0x000F
#define L2TP_HDR_VER_2 0x0002
/* L2TPv3 default L2-specific sublayer */
#define L2TP_SLFLAG_S 0x40000000
#define L2TP_SL_SEQ_MASK 0x00ffffff
#define L2TP_HDR_SIZE_SEQ 10
#define L2TP_HDR_SIZE_NOSEQ 6
/* Default trace flags */
#define L2TP_DEFAULT_DEBUG_FLAGS 0
#define PRINTK(_mask, _type, _lvl, _fmt, args...) \
do { \
if ((_mask) & (_type)) \
printk(_lvl "L2TP: " _fmt, ##args); \
} while (0)
/* Private data stored for received packets in the skb.
*/
struct l2tp_skb_cb {
u16 ns;
u16 has_seq;
u16 length;
unsigned long expires;
};
#define L2TP_SKB_CB(skb) ((struct l2tp_skb_cb *) &skb->cb[sizeof(struct inet_skb_parm)])
static atomic_t l2tp_tunnel_count;
static atomic_t l2tp_session_count;
/* per-net private data for this module */
static unsigned int l2tp_net_id;
struct l2tp_net {
struct list_head l2tp_tunnel_list;
rwlock_t l2tp_tunnel_list_lock;
};
static inline struct l2tp_net *l2tp_pernet(struct net *net)
{
BUG_ON(!net);
return net_generic(net, l2tp_net_id);
}
/* Session hash list.
* The session_id SHOULD be random according to RFC2661, but several
* L2TP implementations (Cisco and Microsoft) use incrementing
* session_ids. So we do a real hash on the session_id, rather than a
* simple bitmask.
*/
static inline struct hlist_head *
l2tp_session_id_hash(struct l2tp_tunnel *tunnel, u32 session_id)
{
return &tunnel->session_hlist[hash_32(session_id, L2TP_HASH_BITS)];
}
/* Lookup a session by id
*/
struct l2tp_session *l2tp_session_find(struct l2tp_tunnel *tunnel, u32 session_id)
{
struct hlist_head *session_list =
l2tp_session_id_hash(tunnel, session_id);
struct l2tp_session *session;
struct hlist_node *walk;
read_lock_bh(&tunnel->hlist_lock);
hlist_for_each_entry(session, walk, session_list, hlist) {
if (session->session_id == session_id) {
read_unlock_bh(&tunnel->hlist_lock);
return session;
}
}
read_unlock_bh(&tunnel->hlist_lock);
return NULL;
}
EXPORT_SYMBOL_GPL(l2tp_session_find);
struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth)
{
int hash;
struct hlist_node *walk;
struct l2tp_session *session;
int count = 0;
read_lock_bh(&tunnel->hlist_lock);
for (hash = 0; hash < L2TP_HASH_SIZE; hash++) {
hlist_for_each_entry(session, walk, &tunnel->session_hlist[hash], hlist) {
if (++count > nth) {
read_unlock_bh(&tunnel->hlist_lock);
return session;
}
}
}
read_unlock_bh(&tunnel->hlist_lock);
return NULL;
}
EXPORT_SYMBOL_GPL(l2tp_session_find_nth);
/* Lookup a tunnel by id
*/
struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id)
{
struct l2tp_tunnel *tunnel;
struct l2tp_net *pn = l2tp_pernet(net);
read_lock_bh(&pn->l2tp_tunnel_list_lock);
list_for_each_entry(tunnel, &pn->l2tp_tunnel_list, list) {
if (tunnel->tunnel_id == tunnel_id) {
read_unlock_bh(&pn->l2tp_tunnel_list_lock);
return tunnel;
}
}
read_unlock_bh(&pn->l2tp_tunnel_list_lock);
return NULL;
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_find);
struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth)
{
struct l2tp_net *pn = l2tp_pernet(net);
struct l2tp_tunnel *tunnel;
int count = 0;
read_lock_bh(&pn->l2tp_tunnel_list_lock);
list_for_each_entry(tunnel, &pn->l2tp_tunnel_list, list) {
if (++count > nth) {
read_unlock_bh(&pn->l2tp_tunnel_list_lock);
return tunnel;
}
}
read_unlock_bh(&pn->l2tp_tunnel_list_lock);
return NULL;
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_find_nth);
/*****************************************************************************
* Receive data handling
*****************************************************************************/
/* Queue a skb in order. We come here only if the skb has an L2TP sequence
* number.
*/
static void l2tp_recv_queue_skb(struct l2tp_session *session, struct sk_buff *skb)
{
struct sk_buff *skbp;
struct sk_buff *tmp;
u16 ns = L2TP_SKB_CB(skb)->ns;
spin_lock_bh(&session->reorder_q.lock);
skb_queue_walk_safe(&session->reorder_q, skbp, tmp) {
if (L2TP_SKB_CB(skbp)->ns > ns) {
__skb_queue_before(&session->reorder_q, skbp, skb);
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
"%s: pkt %hu, inserted before %hu, reorder_q len=%d\n",
session->name, ns, L2TP_SKB_CB(skbp)->ns,
skb_queue_len(&session->reorder_q));
session->stats.rx_oos_packets++;
goto out;
}
}
__skb_queue_tail(&session->reorder_q, skb);
out:
spin_unlock_bh(&session->reorder_q.lock);
}
/* Dequeue a single skb.
*/
static void l2tp_recv_dequeue_skb(struct l2tp_session *session, struct sk_buff *skb)
{
struct l2tp_tunnel *tunnel = session->tunnel;
int length = L2TP_SKB_CB(skb)->length;
/* We're about to requeue the skb, so return resources
* to its current owner (a socket receive buffer).
*/
skb_orphan(skb);
tunnel->stats.rx_packets++;
tunnel->stats.rx_bytes += length;
session->stats.rx_packets++;
session->stats.rx_bytes += length;
if (L2TP_SKB_CB(skb)->has_seq) {
/* Bump our Nr */
session->nr++;
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
"%s: updated nr to %hu\n", session->name, session->nr);
}
/* call private receive handler */
if (session->recv_skb != NULL)
(*session->recv_skb)(session, skb, L2TP_SKB_CB(skb)->length);
else
kfree_skb(skb);
if (session->deref)
(*session->deref)(session);
}
/* Dequeue skbs from the session's reorder_q, subject to packet order.
* Skbs that have been in the queue for too long are simply discarded.
*/
static void l2tp_recv_dequeue(struct l2tp_session *session)
{
struct sk_buff *skb;
struct sk_buff *tmp;
/* If the pkt at the head of the queue has the nr that we
* expect to send up next, dequeue it and any other
* in-sequence packets behind it.
*/
spin_lock_bh(&session->reorder_q.lock);
skb_queue_walk_safe(&session->reorder_q, skb, tmp) {
if (time_after(jiffies, L2TP_SKB_CB(skb)->expires)) {
session->stats.rx_seq_discards++;
session->stats.rx_errors++;
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
"%s: oos pkt %hu len %d discarded (too old), "
"waiting for %hu, reorder_q_len=%d\n",
session->name, L2TP_SKB_CB(skb)->ns,
L2TP_SKB_CB(skb)->length, session->nr,
skb_queue_len(&session->reorder_q));
__skb_unlink(skb, &session->reorder_q);
kfree_skb(skb);
if (session->deref)
(*session->deref)(session);
continue;
}
if (L2TP_SKB_CB(skb)->has_seq) {
if (L2TP_SKB_CB(skb)->ns != session->nr) {
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
"%s: holding oos pkt %hu len %d, "
"waiting for %hu, reorder_q_len=%d\n",
session->name, L2TP_SKB_CB(skb)->ns,
L2TP_SKB_CB(skb)->length, session->nr,
skb_queue_len(&session->reorder_q));
goto out;
}
}
__skb_unlink(skb, &session->reorder_q);
/* Process the skb. We release the queue lock while we
* do so to let other contexts process the queue.
*/
spin_unlock_bh(&session->reorder_q.lock);
l2tp_recv_dequeue_skb(session, skb);
spin_lock_bh(&session->reorder_q.lock);
}
out:
spin_unlock_bh(&session->reorder_q.lock);
}
static inline int l2tp_verify_udp_checksum(struct sock *sk,
struct sk_buff *skb)
{
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)))
return 0;
skb->csum = psum;
return __skb_checksum_complete(skb);
}
/* Internal UDP receive frame. Do the real work of receiving an L2TP data frame
* here. The skb is not on a list when we get here.
* Returns 0 if the packet was a data packet and was successfully passed on.
* Returns 1 if the packet was not a good data packet and could not be
* forwarded. All such packets are passed up to userspace to deal with.
*/
int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,
int (*payload_hook)(struct sk_buff *skb))
{
struct l2tp_session *session = NULL;
unsigned char *ptr, *optr;
u16 hdrflags;
u32 tunnel_id, session_id;
int length;
int offset;
u16 version;
u16 ns, nr;
if (tunnel->sock && l2tp_verify_udp_checksum(tunnel->sock, skb))
goto discard_bad_csum;
/* UDP always verifies the packet length. */
__skb_pull(skb, sizeof(struct udphdr));
/* Short packet? */
if (!pskb_may_pull(skb, L2TP_HDR_SIZE_SEQ)) {
PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO,
"%s: recv short packet (len=%d)\n", tunnel->name, skb->len);
goto error;
}
/* Point to L2TP header */
optr = ptr = skb->data;
/* Trace packet contents, if enabled */
if (tunnel->debug & L2TP_MSG_DATA) {
length = min(32u, skb->len);
if (!pskb_may_pull(skb, length))
goto error;
printk(KERN_DEBUG "%s: recv: ", tunnel->name);
offset = 0;
do {
printk(" %02X", ptr[offset]);
} while (++offset < length);
printk("\n");
}
/* Get L2TP header flags */
hdrflags = ntohs(*(__be16 *)ptr);
/* Check protocol version */
version = hdrflags & L2TP_HDR_VER_MASK;
if (version != tunnel->version) {
PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO,
"%s: recv protocol version mismatch: got %d expected %d\n",
tunnel->name, version, tunnel->version);
goto error;
}
/* Get length of L2TP packet */
length = skb->len;
/* If type is control packet, it is handled by userspace. */
if (hdrflags & L2TP_HDRFLAG_T) {
PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_DEBUG,
"%s: recv control packet, len=%d\n", tunnel->name, length);
goto error;
}
/* Skip flags */
ptr += 2;
/* If length is present, skip it */
if (hdrflags & L2TP_HDRFLAG_L)
ptr += 2;
/* Extract tunnel and session ID */
tunnel_id = ntohs(*(__be16 *) ptr);
ptr += 2;
session_id = ntohs(*(__be16 *) ptr);
ptr += 2;
/* Find the session context */
session = l2tp_session_find(tunnel, session_id);
if (!session) {
/* Not found? Pass to userspace to deal with */
PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_INFO,
"%s: no session found (%hu/%hu). Passing up.\n",
tunnel->name, tunnel_id, session_id);
goto error;
}
/* The ref count is increased since we now hold a pointer to
* the session. Take care to decrement the refcnt when exiting
* this function from now on...
*/
l2tp_session_inc_refcount(session);
if (session->ref)
(*session->ref)(session);
/* Handle the optional sequence numbers. Sequence numbers are
* in different places for L2TPv2 and L2TPv3.
*
* If we are the LAC, enable/disable sequence numbers under
* the control of the LNS. If no sequence numbers present but
* we were expecting them, discard frame.
*/
ns = nr = 0;
L2TP_SKB_CB(skb)->has_seq = 0;
if (hdrflags & L2TP_HDRFLAG_S) {
ns = (u16) ntohs(*(__be16 *) ptr);
ptr += 2;
nr = ntohs(*(__be16 *) ptr);
ptr += 2;
/* Store L2TP info in the skb */
L2TP_SKB_CB(skb)->ns = ns;
L2TP_SKB_CB(skb)->has_seq = 1;
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
"%s: recv data ns=%hu, nr=%hu, session nr=%hu\n",
session->name, ns, nr, session->nr);
}
if (L2TP_SKB_CB(skb)->has_seq) {
/* Received a packet with sequence numbers. If we're the LNS,
* check if we sre sending sequence numbers and if not,
* configure it so.
*/
if ((!session->lns_mode) && (!session->send_seq)) {
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_INFO,
"%s: requested to enable seq numbers by LNS\n",
session->name);
session->send_seq = -1;
}
} else {
/* No sequence numbers.
* If user has configured mandatory sequence numbers, discard.
*/
if (session->recv_seq) {
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_WARNING,
"%s: recv data has no seq numbers when required. "
"Discarding\n", session->name);
session->stats.rx_seq_discards++;
goto discard;
}
/* If we're the LAC and we're sending sequence numbers, the
* LNS has requested that we no longer send sequence numbers.
* If we're the LNS and we're sending sequence numbers, the
* LAC is broken. Discard the frame.
*/
if ((!session->lns_mode) && (session->send_seq)) {
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_INFO,
"%s: requested to disable seq numbers by LNS\n",
session->name);
session->send_seq = 0;
} else if (session->send_seq) {
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_WARNING,
"%s: recv data has no seq numbers when required. "
"Discarding\n", session->name);
session->stats.rx_seq_discards++;
goto discard;
}
}
/* If offset bit set, skip it. */
if (hdrflags & L2TP_HDRFLAG_O) {
offset = ntohs(*(__be16 *)ptr);
ptr += 2 + offset;
}
offset = ptr - optr;
if (!pskb_may_pull(skb, offset))
goto discard;
__skb_pull(skb, offset);
/* If caller wants to process the payload before we queue the
* packet, do so now.
*/
if (payload_hook)
if ((*payload_hook)(skb))
goto discard;
/* Prepare skb for adding to the session's reorder_q. Hold
* packets for max reorder_timeout or 1 second if not
* reordering.
*/
L2TP_SKB_CB(skb)->length = length;
L2TP_SKB_CB(skb)->expires = jiffies +
(session->reorder_timeout ? session->reorder_timeout : HZ);
/* Add packet to the session's receive queue. Reordering is done here, if
* enabled. Saved L2TP protocol info is stored in skb->sb[].
*/
if (L2TP_SKB_CB(skb)->has_seq) {
if (session->reorder_timeout != 0) {
/* Packet reordering enabled. Add skb to session's
* reorder queue, in order of ns.
*/
l2tp_recv_queue_skb(session, skb);
} else {
/* Packet reordering disabled. Discard out-of-sequence
* packets
*/
if (L2TP_SKB_CB(skb)->ns != session->nr) {
session->stats.rx_seq_discards++;
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
"%s: oos pkt %hu len %d discarded, "
"waiting for %hu, reorder_q_len=%d\n",
session->name, L2TP_SKB_CB(skb)->ns,
L2TP_SKB_CB(skb)->length, session->nr,
skb_queue_len(&session->reorder_q));
goto discard;
}
skb_queue_tail(&session->reorder_q, skb);
}
} else {
/* No sequence numbers. Add the skb to the tail of the
* reorder queue. This ensures that it will be
* delivered after all previous sequenced skbs.
*/
skb_queue_tail(&session->reorder_q, skb);
}
/* Try to dequeue as many skbs from reorder_q as we can. */
l2tp_recv_dequeue(session);
l2tp_session_dec_refcount(session);
return 0;
discard:
session->stats.rx_errors++;
kfree_skb(skb);
if (session->deref)
(*session->deref)(session);
l2tp_session_dec_refcount(session);
return 0;
discard_bad_csum:
LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name);
UDP_INC_STATS_USER(tunnel->l2tp_net, UDP_MIB_INERRORS, 0);
tunnel->stats.rx_errors++;
kfree_skb(skb);
return 0;
error:
/* Put UDP header back */
__skb_push(skb, sizeof(struct udphdr));
return 1;
}
EXPORT_SYMBOL_GPL(l2tp_udp_recv_core);
/* UDP encapsulation receive handler. See net/ipv4/udp.c.
* Return codes:
* 0 : success.
* <0: error
* >0: skb should be passed up to userspace as UDP.
*/
int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
{
struct l2tp_tunnel *tunnel;
tunnel = l2tp_sock_to_tunnel(sk);
if (tunnel == NULL)
goto pass_up;
PRINTK(tunnel->debug, L2TP_MSG_DATA, KERN_DEBUG,
"%s: received %d bytes\n", tunnel->name, skb->len);
if (l2tp_udp_recv_core(tunnel, skb, tunnel->recv_payload_hook))
goto pass_up_put;
sock_put(sk);
return 0;
pass_up_put:
sock_put(sk);
pass_up:
return 1;
}
EXPORT_SYMBOL_GPL(l2tp_udp_encap_recv);
/************************************************************************
* Transmit handling
***********************************************************************/
/* Build an L2TP header for the session into the buffer provided.
*/
static void l2tp_build_l2tpv2_header(struct l2tp_tunnel *tunnel,
struct l2tp_session *session,
void *buf)
{
__be16 *bufp = buf;
u16 flags = L2TP_HDR_VER_2;
u32 tunnel_id = tunnel->peer_tunnel_id;
u32 session_id = session->peer_session_id;
if (session->send_seq)
flags |= L2TP_HDRFLAG_S;
/* Setup L2TP header. */
*bufp++ = htons(flags);
*bufp++ = htons(tunnel_id);
*bufp++ = htons(session_id);
if (session->send_seq) {
*bufp++ = htons(session->ns);
*bufp++ = 0;
session->ns++;
PRINTK(session->debug, L2TP_MSG_SEQ, KERN_DEBUG,
"%s: updated ns to %hu\n", session->name, session->ns);
}
}
void l2tp_build_l2tp_header(struct l2tp_session *session, void *buf)
{
struct l2tp_tunnel *tunnel = session->tunnel;
BUG_ON(tunnel->version != L2TP_HDR_VER_2);
l2tp_build_l2tpv2_header(tunnel, session, buf);
}
EXPORT_SYMBOL_GPL(l2tp_build_l2tp_header);
int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, size_t data_len)
{
struct l2tp_tunnel *tunnel = session->tunnel;
unsigned int len = skb->len;
int error;
/* Debug */
if (session->send_seq)
PRINTK(session->debug, L2TP_MSG_DATA, KERN_DEBUG,
"%s: send %Zd bytes, ns=%hu\n", session->name,
data_len, session->ns - 1);
else
PRINTK(session->debug, L2TP_MSG_DATA, KERN_DEBUG,
"%s: send %Zd bytes\n", session->name, data_len);
if (session->debug & L2TP_MSG_DATA) {
int i;
unsigned char *datap = skb->data + sizeof(struct udphdr);
printk(KERN_DEBUG "%s: xmit:", session->name);
for (i = 0; i < (len - sizeof(struct udphdr)); i++) {
printk(" %02X", *datap++);
if (i == 31) {
printk(" ...");
break;
}
}
printk("\n");
}
/* Queue the packet to IP for output */
error = ip_queue_xmit(skb, 1);
/* Update stats */
if (error >= 0) {
tunnel->stats.tx_packets++;
tunnel->stats.tx_bytes += len;
session->stats.tx_packets++;
session->stats.tx_bytes += len;
} else {
tunnel->stats.tx_errors++;
session->stats.tx_errors++;
}
return 0;
}
EXPORT_SYMBOL_GPL(l2tp_xmit_core);
/* Automatically called when the skb is freed.
*/
static void l2tp_sock_wfree(struct sk_buff *skb)
{
sock_put(skb->sk);
}
/* For data skbs that we transmit, we associate with the tunnel socket
* but don't do accounting.
*/
static inline void l2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
{
sock_hold(sk);
skb->sk = sk;
skb->destructor = l2tp_sock_wfree;
}
/* If caller requires the skb to have a ppp header, the header must be
* inserted in the skb data before calling this function.
*/
int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len)
{
int data_len = skb->len;
struct sock *sk = session->tunnel->sock;
struct udphdr *uh;
unsigned int udp_len;
struct inet_sock *inet;
__wsum csum;
int old_headroom;
int new_headroom;
int headroom;
/* Check that there's enough headroom in the skb to insert IP,
* UDP and L2TP headers. If not enough, expand it to
* make room. Adjust truesize.
*/
headroom = NET_SKB_PAD + sizeof(struct iphdr) +
sizeof(struct udphdr) + hdr_len;
old_headroom = skb_headroom(skb);
if (skb_cow_head(skb, headroom))
goto abort;
new_headroom = skb_headroom(skb);
skb_orphan(skb);
skb->truesize += new_headroom - old_headroom;
/* Setup L2TP header */
l2tp_build_l2tp_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;
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
IPSKB_REROUTED);
nf_reset(skb);
/* Get routing info from the tunnel socket */
skb_dst_drop(skb);
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 */
if (sk->sk_no_check == UDP_CSUM_NOXMIT)
skb->ip_summed = CHECKSUM_NONE;
else if ((skb_dst(skb) && skb_dst(skb)->dev) &&
(!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) {
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);
}
l2tp_xmit_core(session, skb, data_len);
abort:
return 0;
}
EXPORT_SYMBOL_GPL(l2tp_xmit_skb);
/*****************************************************************************
* Tinnel and session create/destroy.
*****************************************************************************/
/* Tunnel socket destruct hook.
* The tunnel context is deleted only when all session sockets have been
* closed.
*/
void l2tp_tunnel_destruct(struct sock *sk)
{
struct l2tp_tunnel *tunnel;
tunnel = sk->sk_user_data;
if (tunnel == NULL)
goto end;
PRINTK(tunnel->debug, L2TP_MSG_CONTROL, KERN_INFO,
"%s: closing...\n", tunnel->name);
/* Close all sessions */
l2tp_tunnel_closeall(tunnel);
/* No longer an encapsulation socket. See net/ipv4/udp.c */
(udp_sk(sk))->encap_type = 0;
(udp_sk(sk))->encap_rcv = NULL;
/* Remove hooks into tunnel socket */
tunnel->sock = NULL;
sk->sk_destruct = tunnel->old_sk_destruct;
sk->sk_user_data = NULL;
/* Call the original destructor */
if (sk->sk_destruct)
(*sk->sk_destruct)(sk);
/* We're finished with the socket */
l2tp_tunnel_dec_refcount(tunnel);
end:
return;
}
EXPORT_SYMBOL(l2tp_tunnel_destruct);
/* When the tunnel is closed, all the attached sessions need to go too.
*/
void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel)
{
int hash;
struct hlist_node *walk;
struct hlist_node *tmp;
struct l2tp_session *session;
BUG_ON(tunnel == NULL);
PRINTK(tunnel->debug, L2TP_MSG_CONTROL, KERN_INFO,
"%s: closing all sessions...\n", tunnel->name);
write_lock_bh(&tunnel->hlist_lock);
for (hash = 0; hash < L2TP_HASH_SIZE; hash++) {
again:
hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) {
session = hlist_entry(walk, struct l2tp_session, hlist);
PRINTK(session->debug, L2TP_MSG_CONTROL, KERN_INFO,
"%s: closing session\n", session->name);
hlist_del_init(&session->hlist);
/* Since we should hold the sock lock while
* doing any unbinding, we need to release the
* lock we're holding before taking that lock.
* Hold a reference to the sock so it doesn't
* disappear as we're jumping between locks.
*/
if (session->ref != NULL)
(*session->ref)(session);
write_unlock_bh(&tunnel->hlist_lock);
if (session->session_close != NULL)
(*session->session_close)(session);
if (session->deref != NULL)
(*session->deref)(session);
write_lock_bh(&tunnel->hlist_lock);
/* Now restart from the beginning of this hash
* chain. We always remove a session from the
* list so we are guaranteed to make forward
* progress.
*/
goto again;
}
}
write_unlock_bh(&tunnel->hlist_lock);
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_closeall);
/* Really kill the tunnel.
* Come here only when all sessions have been cleared from the tunnel.
*/
void l2tp_tunnel_free(struct l2tp_tunnel *tunnel)
{
struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net);
BUG_ON(atomic_read(&tunnel->ref_count) != 0);
BUG_ON(tunnel->sock != NULL);
PRINTK(tunnel->debug, L2TP_MSG_CONTROL, KERN_INFO,
"%s: free...\n", tunnel->name);
/* Remove from tunnel list */
write_lock_bh(&pn->l2tp_tunnel_list_lock);
list_del_init(&tunnel->list);
write_unlock_bh(&pn->l2tp_tunnel_list_lock);
atomic_dec(&l2tp_tunnel_count);
kfree(tunnel);
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_free);
int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp)
{
struct l2tp_tunnel *tunnel = NULL;
int err;
struct socket *sock = NULL;
struct sock *sk = NULL;
struct l2tp_net *pn;
/* Get the tunnel socket from the fd, which was opened by
* the userspace L2TP daemon.
*/
err = -EBADF;
sock = sockfd_lookup(fd, &err);
if (!sock) {
printk(KERN_ERR "tunl %hu: sockfd_lookup(fd=%d) returned %d\n",
tunnel_id, fd, err);
goto err;
}
sk = sock->sk;
/* Quick sanity checks */
err = -EPROTONOSUPPORT;
if (sk->sk_protocol != IPPROTO_UDP) {
printk(KERN_ERR "tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP);
goto err;
}
err = -EAFNOSUPPORT;
if (sock->ops->family != AF_INET) {
printk(KERN_ERR "tunl %hu: fd %d wrong family, got %d, expected %d\n",
tunnel_id, fd, sock->ops->family, AF_INET);
goto err;
}
/* Check if this socket has already been prepped */
tunnel = (struct l2tp_tunnel *)sk->sk_user_data;
if (tunnel != NULL) {
/* This socket has already been prepped */
err = -EBUSY;
goto err;
}
if (version != L2TP_HDR_VER_2)
goto err;
tunnel = kzalloc(sizeof(struct l2tp_tunnel), GFP_KERNEL);
if (tunnel == NULL) {
err = -ENOMEM;
goto err;
}
tunnel->version = version;
tunnel->tunnel_id = tunnel_id;
tunnel->peer_tunnel_id = peer_tunnel_id;
tunnel->debug = L2TP_DEFAULT_DEBUG_FLAGS;
tunnel->magic = L2TP_TUNNEL_MAGIC;
sprintf(&tunnel->name[0], "tunl %u", tunnel_id);
rwlock_init(&tunnel->hlist_lock);
/* The net we belong to */
tunnel->l2tp_net = net;
pn = l2tp_pernet(net);
if (cfg)
tunnel->debug = cfg->debug;
/* 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;
/* Hook on the tunnel socket destructor so that we can cleanup
* if the tunnel socket goes away.
*/
tunnel->old_sk_destruct = sk->sk_destruct;
sk->sk_destruct = &l2tp_tunnel_destruct;
tunnel->sock = sk;
sk->sk_allocation = GFP_ATOMIC;
/* Add tunnel to our list */
INIT_LIST_HEAD(&tunnel->list);
write_lock_bh(&pn->l2tp_tunnel_list_lock);
list_add(&tunnel->list, &pn->l2tp_tunnel_list);
write_unlock_bh(&pn->l2tp_tunnel_list_lock);
atomic_inc(&l2tp_tunnel_count);
/* Bump the reference count. The tunnel context is deleted
* only when this drops to zero.
*/
l2tp_tunnel_inc_refcount(tunnel);
err = 0;
err:
if (tunnelp)
*tunnelp = tunnel;
if (sock)
sockfd_put(sock);
return err;
}
EXPORT_SYMBOL_GPL(l2tp_tunnel_create);
/* Really kill the session.
*/
void l2tp_session_free(struct l2tp_session *session)
{
struct l2tp_tunnel *tunnel;
BUG_ON(atomic_read(&session->ref_count) != 0);
tunnel = session->tunnel;
if (tunnel != NULL) {
BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);
/* Delete the session from the hash */
write_lock_bh(&tunnel->hlist_lock);
hlist_del_init(&session->hlist);
write_unlock_bh(&tunnel->hlist_lock);
if (session->session_id != 0)
atomic_dec(&l2tp_session_count);
sock_put(tunnel->sock);
/* This will delete the tunnel context if this
* is the last session on the tunnel.
*/
session->tunnel = NULL;
l2tp_tunnel_dec_refcount(tunnel);
}
kfree(session);
return;
}
EXPORT_SYMBOL_GPL(l2tp_session_free);
struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)
{
struct l2tp_session *session;
session = kzalloc(sizeof(struct l2tp_session) + priv_size, GFP_KERNEL);
if (session != NULL) {
session->magic = L2TP_SESSION_MAGIC;
session->tunnel = tunnel;
session->session_id = session_id;
session->peer_session_id = peer_session_id;
sprintf(&session->name[0], "sess %u/%u",
tunnel->tunnel_id, session->session_id);
skb_queue_head_init(&session->reorder_q);
INIT_HLIST_NODE(&session->hlist);
/* Inherit debug options from tunnel */
session->debug = tunnel->debug;
if (cfg) {
session->debug = cfg->debug;
session->hdr_len = cfg->hdr_len;
session->mtu = cfg->mtu;
session->mru = cfg->mru;
session->send_seq = cfg->send_seq;
session->recv_seq = cfg->recv_seq;
session->lns_mode = cfg->lns_mode;
}
/* Bump the reference count. The session context is deleted
* only when this drops to zero.
*/
l2tp_session_inc_refcount(session);
l2tp_tunnel_inc_refcount(tunnel);
/* Ensure tunnel socket isn't deleted */
sock_hold(tunnel->sock);
/* Add session to the tunnel's hash list */
write_lock_bh(&tunnel->hlist_lock);
hlist_add_head(&session->hlist,
l2tp_session_id_hash(tunnel, session_id));
write_unlock_bh(&tunnel->hlist_lock);
/* Ignore management session in session count value */
if (session->session_id != 0)
atomic_inc(&l2tp_session_count);
}
return session;
}
EXPORT_SYMBOL_GPL(l2tp_session_create);
/*****************************************************************************
* Init and cleanup
*****************************************************************************/
static __net_init int l2tp_init_net(struct net *net)
{
struct l2tp_net *pn;
int err;
pn = kzalloc(sizeof(*pn), GFP_KERNEL);
if (!pn)
return -ENOMEM;
INIT_LIST_HEAD(&pn->l2tp_tunnel_list);
rwlock_init(&pn->l2tp_tunnel_list_lock);
err = net_assign_generic(net, l2tp_net_id, pn);
if (err)
goto out;
return 0;
out:
kfree(pn);
return err;
}
static __net_exit void l2tp_exit_net(struct net *net)
{
struct l2tp_net *pn;
pn = net_generic(net, l2tp_net_id);
/*
* if someone has cached our net then
* further net_generic call will return NULL
*/
net_assign_generic(net, l2tp_net_id, NULL);
kfree(pn);
}
static struct pernet_operations l2tp_net_ops = {
.init = l2tp_init_net,
.exit = l2tp_exit_net,
.id = &l2tp_net_id,
.size = sizeof(struct l2tp_net),
};
static int __init l2tp_init(void)
{
int rc = 0;
rc = register_pernet_device(&l2tp_net_ops);
if (rc)
goto out;
printk(KERN_INFO "L2TP core driver, %s\n", L2TP_DRV_VERSION);
out:
return rc;
}
static void __exit l2tp_exit(void)
{
unregister_pernet_device(&l2tp_net_ops);
}
module_init(l2tp_init);
module_exit(l2tp_exit);
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("L2TP core");
MODULE_LICENSE("GPL");
MODULE_VERSION(L2TP_DRV_VERSION);
/*
* L2TP internal definitions.
*
* Copyright (c) 2008,2009 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 version 2 as
* published by the Free Software Foundation.
*/
#ifndef _L2TP_CORE_H_
#define _L2TP_CORE_H_
/* Just some random numbers */
#define L2TP_TUNNEL_MAGIC 0x42114DDA
#define L2TP_SESSION_MAGIC 0x0C04EB7D
#define L2TP_HASH_BITS 4
#define L2TP_HASH_SIZE (1 << L2TP_HASH_BITS)
/* Debug message categories for the DEBUG socket option */
enum {
L2TP_MSG_DEBUG = (1 << 0), /* verbose debug (if
* compiled in) */
L2TP_MSG_CONTROL = (1 << 1), /* userspace - kernel
* interface */
L2TP_MSG_SEQ = (1 << 2), /* sequence numbers */
L2TP_MSG_DATA = (1 << 3), /* data packets */
};
struct sk_buff;
struct l2tp_stats {
u64 tx_packets;
u64 tx_bytes;
u64 tx_errors;
u64 rx_packets;
u64 rx_bytes;
u64 rx_seq_discards;
u64 rx_oos_packets;
u64 rx_errors;
};
struct l2tp_tunnel;
/* Describes a session. Contains information to determine incoming
* packets and transmit outgoing ones.
*/
struct l2tp_session_cfg {
unsigned data_seq:2; /* data sequencing level
* 0 => none, 1 => IP only,
* 2 => all
*/
unsigned recv_seq:1; /* expect receive packets with
* sequence numbers? */
unsigned send_seq:1; /* send packets with sequence
* numbers? */
unsigned lns_mode:1; /* behave as LNS? LAC enables
* sequence numbers under
* control of LNS. */
int debug; /* bitmask of debug message
* categories */
int offset; /* offset to payload */
int reorder_timeout; /* configured reorder timeout
* (in jiffies) */
int mtu;
int mru;
int hdr_len;
};
struct l2tp_session {
int magic; /* should be
* L2TP_SESSION_MAGIC */
struct l2tp_tunnel *tunnel; /* back pointer to tunnel
* context */
u32 session_id;
u32 peer_session_id;
u16 nr; /* session NR state (receive) */
u16 ns; /* session NR state (send) */
struct sk_buff_head reorder_q; /* receive reorder queue */
struct hlist_node hlist; /* Hash list node */
atomic_t ref_count;
char name[32]; /* for logging */
unsigned data_seq:2; /* data sequencing level
* 0 => none, 1 => IP only,
* 2 => all
*/
unsigned recv_seq:1; /* expect receive packets with
* sequence numbers? */
unsigned send_seq:1; /* send packets with sequence
* numbers? */
unsigned lns_mode:1; /* behave as LNS? LAC enables
* sequence numbers under
* control of LNS. */
int debug; /* bitmask of debug message
* categories */
int reorder_timeout; /* configured reorder timeout
* (in jiffies) */
int mtu;
int mru;
int hdr_len;
struct l2tp_stats stats;
void (*recv_skb)(struct l2tp_session *session, struct sk_buff *skb, int data_len);
void (*session_close)(struct l2tp_session *session);
void (*ref)(struct l2tp_session *session);
void (*deref)(struct l2tp_session *session);
uint8_t priv[0]; /* private data */
};
/* Describes the tunnel. It contains info to track all the associated
* sessions so incoming packets can be sorted out
*/
struct l2tp_tunnel_cfg {
int debug; /* bitmask of debug message
* categories */
};
struct l2tp_tunnel {
int magic; /* Should be L2TP_TUNNEL_MAGIC */
rwlock_t hlist_lock; /* protect session_hlist */
struct hlist_head session_hlist[L2TP_HASH_SIZE];
/* hashed list of sessions,
* hashed by id */
u32 tunnel_id;
u32 peer_tunnel_id;
int version; /* 2=>L2TPv2, 3=>L2TPv3 */
char name[20]; /* for logging */
int debug; /* bitmask of debug message
* categories */
int hdr_len;
struct l2tp_stats stats;
struct list_head list; /* Keep a list of all tunnels */
struct net *l2tp_net; /* the net we belong to */
atomic_t ref_count;
int (*recv_payload_hook)(struct sk_buff *skb);
void (*old_sk_destruct)(struct sock *);
struct sock *sock; /* Parent socket */
int fd;
uint8_t priv[0]; /* private data */
};
static inline void *l2tp_tunnel_priv(struct l2tp_tunnel *tunnel)
{
return &tunnel->priv[0];
}
static inline void *l2tp_session_priv(struct l2tp_session *session)
{
return &session->priv[0];
}
static inline struct l2tp_tunnel *l2tp_sock_to_tunnel(struct sock *sk)
{
struct l2tp_tunnel *tunnel;
if (sk == NULL)
return NULL;
sock_hold(sk);
tunnel = (struct l2tp_tunnel *)(sk->sk_user_data);
if (tunnel == NULL) {
sock_put(sk);
goto out;
}
BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);
out:
return tunnel;
}
extern struct l2tp_session *l2tp_session_find(struct l2tp_tunnel *tunnel, u32 session_id);
extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth);
extern struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
extern struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
extern int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 peer_tunnel_id, struct l2tp_tunnel_cfg *cfg, struct l2tp_tunnel **tunnelp);
extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg);
extern void l2tp_tunnel_free(struct l2tp_tunnel *tunnel);
extern void l2tp_session_free(struct l2tp_session *session);
extern int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb, int (*payload_hook)(struct sk_buff *skb));
extern int l2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb);
extern void l2tp_build_l2tp_header(struct l2tp_session *session, void *buf);
extern int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb, size_t data_len);
extern int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len);
extern void l2tp_tunnel_destruct(struct sock *sk);
extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel);
/* Tunnel reference counts. Incremented per session that is added to
* the tunnel.
*/
static inline void l2tp_tunnel_inc_refcount_1(struct l2tp_tunnel *tunnel)
{
atomic_inc(&tunnel->ref_count);
}
static inline void l2tp_tunnel_dec_refcount_1(struct l2tp_tunnel *tunnel)
{
if (atomic_dec_and_test(&tunnel->ref_count))
l2tp_tunnel_free(tunnel);
}
#ifdef L2TP_REFCNT_DEBUG
#define l2tp_tunnel_inc_refcount(_t) do { \
printk(KERN_DEBUG "l2tp_tunnel_inc_refcount: %s:%d %s: cnt=%d\n", __func__, __LINE__, (_t)->name, atomic_read(&_t->ref_count)); \
l2tp_tunnel_inc_refcount_1(_t); \
} while (0)
#define l2tp_tunnel_dec_refcount(_t) do { \
printk(KERN_DEBUG "l2tp_tunnel_dec_refcount: %s:%d %s: cnt=%d\n", __func__, __LINE__, (_t)->name, atomic_read(&_t->ref_count)); \
l2tp_tunnel_dec_refcount_1(_t); \
} while (0)
#else
#define l2tp_tunnel_inc_refcount(t) l2tp_tunnel_inc_refcount_1(t)
#define l2tp_tunnel_dec_refcount(t) l2tp_tunnel_dec_refcount_1(t)
#endif
/* Session reference counts. Incremented when code obtains a reference
* to a session.
*/
static inline void l2tp_session_inc_refcount_1(struct l2tp_session *session)
{
atomic_inc(&session->ref_count);
}
static inline void l2tp_session_dec_refcount_1(struct l2tp_session *session)
{
if (atomic_dec_and_test(&session->ref_count))
l2tp_session_free(session);
}
#ifdef L2TP_REFCNT_DEBUG
#define l2tp_session_inc_refcount(_s) do { \
printk(KERN_DEBUG "l2tp_session_inc_refcount: %s:%d %s: cnt=%d\n", __func__, __LINE__, (_s)->name, atomic_read(&_s->ref_count)); \
l2tp_session_inc_refcount_1(_s); \
} while (0)
#define l2tp_session_dec_refcount(_s) do { \
printk(KERN_DEBUG "l2tp_session_dec_refcount: %s:%d %s: cnt=%d\n", __func__, __LINE__, (_s)->name, atomic_read(&_s->ref_count)); \
l2tp_session_dec_refcount_1(_s); \
} while (0)
#else
#define l2tp_session_inc_refcount(s) l2tp_session_inc_refcount_1(s)
#define l2tp_session_dec_refcount(s) l2tp_session_dec_refcount_1(s)
#endif
#endif /* _L2TP_CORE_H_ */
......@@ -4,14 +4,11 @@
* PPPoX --- Generic PPP encapsulation socket family
* PPPoL2TP --- PPP over L2TP (RFC 2661)
*
* Version: 1.0.0
* Version: 2.0.0
*
* Authors: Martijn van Oosterhout <kleptog@svana.org>
* James Chapman (jchapman@katalix.com)
* Contributors:
* Michal Ostrowski <mostrows@speakeasy.net>
* Arnaldo Carvalho de Melo <acme@xconectiva.com.br>
* David S. Miller (davem@redhat.com)
* Authors: James Chapman (jchapman@katalix.com)
*
* Based on original work by Martijn van Oosterhout <kleptog@svana.org>
*
* License:
* This program is free software; you can redistribute it and/or
......@@ -63,7 +60,7 @@
#include <linux/module.h>
#include <linux/string.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <linux/uaccess.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
......@@ -101,37 +98,18 @@
#include <asm/byteorder.h>
#include <asm/atomic.h>
#include "l2tp_core.h"
#define PPPOL2TP_DRV_VERSION "V1.0"
/* L2TP header constants */
#define L2TP_HDRFLAG_T 0x8000
#define L2TP_HDRFLAG_L 0x4000
#define L2TP_HDRFLAG_S 0x0800
#define L2TP_HDRFLAG_O 0x0200
#define L2TP_HDRFLAG_P 0x0100
#define L2TP_HDR_VER_MASK 0x000F
#define L2TP_HDR_VER 0x0002
#define PPPOL2TP_DRV_VERSION "V2.0"
/* Space for UDP, L2TP and PPP headers */
#define PPPOL2TP_HEADER_OVERHEAD 40
/* Just some random numbers */
#define L2TP_TUNNEL_MAGIC 0x42114DDA
#define L2TP_SESSION_MAGIC 0x0C04EB7D
#define PPPOL2TP_HASH_BITS 4
#define PPPOL2TP_HASH_SIZE (1 << PPPOL2TP_HASH_BITS)
/* Default trace flags */
#define PPPOL2TP_DEFAULT_DEBUG_FLAGS 0
#define PRINTK(_mask, _type, _lvl, _fmt, args...) \
do { \
if ((_mask) & (_type)) \
printk(_lvl "PPPOL2TP: " _fmt, ##args); \
} while(0)
} while (0)
/* Number of bytes to build transmit L2TP headers.
* Unfortunately the size is different depending on whether sequence numbers
......@@ -140,285 +118,123 @@
#define PPPOL2TP_L2TP_HDR_SIZE_SEQ 10
#define PPPOL2TP_L2TP_HDR_SIZE_NOSEQ 6
struct pppol2tp_tunnel;
/* Describes a session. It is the sk_user_data field in the PPPoL2TP
* socket. Contains information to determine incoming packets and transmit
* outgoing ones.
/* Private data of each session. This data lives at the end of struct
* l2tp_session, referenced via session->priv[].
*/
struct pppol2tp_session
{
int magic; /* should be
* L2TP_SESSION_MAGIC */
struct pppol2tp_session {
int owner; /* pid that opened the socket */
struct sock *sock; /* Pointer to the session
* PPPoX socket */
struct sock *tunnel_sock; /* Pointer to the tunnel UDP
* socket */
struct pppol2tp_addr tunnel_addr; /* Description of tunnel */
struct pppol2tp_tunnel *tunnel; /* back pointer to tunnel
* context */
char name[20]; /* "sess xxxxx/yyyyy", where
* x=tunnel_id, y=session_id */
int mtu;
int mru;
int flags; /* accessed by PPPIOCGFLAGS.
* Unused. */
unsigned recv_seq:1; /* expect receive packets with
* sequence numbers? */
unsigned send_seq:1; /* send packets with sequence
* numbers? */
unsigned lns_mode:1; /* behave as LNS? LAC enables
* sequence numbers under
* control of LNS. */
int debug; /* bitmask of debug message
* categories */
int reorder_timeout; /* configured reorder timeout
* (in jiffies) */
u16 nr; /* session NR state (receive) */
u16 ns; /* session NR state (send) */
struct sk_buff_head reorder_q; /* receive reorder queue */
struct pppol2tp_ioc_stats stats;
struct hlist_node hlist; /* Hash list node */
};
/* The sk_user_data field of the tunnel's UDP socket. It contains info to track
* all the associated sessions so incoming packets can be sorted out
*/
struct pppol2tp_tunnel
{
int magic; /* Should be L2TP_TUNNEL_MAGIC */
rwlock_t hlist_lock; /* protect session_hlist */
struct hlist_head session_hlist[PPPOL2TP_HASH_SIZE];
/* hashed list of sessions,
* hashed by id */
int debug; /* bitmask of debug message
* categories */
char name[12]; /* "tunl xxxxx" */
struct pppol2tp_ioc_stats stats;
void (*old_sk_destruct)(struct sock *);
struct sock *sock; /* Parent socket */
struct list_head list; /* Keep a list of all open
* prepared sockets */
struct net *pppol2tp_net; /* the net we belong to */
atomic_t ref_count;
};
/* Private data stored for received packets in the skb.
*/
struct pppol2tp_skb_cb {
u16 ns;
u16 nr;
u16 has_seq;
u16 length;
unsigned long expires;
};
#define PPPOL2TP_SKB_CB(skb) ((struct pppol2tp_skb_cb *) &skb->cb[sizeof(struct inet_skb_parm)])
static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb);
static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel);
static atomic_t pppol2tp_tunnel_count;
static atomic_t pppol2tp_session_count;
static struct ppp_channel_ops pppol2tp_chan_ops = { pppol2tp_xmit , NULL };
static const struct proto_ops pppol2tp_ops;
/* per-net private data for this module */
static int pppol2tp_net_id __read_mostly;
struct pppol2tp_net {
struct list_head pppol2tp_tunnel_list;
rwlock_t pppol2tp_tunnel_list_lock;
};
static inline struct pppol2tp_net *pppol2tp_pernet(struct net *net)
{
BUG_ON(!net);
return net_generic(net, pppol2tp_net_id);
}
/* Helpers to obtain tunnel/session contexts from sockets.
*/
static inline struct pppol2tp_session *pppol2tp_sock_to_session(struct sock *sk)
static inline struct l2tp_session *pppol2tp_sock_to_session(struct sock *sk)
{
struct pppol2tp_session *session;
struct l2tp_session *session;
if (sk == NULL)
return NULL;
sock_hold(sk);
session = (struct pppol2tp_session *)(sk->sk_user_data);
session = (struct l2tp_session *)(sk->sk_user_data);
if (session == NULL) {
sock_put(sk);
goto out;
}
BUG_ON(session->magic != L2TP_SESSION_MAGIC);
out:
return session;
}
static inline struct pppol2tp_tunnel *pppol2tp_sock_to_tunnel(struct sock *sk)
{
struct pppol2tp_tunnel *tunnel;
if (sk == NULL)
return NULL;
sock_hold(sk);
tunnel = (struct pppol2tp_tunnel *)(sk->sk_user_data);
if (tunnel == NULL) {
sock_put(sk);
goto out;
}
BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);
out:
return tunnel;
}
/*****************************************************************************
* Receive data handling
*****************************************************************************/
/* Tunnel reference counts. Incremented per session that is added to
* the tunnel.
*/
static inline void pppol2tp_tunnel_inc_refcount(struct pppol2tp_tunnel *tunnel)
static int pppol2tp_recv_payload_hook(struct sk_buff *skb)
{
atomic_inc(&tunnel->ref_count);
}
/* Skip PPP header, if present. In testing, Microsoft L2TP clients
* don't send the PPP header (PPP header compression enabled), but
* other clients can include the header. So we cope with both cases
* here. The PPP header is always FF03 when using L2TP.
*
* Note that skb->data[] isn't dereferenced from a u16 ptr here since
* the field may be unaligned.
*/
if (!pskb_may_pull(skb, 2))
return 1;
static inline void pppol2tp_tunnel_dec_refcount(struct pppol2tp_tunnel *tunnel)
{
if (atomic_dec_and_test(&tunnel->ref_count))
pppol2tp_tunnel_free(tunnel);
}
if ((skb->data[0] == 0xff) && (skb->data[1] == 0x03))
skb_pull(skb, 2);
/* Session hash list.
* The session_id SHOULD be random according to RFC2661, but several
* L2TP implementations (Cisco and Microsoft) use incrementing
* session_ids. So we do a real hash on the session_id, rather than a
* simple bitmask.
*/
static inline struct hlist_head *
pppol2tp_session_id_hash(struct pppol2tp_tunnel *tunnel, u16 session_id)
{
unsigned long hash_val = (unsigned long) session_id;
return &tunnel->session_hlist[hash_long(hash_val, PPPOL2TP_HASH_BITS)];
return 0;
}
/* Lookup a session by id
/* Receive message. This is the recvmsg for the PPPoL2TP socket.
*/
static struct pppol2tp_session *
pppol2tp_session_find(struct pppol2tp_tunnel *tunnel, u16 session_id)
static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len,
int flags)
{
struct hlist_head *session_list =
pppol2tp_session_id_hash(tunnel, session_id);
struct pppol2tp_session *session;
struct hlist_node *walk;
read_lock_bh(&tunnel->hlist_lock);
hlist_for_each_entry(session, walk, session_list, hlist) {
if (session->tunnel_addr.s_session == session_id) {
read_unlock_bh(&tunnel->hlist_lock);
return session;
}
}
read_unlock_bh(&tunnel->hlist_lock);
return NULL;
}
int err;
struct sk_buff *skb;
struct sock *sk = sock->sk;
/* Lookup a tunnel by id
*/
static struct pppol2tp_tunnel *pppol2tp_tunnel_find(struct net *net, u16 tunnel_id)
{
struct pppol2tp_tunnel *tunnel;
struct pppol2tp_net *pn = pppol2tp_pernet(net);
read_lock_bh(&pn->pppol2tp_tunnel_list_lock);
list_for_each_entry(tunnel, &pn->pppol2tp_tunnel_list, list) {
if (tunnel->stats.tunnel_id == tunnel_id) {
read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
return tunnel;
}
}
read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
err = -EIO;
if (sk->sk_state & PPPOX_BOUND)
goto end;
return NULL;
}
msg->msg_namelen = 0;
/*****************************************************************************
* Receive data handling
*****************************************************************************/
err = 0;
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &err);
if (!skb)
goto end;
/* Queue a skb in order. We come here only if the skb has an L2TP sequence
* number.
*/
static void pppol2tp_recv_queue_skb(struct pppol2tp_session *session, struct sk_buff *skb)
{
struct sk_buff *skbp;
struct sk_buff *tmp;
u16 ns = PPPOL2TP_SKB_CB(skb)->ns;
spin_lock_bh(&session->reorder_q.lock);
skb_queue_walk_safe(&session->reorder_q, skbp, tmp) {
if (PPPOL2TP_SKB_CB(skbp)->ns > ns) {
__skb_queue_before(&session->reorder_q, skbp, skb);
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
"%s: pkt %hu, inserted before %hu, reorder_q len=%d\n",
session->name, ns, PPPOL2TP_SKB_CB(skbp)->ns,
skb_queue_len(&session->reorder_q));
session->stats.rx_oos_packets++;
goto out;
}
}
if (len > skb->len)
len = skb->len;
else if (len < skb->len)
msg->msg_flags |= MSG_TRUNC;
__skb_queue_tail(&session->reorder_q, skb);
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
if (likely(err == 0))
err = len;
out:
spin_unlock_bh(&session->reorder_q.lock);
kfree_skb(skb);
end:
return err;
}
/* Dequeue a single skb.
*/
static void pppol2tp_recv_dequeue_skb(struct pppol2tp_session *session, struct sk_buff *skb)
static void pppol2tp_recv(struct l2tp_session *session, struct sk_buff *skb, int data_len)
{
struct pppol2tp_tunnel *tunnel = session->tunnel;
int length = PPPOL2TP_SKB_CB(skb)->length;
struct sock *session_sock = NULL;
/* We're about to requeue the skb, so return resources
* to its current owner (a socket receive buffer).
*/
skb_orphan(skb);
tunnel->stats.rx_packets++;
tunnel->stats.rx_bytes += length;
session->stats.rx_packets++;
session->stats.rx_bytes += length;
if (PPPOL2TP_SKB_CB(skb)->has_seq) {
/* Bump our Nr */
session->nr++;
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
"%s: updated nr to %hu\n", session->name, session->nr);
}
struct pppol2tp_session *ps = l2tp_session_priv(session);
struct sock *sk = NULL;
/* If the socket is bound, send it in to PPP's input queue. Otherwise
* queue it on the session socket.
*/
session_sock = session->sock;
if (session_sock->sk_state & PPPOX_BOUND) {
sk = ps->sock;
if (sk == NULL)
goto no_sock;
if (sk->sk_state & PPPOX_BOUND) {
struct pppox_sock *po;
PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
"%s: recv %d byte data frame, passing to ppp\n",
session->name, length);
session->name, data_len);
/* We need to forget all info related to the L2TP packet
* gathered in the skb as we are going to reuse the same
......@@ -436,7 +252,7 @@ static void pppol2tp_recv_dequeue_skb(struct pppol2tp_session *session, struct s
skb_dst_drop(skb);
nf_reset(skb);
po = pppox_sk(session_sock);
po = pppox_sk(sk);
ppp_input(&po->chan, skb);
} else {
PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_INFO,
......@@ -447,408 +263,28 @@ static void pppol2tp_recv_dequeue_skb(struct pppol2tp_session *session, struct s
kfree_skb(skb);
}
sock_put(session->sock);
}
/* Dequeue skbs from the session's reorder_q, subject to packet order.
* Skbs that have been in the queue for too long are simply discarded.
*/
static void pppol2tp_recv_dequeue(struct pppol2tp_session *session)
{
struct sk_buff *skb;
struct sk_buff *tmp;
/* If the pkt at the head of the queue has the nr that we
* expect to send up next, dequeue it and any other
* in-sequence packets behind it.
*/
spin_lock_bh(&session->reorder_q.lock);
skb_queue_walk_safe(&session->reorder_q, skb, tmp) {
if (time_after(jiffies, PPPOL2TP_SKB_CB(skb)->expires)) {
session->stats.rx_seq_discards++;
session->stats.rx_errors++;
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
"%s: oos pkt %hu len %d discarded (too old), "
"waiting for %hu, reorder_q_len=%d\n",
session->name, PPPOL2TP_SKB_CB(skb)->ns,
PPPOL2TP_SKB_CB(skb)->length, session->nr,
skb_queue_len(&session->reorder_q));
__skb_unlink(skb, &session->reorder_q);
kfree_skb(skb);
sock_put(session->sock);
continue;
}
if (PPPOL2TP_SKB_CB(skb)->has_seq) {
if (PPPOL2TP_SKB_CB(skb)->ns != session->nr) {
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
"%s: holding oos pkt %hu len %d, "
"waiting for %hu, reorder_q_len=%d\n",
session->name, PPPOL2TP_SKB_CB(skb)->ns,
PPPOL2TP_SKB_CB(skb)->length, session->nr,
skb_queue_len(&session->reorder_q));
goto out;
}
}
__skb_unlink(skb, &session->reorder_q);
/* Process the skb. We release the queue lock while we
* do so to let other contexts process the queue.
*/
spin_unlock_bh(&session->reorder_q.lock);
pppol2tp_recv_dequeue_skb(session, skb);
spin_lock_bh(&session->reorder_q.lock);
}
out:
spin_unlock_bh(&session->reorder_q.lock);
}
static inline int pppol2tp_verify_udp_checksum(struct sock *sk,
struct sk_buff *skb)
{
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)))
return 0;
skb->csum = psum;
return __skb_checksum_complete(skb);
}
/* Internal receive frame. Do the real work of receiving an L2TP data frame
* here. The skb is not on a list when we get here.
* Returns 0 if the packet was a data packet and was successfully passed on.
* Returns 1 if the packet was not a good data packet and could not be
* forwarded. All such packets are passed up to userspace to deal with.
*/
static int pppol2tp_recv_core(struct sock *sock, struct sk_buff *skb)
{
struct pppol2tp_session *session = NULL;
struct pppol2tp_tunnel *tunnel;
unsigned char *ptr, *optr;
u16 hdrflags;
u16 tunnel_id, session_id;
int length;
int offset;
tunnel = pppol2tp_sock_to_tunnel(sock);
if (tunnel == NULL)
goto no_tunnel;
if (tunnel->sock && pppol2tp_verify_udp_checksum(tunnel->sock, skb))
goto discard_bad_csum;
/* UDP always verifies the packet length. */
__skb_pull(skb, sizeof(struct udphdr));
/* Short packet? */
if (!pskb_may_pull(skb, 12)) {
PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_INFO,
"%s: recv short packet (len=%d)\n", tunnel->name, skb->len);
goto error;
}
/* Point to L2TP header */
optr = ptr = skb->data;
/* Get L2TP header flags */
hdrflags = ntohs(*(__be16*)ptr);
/* Trace packet contents, if enabled */
if (tunnel->debug & PPPOL2TP_MSG_DATA) {
length = min(16u, skb->len);
if (!pskb_may_pull(skb, length))
goto error;
printk(KERN_DEBUG "%s: recv: ", tunnel->name);
offset = 0;
do {
printk(" %02X", ptr[offset]);
} while (++offset < length);
printk("\n");
}
/* Get length of L2TP packet */
length = skb->len;
/* If type is control packet, it is handled by userspace. */
if (hdrflags & L2TP_HDRFLAG_T) {
PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
"%s: recv control packet, len=%d\n", tunnel->name, length);
goto error;
}
/* Skip flags */
ptr += 2;
/* If length is present, skip it */
if (hdrflags & L2TP_HDRFLAG_L)
ptr += 2;
/* Extract tunnel and session ID */
tunnel_id = ntohs(*(__be16 *) ptr);
ptr += 2;
session_id = ntohs(*(__be16 *) ptr);
ptr += 2;
/* Find the session context */
session = pppol2tp_session_find(tunnel, session_id);
if (!session) {
/* Not found? Pass to userspace to deal with */
PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_INFO,
"%s: no socket found (%hu/%hu). Passing up.\n",
tunnel->name, tunnel_id, session_id);
goto error;
}
sock_hold(session->sock);
/* The ref count on the socket was increased by the above call since
* we now hold a pointer to the session. Take care to do sock_put()
* when exiting this function from now on...
*/
/* Handle the optional sequence numbers. If we are the LAC,
* enable/disable sequence numbers under the control of the LNS. If
* no sequence numbers present but we were expecting them, discard
* frame.
*/
if (hdrflags & L2TP_HDRFLAG_S) {
u16 ns, nr;
ns = ntohs(*(__be16 *) ptr);
ptr += 2;
nr = ntohs(*(__be16 *) ptr);
ptr += 2;
/* Received a packet with sequence numbers. If we're the LNS,
* check if we sre sending sequence numbers and if not,
* configure it so.
*/
if ((!session->lns_mode) && (!session->send_seq)) {
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_INFO,
"%s: requested to enable seq numbers by LNS\n",
session->name);
session->send_seq = -1;
}
/* Store L2TP info in the skb */
PPPOL2TP_SKB_CB(skb)->ns = ns;
PPPOL2TP_SKB_CB(skb)->nr = nr;
PPPOL2TP_SKB_CB(skb)->has_seq = 1;
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
"%s: recv data ns=%hu, nr=%hu, session nr=%hu\n",
session->name, ns, nr, session->nr);
} else {
/* No sequence numbers.
* If user has configured mandatory sequence numbers, discard.
*/
if (session->recv_seq) {
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_WARNING,
"%s: recv data has no seq numbers when required. "
"Discarding\n", session->name);
session->stats.rx_seq_discards++;
goto discard;
}
/* If we're the LAC and we're sending sequence numbers, the
* LNS has requested that we no longer send sequence numbers.
* If we're the LNS and we're sending sequence numbers, the
* LAC is broken. Discard the frame.
*/
if ((!session->lns_mode) && (session->send_seq)) {
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_INFO,
"%s: requested to disable seq numbers by LNS\n",
session->name);
session->send_seq = 0;
} else if (session->send_seq) {
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_WARNING,
"%s: recv data has no seq numbers when required. "
"Discarding\n", session->name);
session->stats.rx_seq_discards++;
goto discard;
}
/* Store L2TP info in the skb */
PPPOL2TP_SKB_CB(skb)->has_seq = 0;
}
/* If offset bit set, skip it. */
if (hdrflags & L2TP_HDRFLAG_O) {
offset = ntohs(*(__be16 *)ptr);
ptr += 2 + offset;
}
offset = ptr - optr;
if (!pskb_may_pull(skb, offset))
goto discard;
__skb_pull(skb, offset);
/* Skip PPP header, if present. In testing, Microsoft L2TP clients
* don't send the PPP header (PPP header compression enabled), but
* other clients can include the header. So we cope with both cases
* here. The PPP header is always FF03 when using L2TP.
*
* Note that skb->data[] isn't dereferenced from a u16 ptr here since
* the field may be unaligned.
*/
if (!pskb_may_pull(skb, 2))
goto discard;
if ((skb->data[0] == 0xff) && (skb->data[1] == 0x03))
skb_pull(skb, 2);
/* Prepare skb for adding to the session's reorder_q. Hold
* packets for max reorder_timeout or 1 second if not
* reordering.
*/
PPPOL2TP_SKB_CB(skb)->length = length;
PPPOL2TP_SKB_CB(skb)->expires = jiffies +
(session->reorder_timeout ? session->reorder_timeout : HZ);
/* Add packet to the session's receive queue. Reordering is done here, if
* enabled. Saved L2TP protocol info is stored in skb->sb[].
*/
if (PPPOL2TP_SKB_CB(skb)->has_seq) {
if (session->reorder_timeout != 0) {
/* Packet reordering enabled. Add skb to session's
* reorder queue, in order of ns.
*/
pppol2tp_recv_queue_skb(session, skb);
} else {
/* Packet reordering disabled. Discard out-of-sequence
* packets
*/
if (PPPOL2TP_SKB_CB(skb)->ns != session->nr) {
session->stats.rx_seq_discards++;
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
"%s: oos pkt %hu len %d discarded, "
"waiting for %hu, reorder_q_len=%d\n",
session->name, PPPOL2TP_SKB_CB(skb)->ns,
PPPOL2TP_SKB_CB(skb)->length, session->nr,
skb_queue_len(&session->reorder_q));
goto discard;
}
skb_queue_tail(&session->reorder_q, skb);
}
} else {
/* No sequence numbers. Add the skb to the tail of the
* reorder queue. This ensures that it will be
* delivered after all previous sequenced skbs.
*/
skb_queue_tail(&session->reorder_q, skb);
}
/* Try to dequeue as many skbs from reorder_q as we can. */
pppol2tp_recv_dequeue(session);
sock_put(sock);
return 0;
discard:
session->stats.rx_errors++;
kfree_skb(skb);
sock_put(session->sock);
sock_put(sock);
return 0;
return;
discard_bad_csum:
LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name);
UDP_INC_STATS_USER(&init_net, UDP_MIB_INERRORS, 0);
tunnel->stats.rx_errors++;
no_sock:
PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_INFO,
"%s: no socket\n", session->name);
kfree_skb(skb);
sock_put(sock);
return 0;
error:
/* Put UDP header back */
__skb_push(skb, sizeof(struct udphdr));
sock_put(sock);
no_tunnel:
return 1;
}
/* UDP encapsulation receive handler. See net/ipv4/udp.c.
* Return codes:
* 0 : success.
* <0: error
* >0: skb should be passed up to userspace as UDP.
*/
static int pppol2tp_udp_encap_recv(struct sock *sk, struct sk_buff *skb)
static void pppol2tp_session_sock_hold(struct l2tp_session *session)
{
struct pppol2tp_tunnel *tunnel;
tunnel = pppol2tp_sock_to_tunnel(sk);
if (tunnel == NULL)
goto pass_up;
PRINTK(tunnel->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
"%s: received %d bytes\n", tunnel->name, skb->len);
if (pppol2tp_recv_core(sk, skb))
goto pass_up_put;
sock_put(sk);
return 0;
struct pppol2tp_session *ps = l2tp_session_priv(session);
pass_up_put:
sock_put(sk);
pass_up:
return 1;
if (ps->sock)
sock_hold(ps->sock);
}
/* Receive message. This is the recvmsg for the PPPoL2TP socket.
*/
static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *msg, size_t len,
int flags)
static void pppol2tp_session_sock_put(struct l2tp_session *session)
{
int err;
struct sk_buff *skb;
struct sock *sk = sock->sk;
err = -EIO;
if (sk->sk_state & PPPOX_BOUND)
goto end;
msg->msg_namelen = 0;
err = 0;
skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
flags & MSG_DONTWAIT, &err);
if (!skb)
goto end;
struct pppol2tp_session *ps = l2tp_session_priv(session);
if (len > skb->len)
len = skb->len;
else if (len < skb->len)
msg->msg_flags |= MSG_TRUNC;
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
if (likely(err == 0))
err = len;
kfree_skb(skb);
end:
return err;
if (ps->sock)
sock_put(ps->sock);
}
/************************************************************************
......@@ -858,7 +294,7 @@ static int pppol2tp_recvmsg(struct kiocb *iocb, struct socket *sock,
/* Tell how big L2TP headers are for a particular session. This
* depends on whether sequence numbers are being used.
*/
static inline int pppol2tp_l2tp_header_len(struct pppol2tp_session *session)
static inline int pppol2tp_l2tp_header_len(struct l2tp_session *session)
{
if (session->send_seq)
return PPPOL2TP_L2TP_HDR_SIZE_SEQ;
......@@ -866,33 +302,6 @@ static inline int pppol2tp_l2tp_header_len(struct pppol2tp_session *session)
return PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
}
/* Build an L2TP header for the session into the buffer provided.
*/
static void pppol2tp_build_l2tp_header(struct pppol2tp_session *session,
void *buf)
{
__be16 *bufp = buf;
u16 flags = L2TP_HDR_VER;
if (session->send_seq)
flags |= L2TP_HDRFLAG_S;
/* Setup L2TP header.
* FIXME: Can this ever be unaligned? Is direct dereferencing of
* 16-bit header fields safe here for all architectures?
*/
*bufp++ = htons(flags);
*bufp++ = htons(session->tunnel_addr.d_tunnel);
*bufp++ = htons(session->tunnel_addr.d_session);
if (session->send_seq) {
*bufp++ = htons(session->ns);
*bufp++ = 0;
session->ns++;
PRINTK(session->debug, PPPOL2TP_MSG_SEQ, KERN_DEBUG,
"%s: updated ns to %hu\n", session->name, session->ns);
}
}
/* This is the sendmsg for the PPPoL2TP pppol2tp_session socket. We come here
* when a user application does a sendmsg() on the session socket. L2TP and
* PPP headers must be inserted into the user's data.
......@@ -902,17 +311,11 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
{
static const unsigned char ppph[2] = { 0xff, 0x03 };
struct sock *sk = sock->sk;
struct inet_sock *inet;
__wsum csum;
struct sk_buff *skb;
int error;
int hdr_len;
struct pppol2tp_session *session;
struct pppol2tp_tunnel *tunnel;
struct udphdr *uh;
unsigned int len;
struct sock *sk_tun;
u16 udp_len;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel;
struct pppol2tp_session *ps;
error = -ENOTCONN;
if (sock_flag(sk, SOCK_DEAD) || !(sk->sk_state & PPPOX_CONNECTED))
......@@ -924,18 +327,15 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
if (session == NULL)
goto error;
sk_tun = session->tunnel_sock;
tunnel = pppol2tp_sock_to_tunnel(sk_tun);
ps = l2tp_session_priv(session);
tunnel = l2tp_sock_to_tunnel(ps->tunnel_sock);
if (tunnel == NULL)
goto error_put_sess;
/* What header length is configured for this session? */
hdr_len = pppol2tp_l2tp_header_len(session);
/* Allocate a socket buffer */
error = -ENOMEM;
skb = sock_wmalloc(sk, NET_SKB_PAD + sizeof(struct iphdr) +
sizeof(struct udphdr) + hdr_len +
sizeof(struct udphdr) + session->hdr_len +
sizeof(ppph) + total_len,
0, GFP_KERNEL);
if (!skb)
......@@ -946,120 +346,35 @@ static int pppol2tp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msgh
skb_reset_network_header(skb);
skb_reserve(skb, sizeof(struct iphdr));
skb_reset_transport_header(skb);
/* Build UDP header */
inet = inet_sk(sk_tun);
udp_len = hdr_len + sizeof(ppph) + total_len;
uh = (struct udphdr *) skb->data;
uh->source = inet->inet_sport;
uh->dest = inet->inet_dport;
uh->len = htons(udp_len);
uh->check = 0;
skb_put(skb, sizeof(struct udphdr));
/* Build L2TP header */
pppol2tp_build_l2tp_header(session, skb->data);
skb_put(skb, hdr_len);
skb_reserve(skb, sizeof(struct udphdr));
/* Add PPP header */
skb->data[0] = ppph[0];
skb->data[1] = ppph[1];
skb_put(skb, 2);
/* Copy user data into skb */
error = memcpy_fromiovec(skb->data, m->msg_iov, total_len);
if (error < 0) {
kfree_skb(skb);
goto error_put_sess_tun;
}
skb_put(skb, total_len);
/* Calculate UDP checksum if configured to do so */
if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
skb->ip_summed = CHECKSUM_NONE;
else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
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);
}
/* Debug */
if (session->send_seq)
PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
"%s: send %Zd bytes, ns=%hu\n", session->name,
total_len, session->ns - 1);
else
PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
"%s: send %Zd bytes\n", session->name, total_len);
if (session->debug & PPPOL2TP_MSG_DATA) {
int i;
unsigned char *datap = skb->data;
printk(KERN_DEBUG "%s: xmit:", session->name);
for (i = 0; i < total_len; i++) {
printk(" %02X", *datap++);
if (i == 15) {
printk(" ...");
break;
}
}
printk("\n");
skb_put(skb, 2);
/* Copy user data into skb */
error = memcpy_fromiovec(skb->data, m->msg_iov, total_len);
if (error < 0) {
kfree_skb(skb);
goto error_put_sess_tun;
}
skb_put(skb, total_len);
/* Queue the packet to IP for output */
len = skb->len;
error = ip_queue_xmit(skb, 1);
l2tp_xmit_skb(session, skb, session->hdr_len);
/* Update stats */
if (error >= 0) {
tunnel->stats.tx_packets++;
tunnel->stats.tx_bytes += len;
session->stats.tx_packets++;
session->stats.tx_bytes += len;
} else {
tunnel->stats.tx_errors++;
session->stats.tx_errors++;
}
sock_put(ps->tunnel_sock);
return error;
error_put_sess_tun:
sock_put(session->tunnel_sock);
sock_put(ps->tunnel_sock);
error_put_sess:
sock_put(sk);
error:
return error;
}
/* Automatically called when the skb is freed.
*/
static void pppol2tp_sock_wfree(struct sk_buff *skb)
{
sock_put(skb->sk);
}
/* For data skbs that we transmit, we associate with the tunnel socket
* but don't do accounting.
*/
static inline void pppol2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk)
{
sock_hold(sk);
skb->sk = sk;
skb->destructor = pppol2tp_sock_wfree;
}
/* Transmit function called by generic PPP driver. Sends PPP frame
* over PPPoL2TP socket.
*
......@@ -1080,16 +395,9 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
struct sock *sk = (struct sock *) chan->private;
struct sock *sk_tun;
int hdr_len;
u16 udp_len;
struct pppol2tp_session *session;
struct pppol2tp_tunnel *tunnel;
int rc;
int headroom;
int data_len = skb->len;
struct inet_sock *inet;
__wsum csum;
struct udphdr *uh;
unsigned int len;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel;
struct pppol2tp_session *ps;
int old_headroom;
int new_headroom;
......@@ -1101,28 +409,22 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
if (session == NULL)
goto abort;
sk_tun = session->tunnel_sock;
ps = l2tp_session_priv(session);
sk_tun = ps->tunnel_sock;
if (sk_tun == NULL)
goto abort_put_sess;
tunnel = pppol2tp_sock_to_tunnel(sk_tun);
tunnel = l2tp_sock_to_tunnel(sk_tun);
if (tunnel == NULL)
goto abort_put_sess;
/* What header length is configured for this session? */
hdr_len = pppol2tp_l2tp_header_len(session);
/* Check that there's enough headroom in the skb to insert IP,
* UDP and L2TP and PPP headers. If not enough, expand it to
* make room. Adjust truesize.
*/
headroom = NET_SKB_PAD + sizeof(struct iphdr) +
sizeof(struct udphdr) + hdr_len + sizeof(ppph);
old_headroom = skb_headroom(skb);
if (skb_cow_head(skb, headroom))
if (skb_cow_head(skb, sizeof(ppph)))
goto abort_put_sess_tun;
new_headroom = skb_headroom(skb);
skb_orphan(skb);
skb->truesize += new_headroom - old_headroom;
/* Setup PPP header */
......@@ -1130,90 +432,7 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
skb->data[0] = ppph[0];
skb->data[1] = ppph[1];
/* Setup L2TP header */
pppol2tp_build_l2tp_header(session, __skb_push(skb, hdr_len));
udp_len = sizeof(struct udphdr) + hdr_len + sizeof(ppph) + data_len;
/* Setup UDP header */
inet = inet_sk(sk_tun);
__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;
/* Debug */
if (session->send_seq)
PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
"%s: send %d bytes, ns=%hu\n", session->name,
data_len, session->ns - 1);
else
PRINTK(session->debug, PPPOL2TP_MSG_DATA, KERN_DEBUG,
"%s: send %d bytes\n", session->name, data_len);
if (session->debug & PPPOL2TP_MSG_DATA) {
int i;
unsigned char *datap = skb->data;
printk(KERN_DEBUG "%s: xmit:", session->name);
for (i = 0; i < data_len; i++) {
printk(" %02X", *datap++);
if (i == 31) {
printk(" ...");
break;
}
}
printk("\n");
}
memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
IPCB(skb)->flags &= ~(IPSKB_XFRM_TUNNEL_SIZE | IPSKB_XFRM_TRANSFORMED |
IPSKB_REROUTED);
nf_reset(skb);
/* Get routing info from the tunnel socket */
skb_dst_drop(skb);
skb_dst_set(skb, dst_clone(__sk_dst_get(sk_tun)));
pppol2tp_skb_set_owner_w(skb, sk_tun);
/* Calculate UDP checksum if configured to do so */
if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
skb->ip_summed = CHECKSUM_NONE;
else if ((skb_dst(skb) && skb_dst(skb)->dev) &&
(!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) {
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);
}
/* Queue the packet to IP for output */
len = skb->len;
rc = ip_queue_xmit(skb, 1);
/* Update stats */
if (rc >= 0) {
tunnel->stats.tx_packets++;
tunnel->stats.tx_bytes += len;
session->stats.tx_packets++;
session->stats.tx_bytes += len;
} else {
tunnel->stats.tx_errors++;
session->stats.tx_errors++;
}
l2tp_xmit_skb(session, skb, hdr_len);
sock_put(sk_tun);
sock_put(sk);
......@@ -1233,126 +452,40 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
* Session (and tunnel control) socket create/destroy.
*****************************************************************************/
/* When the tunnel UDP socket is closed, all the attached sockets need to go
* too.
/* Called by l2tp_core when a session socket is being closed.
*/
static void pppol2tp_tunnel_closeall(struct pppol2tp_tunnel *tunnel)
static void pppol2tp_session_close(struct l2tp_session *session)
{
int hash;
struct hlist_node *walk;
struct hlist_node *tmp;
struct pppol2tp_session *session;
struct sock *sk;
BUG_ON(tunnel == NULL);
PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: closing all sessions...\n", tunnel->name);
write_lock_bh(&tunnel->hlist_lock);
for (hash = 0; hash < PPPOL2TP_HASH_SIZE; hash++) {
again:
hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) {
struct sk_buff *skb;
session = hlist_entry(walk, struct pppol2tp_session, hlist);
sk = session->sock;
PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: closing session\n", session->name);
struct pppol2tp_session *ps = l2tp_session_priv(session);
struct sock *sk = ps->sock;
struct sk_buff *skb;
hlist_del_init(&session->hlist);
BUG_ON(session->magic != L2TP_SESSION_MAGIC);
/* Since we should hold the sock lock while
* doing any unbinding, we need to release the
* lock we're holding before taking that lock.
* Hold a reference to the sock so it doesn't
* disappear as we're jumping between locks.
*/
sock_hold(sk);
write_unlock_bh(&tunnel->hlist_lock);
lock_sock(sk);
if (session->session_id == 0)
goto out;
if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
pppox_unbind_sock(sk);
sk->sk_state = PPPOX_DEAD;
sk->sk_state_change(sk);
}
if (sk != NULL) {
lock_sock(sk);
/* Purge any queued data */
skb_queue_purge(&sk->sk_receive_queue);
skb_queue_purge(&sk->sk_write_queue);
while ((skb = skb_dequeue(&session->reorder_q))) {
kfree_skb(skb);
sock_put(sk);
}
if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
pppox_unbind_sock(sk);
sk->sk_state = PPPOX_DEAD;
sk->sk_state_change(sk);
}
release_sock(sk);
/* Purge any queued data */
skb_queue_purge(&sk->sk_receive_queue);
skb_queue_purge(&sk->sk_write_queue);
while ((skb = skb_dequeue(&session->reorder_q))) {
kfree_skb(skb);
sock_put(sk);
/* Now restart from the beginning of this hash
* chain. We always remove a session from the
* list so we are guaranteed to make forward
* progress.
*/
write_lock_bh(&tunnel->hlist_lock);
goto again;
}
}
write_unlock_bh(&tunnel->hlist_lock);
}
/* Really kill the tunnel.
* Come here only when all sessions have been cleared from the tunnel.
*/
static void pppol2tp_tunnel_free(struct pppol2tp_tunnel *tunnel)
{
struct pppol2tp_net *pn = pppol2tp_pernet(tunnel->pppol2tp_net);
/* Remove from socket list */
write_lock_bh(&pn->pppol2tp_tunnel_list_lock);
list_del_init(&tunnel->list);
write_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
atomic_dec(&pppol2tp_tunnel_count);
kfree(tunnel);
}
/* Tunnel UDP socket destruct hook.
* The tunnel context is deleted only when all session sockets have been
* closed.
*/
static void pppol2tp_tunnel_destruct(struct sock *sk)
{
struct pppol2tp_tunnel *tunnel;
tunnel = sk->sk_user_data;
if (tunnel == NULL)
goto end;
PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: closing...\n", tunnel->name);
/* Close all sessions */
pppol2tp_tunnel_closeall(tunnel);
/* No longer an encapsulation socket. See net/ipv4/udp.c */
(udp_sk(sk))->encap_type = 0;
(udp_sk(sk))->encap_rcv = NULL;
/* Remove hooks into tunnel socket */
tunnel->sock = NULL;
sk->sk_destruct = tunnel->old_sk_destruct;
sk->sk_user_data = NULL;
/* Call original (UDP) socket descructor */
if (sk->sk_destruct != NULL)
(*sk->sk_destruct)(sk);
pppol2tp_tunnel_dec_refcount(tunnel);
release_sock(sk);
}
end:
out:
return;
}
......@@ -1361,53 +494,18 @@ static void pppol2tp_tunnel_destruct(struct sock *sk)
*/
static void pppol2tp_session_destruct(struct sock *sk)
{
struct pppol2tp_session *session = NULL;
struct l2tp_session *session;
if (sk->sk_user_data != NULL) {
struct pppol2tp_tunnel *tunnel;
session = sk->sk_user_data;
if (session == NULL)
goto out;
sk->sk_user_data = NULL;
BUG_ON(session->magic != L2TP_SESSION_MAGIC);
/* Don't use pppol2tp_sock_to_tunnel() here to
* get the tunnel context because the tunnel
* socket might have already been closed (its
* sk->sk_user_data will be NULL) so use the
* session's private tunnel ptr instead.
*/
tunnel = session->tunnel;
if (tunnel != NULL) {
BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);
/* If session_id is zero, this is a null
* session context, which was created for a
* socket that is being used only to manage
* tunnels.
*/
if (session->tunnel_addr.s_session != 0) {
/* Delete the session socket from the
* hash
*/
write_lock_bh(&tunnel->hlist_lock);
hlist_del_init(&session->hlist);
write_unlock_bh(&tunnel->hlist_lock);
atomic_dec(&pppol2tp_session_count);
}
/* This will delete the tunnel context if this
* is the last session on the tunnel.
*/
session->tunnel = NULL;
session->tunnel_sock = NULL;
pppol2tp_tunnel_dec_refcount(tunnel);
}
l2tp_session_dec_refcount(session);
}
kfree(session);
out:
return;
}
......@@ -1417,7 +515,7 @@ static void pppol2tp_session_destruct(struct sock *sk)
static int pppol2tp_release(struct socket *sock)
{
struct sock *sk = sock->sk;
struct pppol2tp_session *session;
struct l2tp_session *session;
int error;
if (!sk)
......@@ -1464,130 +562,23 @@ static int pppol2tp_release(struct socket *sock)
return error;
}
/* Internal function to prepare a tunnel (UDP) socket to have PPPoX
* sockets attached to it.
*/
static struct sock *pppol2tp_prepare_tunnel_socket(struct net *net,
int fd, u16 tunnel_id, int *error)
{
int err;
struct socket *sock = NULL;
struct sock *sk;
struct pppol2tp_tunnel *tunnel;
struct pppol2tp_net *pn;
struct sock *ret = NULL;
/* Get the tunnel UDP socket from the fd, which was opened by
* the userspace L2TP daemon.
*/
err = -EBADF;
sock = sockfd_lookup(fd, &err);
if (!sock) {
PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_ERR,
"tunl %hu: sockfd_lookup(fd=%d) returned %d\n",
tunnel_id, fd, err);
goto err;
}
sk = sock->sk;
/* Quick sanity checks */
err = -EPROTONOSUPPORT;
if (sk->sk_protocol != IPPROTO_UDP) {
PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_ERR,
"tunl %hu: fd %d wrong protocol, got %d, expected %d\n",
tunnel_id, fd, sk->sk_protocol, IPPROTO_UDP);
goto err;
}
err = -EAFNOSUPPORT;
if (sock->ops->family != AF_INET) {
PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_ERR,
"tunl %hu: fd %d wrong family, got %d, expected %d\n",
tunnel_id, fd, sock->ops->family, AF_INET);
goto err;
}
err = -ENOTCONN;
/* Check if this socket has already been prepped */
tunnel = (struct pppol2tp_tunnel *)sk->sk_user_data;
if (tunnel != NULL) {
/* User-data field already set */
err = -EBUSY;
BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);
/* This socket has already been prepped */
ret = tunnel->sock;
goto out;
}
/* This socket is available and needs prepping. Create a new tunnel
* context and init it.
*/
sk->sk_user_data = tunnel = kzalloc(sizeof(struct pppol2tp_tunnel), GFP_KERNEL);
if (sk->sk_user_data == NULL) {
err = -ENOMEM;
goto err;
}
tunnel->magic = L2TP_TUNNEL_MAGIC;
sprintf(&tunnel->name[0], "tunl %hu", tunnel_id);
tunnel->stats.tunnel_id = tunnel_id;
tunnel->debug = PPPOL2TP_DEFAULT_DEBUG_FLAGS;
/* Hook on the tunnel socket destructor so that we can cleanup
* if the tunnel socket goes away.
*/
tunnel->old_sk_destruct = sk->sk_destruct;
sk->sk_destruct = pppol2tp_tunnel_destruct;
tunnel->sock = sk;
sk->sk_allocation = GFP_ATOMIC;
/* Misc init */
rwlock_init(&tunnel->hlist_lock);
/* The net we belong to */
tunnel->pppol2tp_net = net;
pn = pppol2tp_pernet(net);
/* Add tunnel to our list */
INIT_LIST_HEAD(&tunnel->list);
write_lock_bh(&pn->pppol2tp_tunnel_list_lock);
list_add(&tunnel->list, &pn->pppol2tp_tunnel_list);
write_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
atomic_inc(&pppol2tp_tunnel_count);
/* Bump the reference count. The tunnel context is deleted
* only when this drops to zero.
*/
pppol2tp_tunnel_inc_refcount(tunnel);
/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */
(udp_sk(sk))->encap_type = UDP_ENCAP_L2TPINUDP;
(udp_sk(sk))->encap_rcv = pppol2tp_udp_encap_recv;
ret = tunnel->sock;
*error = 0;
out:
if (sock)
sockfd_put(sock);
return ret;
err:
*error = err;
goto out;
}
static struct proto pppol2tp_sk_proto = {
.name = "PPPOL2TP",
.owner = THIS_MODULE,
.obj_size = sizeof(struct pppox_sock),
};
static int pppol2tp_backlog_recv(struct sock *sk, struct sk_buff *skb)
{
int rc;
rc = l2tp_udp_encap_recv(sk, skb);
if (rc)
kfree_skb(skb);
return NET_RX_SUCCESS;
}
/* socket() handler. Initialize a new struct sock.
*/
static int pppol2tp_create(struct net *net, struct socket *sock)
......@@ -1604,7 +595,7 @@ static int pppol2tp_create(struct net *net, struct socket *sock)
sock->state = SS_UNCONNECTED;
sock->ops = &pppol2tp_ops;
sk->sk_backlog_rcv = pppol2tp_recv_core;
sk->sk_backlog_rcv = pppol2tp_backlog_recv;
sk->sk_protocol = PX_PROTO_OL2TP;
sk->sk_family = PF_PPPOX;
sk->sk_state = PPPOX_NONE;
......@@ -1625,10 +616,11 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
struct sock *sk = sock->sk;
struct sockaddr_pppol2tp *sp = (struct sockaddr_pppol2tp *) uservaddr;
struct pppox_sock *po = pppox_sk(sk);
struct sock *tunnel_sock = NULL;
struct pppol2tp_session *session = NULL;
struct pppol2tp_tunnel *tunnel;
struct l2tp_session *session = NULL;
struct l2tp_tunnel *tunnel;
struct pppol2tp_session *ps;
struct dst_entry *dst;
struct l2tp_session_cfg cfg = { 0, };
int error = 0;
lock_sock(sk);
......@@ -1652,68 +644,65 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
if (sp->pppol2tp.s_tunnel == 0)
goto end;
/* Special case: prepare tunnel socket if s_session and
/* Special case: create tunnel context if s_session and
* d_session is 0. Otherwise look up tunnel using supplied
* tunnel id.
*/
if ((sp->pppol2tp.s_session == 0) && (sp->pppol2tp.d_session == 0)) {
tunnel_sock = pppol2tp_prepare_tunnel_socket(sock_net(sk),
sp->pppol2tp.fd,
sp->pppol2tp.s_tunnel,
&error);
if (tunnel_sock == NULL)
error = l2tp_tunnel_create(sock_net(sk), sp->pppol2tp.fd, 2, sp->pppol2tp.s_tunnel, sp->pppol2tp.d_tunnel, NULL, &tunnel);
if (error < 0)
goto end;
sock_hold(tunnel_sock);
tunnel = tunnel_sock->sk_user_data;
} else {
tunnel = pppol2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel);
tunnel = l2tp_tunnel_find(sock_net(sk), sp->pppol2tp.s_tunnel);
/* Error if we can't find the tunnel */
error = -ENOENT;
if (tunnel == NULL)
goto end;
tunnel_sock = tunnel->sock;
/* Error if socket is not prepped */
if (tunnel->sock == NULL)
goto end;
}
if (tunnel->recv_payload_hook == NULL)
tunnel->recv_payload_hook = pppol2tp_recv_payload_hook;
/* Check that this session doesn't already exist */
error = -EEXIST;
session = pppol2tp_session_find(tunnel, sp->pppol2tp.s_session);
session = l2tp_session_find(tunnel, sp->pppol2tp.s_session);
if (session != NULL)
goto end;
/* Default MTU must allow space for UDP/L2TP/PPP
* headers.
*/
cfg.mtu = cfg.mru = 1500 - PPPOL2TP_HEADER_OVERHEAD;
cfg.hdr_len = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
cfg.debug = tunnel->debug;
/* Allocate and initialize a new session context. */
session = kzalloc(sizeof(struct pppol2tp_session), GFP_KERNEL);
session = l2tp_session_create(sizeof(struct pppol2tp_session),
tunnel, sp->pppol2tp.s_session,
sp->pppol2tp.d_session, &cfg);
if (session == NULL) {
error = -ENOMEM;
goto end;
}
skb_queue_head_init(&session->reorder_q);
session->magic = L2TP_SESSION_MAGIC;
session->owner = current->pid;
session->sock = sk;
session->tunnel = tunnel;
session->tunnel_sock = tunnel_sock;
session->tunnel_addr = sp->pppol2tp;
sprintf(&session->name[0], "sess %hu/%hu",
session->tunnel_addr.s_tunnel,
session->tunnel_addr.s_session);
session->stats.tunnel_id = session->tunnel_addr.s_tunnel;
session->stats.session_id = session->tunnel_addr.s_session;
ps = l2tp_session_priv(session);
ps->owner = current->pid;
ps->sock = sk;
ps->tunnel_sock = tunnel->sock;
INIT_HLIST_NODE(&session->hlist);
session->recv_skb = pppol2tp_recv;
session->session_close = pppol2tp_session_close;
/* Inherit debug options from tunnel */
session->debug = tunnel->debug;
/* Default MTU must allow space for UDP/L2TP/PPP
* headers.
/* We need to know each time a skb is dropped from the reorder
* queue.
*/
session->mtu = session->mru = 1500 - PPPOL2TP_HEADER_OVERHEAD;
session->ref = pppol2tp_session_sock_hold;
session->deref = pppol2tp_session_sock_put;
/* If PMTU discovery was enabled, use the MTU that was discovered */
dst = sk_dst_get(sk);
......@@ -1725,29 +714,20 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
dst_release(dst);
}
/* Special case: if source & dest session_id == 0x0000, this socket is
* being created to manage the tunnel. Don't add the session to the
* session hash list, just set up the internal context for use by
* ioctl() and sockopt() handlers.
/* Special case: if source & dest session_id == 0x0000, this
* socket is being created to manage the tunnel. Just set up
* the internal context for use by ioctl() and sockopt()
* handlers.
*/
if ((session->tunnel_addr.s_session == 0) &&
(session->tunnel_addr.d_session == 0)) {
if ((session->session_id == 0) &&
(session->peer_session_id == 0)) {
error = 0;
sk->sk_user_data = session;
goto out_no_ppp;
}
/* Get tunnel context from the tunnel socket */
tunnel = pppol2tp_sock_to_tunnel(tunnel_sock);
if (tunnel == NULL) {
error = -EBADF;
goto end;
}
/* Right now, because we don't have a way to push the incoming skb's
* straight through the UDP layer, the only header we need to worry
* about is the L2TP header. This size is different depending on
* whether sequence numbers are enabled for the data channel.
/* The only header we need to worry about is the L2TP
* header. This size is different depending on whether
* sequence numbers are enabled for the data channel.
*/
po->chan.hdrlen = PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
......@@ -1757,42 +737,18 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr,
error = ppp_register_net_channel(sock_net(sk), &po->chan);
if (error)
goto end_put_tun;
goto end;
out_no_ppp:
/* This is how we get the session context from the socket. */
sk->sk_user_data = session;
/* Add session to the tunnel's hash list */
write_lock_bh(&tunnel->hlist_lock);
hlist_add_head(&session->hlist,
pppol2tp_session_id_hash(tunnel,
session->tunnel_addr.s_session));
write_unlock_bh(&tunnel->hlist_lock);
atomic_inc(&pppol2tp_session_count);
out_no_ppp:
pppol2tp_tunnel_inc_refcount(tunnel);
sk->sk_state = PPPOX_CONNECTED;
PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: created\n", session->name);
end_put_tun:
sock_put(tunnel_sock);
end:
release_sock(sk);
if (error != 0) {
if (session)
PRINTK(session->debug,
PPPOL2TP_MSG_CONTROL, KERN_WARNING,
"%s: connect failed: %d\n",
session->name, error);
else
PRINTK(-1, PPPOL2TP_MSG_CONTROL, KERN_WARNING,
"connect failed: %d\n", error);
}
return error;
}
......@@ -1804,29 +760,52 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
int len = sizeof(struct sockaddr_pppol2tp);
struct sockaddr_pppol2tp sp;
int error = 0;
struct pppol2tp_session *session;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel;
struct sock *sk = sock->sk;
struct inet_sock *inet;
struct pppol2tp_session *pls;
error = -ENOTCONN;
if (sock->sk->sk_state != PPPOX_CONNECTED)
if (sk == NULL)
goto end;
if (sk->sk_state != PPPOX_CONNECTED)
goto end;
session = pppol2tp_sock_to_session(sock->sk);
if (session == NULL) {
error = -EBADF;
error = -EBADF;
session = pppol2tp_sock_to_session(sk);
if (session == NULL)
goto end;
pls = l2tp_session_priv(session);
tunnel = l2tp_sock_to_tunnel(pls->tunnel_sock);
if (tunnel == NULL) {
error = -EBADF;
goto end_put_sess;
}
memset(&sp, 0, len);
sp.sa_family = AF_PPPOX;
sp.sa_protocol = PX_PROTO_OL2TP;
memcpy(&sp.pppol2tp, &session->tunnel_addr,
sizeof(struct pppol2tp_addr));
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;
inet = inet_sk(sk);
sp.pppol2tp.addr.sin_family = AF_INET;
sp.pppol2tp.addr.sin_port = inet->inet_dport;
sp.pppol2tp.addr.sin_addr.s_addr = inet->inet_daddr;
memcpy(uaddr, &sp, len);
*usockaddr_len = len;
sock_put(pls->tunnel_sock);
end_put_sess:
sock_put(sk);
error = 0;
sock_put(sock->sk);
end:
return error;
......@@ -1843,20 +822,37 @@ static int pppol2tp_getname(struct socket *sock, struct sockaddr *uaddr,
* calls.
****************************************************************************/
static void pppol2tp_copy_stats(struct pppol2tp_ioc_stats *dest,
struct l2tp_stats *stats)
{
dest->tx_packets = stats->tx_packets;
dest->tx_bytes = stats->tx_bytes;
dest->tx_errors = stats->tx_errors;
dest->rx_packets = stats->rx_packets;
dest->rx_bytes = stats->rx_bytes;
dest->rx_seq_discards = stats->rx_seq_discards;
dest->rx_oos_packets = stats->rx_oos_packets;
dest->rx_errors = stats->rx_errors;
}
/* Session ioctl helper.
*/
static int pppol2tp_session_ioctl(struct pppol2tp_session *session,
static int pppol2tp_session_ioctl(struct l2tp_session *session,
unsigned int cmd, unsigned long arg)
{
struct ifreq ifr;
int err = 0;
struct sock *sk = session->sock;
struct sock *sk;
int val = (int) arg;
struct pppol2tp_session *ps = l2tp_session_priv(session);
struct l2tp_tunnel *tunnel = session->tunnel;
struct pppol2tp_ioc_stats stats;
PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_DEBUG,
"%s: pppol2tp_session_ioctl(cmd=%#x, arg=%#lx)\n",
session->name, cmd, arg);
sk = ps->sock;
sock_hold(sk);
switch (cmd) {
......@@ -1913,7 +909,7 @@ static int pppol2tp_session_ioctl(struct pppol2tp_session *session,
break;
err = -EFAULT;
if (get_user(val,(int __user *) arg))
if (get_user(val, (int __user *) arg))
break;
session->mru = val;
......@@ -1924,11 +920,11 @@ static int pppol2tp_session_ioctl(struct pppol2tp_session *session,
case PPPIOCGFLAGS:
err = -EFAULT;
if (put_user(session->flags, (int __user *) arg))
if (put_user(ps->flags, (int __user *) arg))
break;
PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: get flags=%d\n", session->name, session->flags);
"%s: get flags=%d\n", session->name, ps->flags);
err = 0;
break;
......@@ -1936,9 +932,9 @@ static int pppol2tp_session_ioctl(struct pppol2tp_session *session,
err = -EFAULT;
if (get_user(val, (int __user *) arg))
break;
session->flags = val;
ps->flags = val;
PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: set flags=%d\n", session->name, session->flags);
"%s: set flags=%d\n", session->name, ps->flags);
err = 0;
break;
......@@ -1947,8 +943,12 @@ static int pppol2tp_session_ioctl(struct pppol2tp_session *session,
if (!(sk->sk_state & PPPOX_CONNECTED))
break;
if (copy_to_user((void __user *) arg, &session->stats,
sizeof(session->stats)))
memset(&stats, 0, sizeof(stats));
stats.tunnel_id = tunnel->tunnel_id;
stats.session_id = session->session_id;
pppol2tp_copy_stats(&stats, &session->stats);
if (copy_to_user((void __user *) arg, &stats,
sizeof(stats)))
break;
PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: get L2TP stats\n", session->name);
......@@ -1971,17 +971,18 @@ static int pppol2tp_session_ioctl(struct pppol2tp_session *session,
* specifies a session_id, the session ioctl handler is called. This allows an
* application to retrieve session stats via a tunnel socket.
*/
static int pppol2tp_tunnel_ioctl(struct pppol2tp_tunnel *tunnel,
static int pppol2tp_tunnel_ioctl(struct l2tp_tunnel *tunnel,
unsigned int cmd, unsigned long arg)
{
int err = 0;
struct sock *sk = tunnel->sock;
struct pppol2tp_ioc_stats stats_req;
struct sock *sk;
struct pppol2tp_ioc_stats stats;
PRINTK(tunnel->debug, PPPOL2TP_MSG_CONTROL, KERN_DEBUG,
"%s: pppol2tp_tunnel_ioctl(cmd=%#x, arg=%#lx)\n", tunnel->name,
cmd, arg);
"%s: pppol2tp_tunnel_ioctl(cmd=%#x, arg=%#lx)\n",
tunnel->name, cmd, arg);
sk = tunnel->sock;
sock_hold(sk);
switch (cmd) {
......@@ -1990,15 +991,15 @@ static int pppol2tp_tunnel_ioctl(struct pppol2tp_tunnel *tunnel,
if (!(sk->sk_state & PPPOX_CONNECTED))
break;
if (copy_from_user(&stats_req, (void __user *) arg,
sizeof(stats_req))) {
if (copy_from_user(&stats, (void __user *) arg,
sizeof(stats))) {
err = -EFAULT;
break;
}
if (stats_req.session_id != 0) {
if (stats.session_id != 0) {
/* resend to session ioctl handler */
struct pppol2tp_session *session =
pppol2tp_session_find(tunnel, stats_req.session_id);
struct l2tp_session *session =
l2tp_session_find(tunnel, stats.session_id);
if (session != NULL)
err = pppol2tp_session_ioctl(session, cmd, arg);
else
......@@ -2006,10 +1007,10 @@ static int pppol2tp_tunnel_ioctl(struct pppol2tp_tunnel *tunnel,
break;
}
#ifdef CONFIG_XFRM
tunnel->stats.using_ipsec = (sk->sk_policy[0] || sk->sk_policy[1]) ? 1 : 0;
stats.using_ipsec = (sk->sk_policy[0] || sk->sk_policy[1]) ? 1 : 0;
#endif
if (copy_to_user((void __user *) arg, &tunnel->stats,
sizeof(tunnel->stats))) {
pppol2tp_copy_stats(&stats, &tunnel->stats);
if (copy_to_user((void __user *) arg, &stats, sizeof(stats))) {
err = -EFAULT;
break;
}
......@@ -2035,8 +1036,9 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
struct sock *sk = sock->sk;
struct pppol2tp_session *session;
struct pppol2tp_tunnel *tunnel;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel;
struct pppol2tp_session *ps;
int err;
if (!sk)
......@@ -2060,15 +1062,16 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd,
/* Special case: if session's session_id is zero, treat ioctl as a
* tunnel ioctl
*/
if ((session->tunnel_addr.s_session == 0) &&
(session->tunnel_addr.d_session == 0)) {
ps = l2tp_session_priv(session);
if ((session->session_id == 0) &&
(session->peer_session_id == 0)) {
err = -EBADF;
tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock);
tunnel = l2tp_sock_to_tunnel(ps->tunnel_sock);
if (tunnel == NULL)
goto end_put_sess;
err = pppol2tp_tunnel_ioctl(tunnel, cmd, arg);
sock_put(session->tunnel_sock);
sock_put(ps->tunnel_sock);
goto end_put_sess;
}
......@@ -2093,7 +1096,7 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd,
/* Tunnel setsockopt() helper.
*/
static int pppol2tp_tunnel_setsockopt(struct sock *sk,
struct pppol2tp_tunnel *tunnel,
struct l2tp_tunnel *tunnel,
int optname, int val)
{
int err = 0;
......@@ -2116,10 +1119,11 @@ static int pppol2tp_tunnel_setsockopt(struct sock *sk,
/* Session setsockopt helper.
*/
static int pppol2tp_session_setsockopt(struct sock *sk,
struct pppol2tp_session *session,
struct l2tp_session *session,
int optname, int val)
{
int err = 0;
struct pppol2tp_session *ps = l2tp_session_priv(session);
switch (optname) {
case PPPOL2TP_SO_RECVSEQ:
......@@ -2129,8 +1133,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk,
}
session->recv_seq = val ? -1 : 0;
PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: set recv_seq=%d\n", session->name,
session->recv_seq);
"%s: set recv_seq=%d\n", session->name, session->recv_seq);
break;
case PPPOL2TP_SO_SENDSEQ:
......@@ -2140,7 +1143,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk,
}
session->send_seq = val ? -1 : 0;
{
struct sock *ssk = session->sock;
struct sock *ssk = ps->sock;
struct pppox_sock *po = pppox_sk(ssk);
po->chan.hdrlen = val ? PPPOL2TP_L2TP_HDR_SIZE_SEQ :
PPPOL2TP_L2TP_HDR_SIZE_NOSEQ;
......@@ -2156,8 +1159,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk,
}
session->lns_mode = val ? -1 : 0;
PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: set lns_mode=%d\n", session->name,
session->lns_mode);
"%s: set lns_mode=%d\n", session->name, session->lns_mode);
break;
case PPPOL2TP_SO_DEBUG:
......@@ -2169,8 +1171,7 @@ static int pppol2tp_session_setsockopt(struct sock *sk,
case PPPOL2TP_SO_REORDERTO:
session->reorder_timeout = msecs_to_jiffies(val);
PRINTK(session->debug, PPPOL2TP_MSG_CONTROL, KERN_INFO,
"%s: set reorder_timeout=%d\n", session->name,
session->reorder_timeout);
"%s: set reorder_timeout=%d\n", session->name, session->reorder_timeout);
break;
default:
......@@ -2190,8 +1191,9 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
struct pppol2tp_session *session = sk->sk_user_data;
struct pppol2tp_tunnel *tunnel;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel;
struct pppol2tp_session *ps;
int val;
int err;
......@@ -2216,15 +1218,16 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname,
/* Special case: if session_id == 0x0000, treat as operation on tunnel
*/
if ((session->tunnel_addr.s_session == 0) &&
(session->tunnel_addr.d_session == 0)) {
ps = l2tp_session_priv(session);
if ((session->session_id == 0) &&
(session->peer_session_id == 0)) {
err = -EBADF;
tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock);
tunnel = l2tp_sock_to_tunnel(ps->tunnel_sock);
if (tunnel == NULL)
goto end_put_sess;
err = pppol2tp_tunnel_setsockopt(sk, tunnel, optname, val);
sock_put(session->tunnel_sock);
sock_put(ps->tunnel_sock);
} else
err = pppol2tp_session_setsockopt(sk, session, optname, val);
......@@ -2239,7 +1242,7 @@ static int pppol2tp_setsockopt(struct socket *sock, int level, int optname,
/* Tunnel getsockopt helper. Called with sock locked.
*/
static int pppol2tp_tunnel_getsockopt(struct sock *sk,
struct pppol2tp_tunnel *tunnel,
struct l2tp_tunnel *tunnel,
int optname, int *val)
{
int err = 0;
......@@ -2262,7 +1265,7 @@ static int pppol2tp_tunnel_getsockopt(struct sock *sk,
/* Session getsockopt helper. Called with sock locked.
*/
static int pppol2tp_session_getsockopt(struct sock *sk,
struct pppol2tp_session *session,
struct l2tp_session *session,
int optname, int *val)
{
int err = 0;
......@@ -2314,10 +1317,11 @@ static int pppol2tp_getsockopt(struct socket *sock, int level,
int optname, char __user *optval, int __user *optlen)
{
struct sock *sk = sock->sk;
struct pppol2tp_session *session = sk->sk_user_data;
struct pppol2tp_tunnel *tunnel;
struct l2tp_session *session;
struct l2tp_tunnel *tunnel;
int val, len;
int err;
struct pppol2tp_session *ps;
if (level != SOL_PPPOL2TP)
return udp_prot.getsockopt(sk, level, optname, optval, optlen);
......@@ -2341,15 +1345,16 @@ static int pppol2tp_getsockopt(struct socket *sock, int level,
goto end;
/* Special case: if session_id == 0x0000, treat as operation on tunnel */
if ((session->tunnel_addr.s_session == 0) &&
(session->tunnel_addr.d_session == 0)) {
ps = l2tp_session_priv(session);
if ((session->session_id == 0) &&
(session->peer_session_id == 0)) {
err = -EBADF;
tunnel = pppol2tp_sock_to_tunnel(session->tunnel_sock);
tunnel = l2tp_sock_to_tunnel(ps->tunnel_sock);
if (tunnel == NULL)
goto end_put_sess;
err = pppol2tp_tunnel_getsockopt(sk, tunnel, optname, &val);
sock_put(session->tunnel_sock);
sock_put(ps->tunnel_sock);
} else
err = pppol2tp_session_getsockopt(sk, session, optname, &val);
......@@ -2372,87 +1377,51 @@ static int pppol2tp_getsockopt(struct socket *sock, int level,
* /proc filesystem for debug
*****************************************************************************/
#ifdef CONFIG_PROC_FS
static unsigned int pppol2tp_net_id;
#include <linux/seq_file.h>
#ifdef CONFIG_PROC_FS
struct pppol2tp_seq_data {
struct seq_net_private p;
struct pppol2tp_tunnel *tunnel; /* current tunnel */
struct pppol2tp_session *session; /* NULL means get first session in tunnel */
int tunnel_idx; /* current tunnel */
int session_idx; /* index of session within current tunnel */
struct l2tp_tunnel *tunnel;
struct l2tp_session *session; /* NULL means get next tunnel */
};
static struct pppol2tp_session *next_session(struct pppol2tp_tunnel *tunnel, struct pppol2tp_session *curr)
static void pppol2tp_next_tunnel(struct net *net, struct pppol2tp_seq_data *pd)
{
struct pppol2tp_session *session = NULL;
struct hlist_node *walk;
int found = 0;
int next = 0;
int i;
read_lock_bh(&tunnel->hlist_lock);
for (i = 0; i < PPPOL2TP_HASH_SIZE; i++) {
hlist_for_each_entry(session, walk, &tunnel->session_hlist[i], hlist) {
if (curr == NULL) {
found = 1;
goto out;
}
if (session == curr) {
next = 1;
continue;
}
if (next) {
found = 1;
goto out;
}
}
}
out:
read_unlock_bh(&tunnel->hlist_lock);
if (!found)
session = NULL;
return session;
pd->tunnel = l2tp_tunnel_find_nth(net, pd->tunnel_idx);
pd->tunnel_idx++;
}
static struct pppol2tp_tunnel *next_tunnel(struct pppol2tp_net *pn,
struct pppol2tp_tunnel *curr)
static void pppol2tp_next_session(struct net *net, struct pppol2tp_seq_data *pd)
{
struct pppol2tp_tunnel *tunnel = NULL;
read_lock_bh(&pn->pppol2tp_tunnel_list_lock);
if (list_is_last(&curr->list, &pn->pppol2tp_tunnel_list)) {
goto out;
pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx);
pd->session_idx++;
if (pd->session == NULL) {
pd->session_idx = 0;
pppol2tp_next_tunnel(net, pd);
}
tunnel = list_entry(curr->list.next, struct pppol2tp_tunnel, list);
out:
read_unlock_bh(&pn->pppol2tp_tunnel_list_lock);
return tunnel;
}
static void *pppol2tp_seq_start(struct seq_file *m, loff_t *offs)
{
struct pppol2tp_seq_data *pd = SEQ_START_TOKEN;
struct pppol2tp_net *pn;
loff_t pos = *offs;
struct net *net;
if (!pos)
goto out;
BUG_ON(m->private == NULL);
pd = m->private;
pn = pppol2tp_pernet(seq_file_net(m));
net = seq_file_net(m);
if (pd->tunnel == NULL) {
if (!list_empty(&pn->pppol2tp_tunnel_list))
pd->tunnel = list_entry(pn->pppol2tp_tunnel_list.next, struct pppol2tp_tunnel, list);
} else {
pd->session = next_session(pd->tunnel, pd->session);
if (pd->session == NULL) {
pd->tunnel = next_tunnel(pn, pd->tunnel);
}
}
if (pd->tunnel == NULL)
pppol2tp_next_tunnel(net, pd);
else
pppol2tp_next_session(net, pd);
/* NULL tunnel and session indicates end of list */
if ((pd->tunnel == NULL) && (pd->session == NULL))
......@@ -2475,11 +1444,11 @@ static void pppol2tp_seq_stop(struct seq_file *p, void *v)
static void pppol2tp_seq_tunnel_show(struct seq_file *m, void *v)
{
struct pppol2tp_tunnel *tunnel = v;
struct l2tp_tunnel *tunnel = v;
seq_printf(m, "\nTUNNEL '%s', %c %d\n",
tunnel->name,
(tunnel == tunnel->sock->sk_user_data) ? 'Y':'N',
(tunnel == tunnel->sock->sk_user_data) ? 'Y' : 'N',
atomic_read(&tunnel->ref_count) - 1);
seq_printf(m, " %08x %llu/%llu/%llu %llu/%llu/%llu\n",
tunnel->debug,
......@@ -2493,19 +1462,27 @@ static void pppol2tp_seq_tunnel_show(struct seq_file *m, void *v)
static void pppol2tp_seq_session_show(struct seq_file *m, void *v)
{
struct pppol2tp_session *session = v;
struct l2tp_session *session = v;
struct l2tp_tunnel *tunnel = session->tunnel;
struct pppol2tp_session *ps = l2tp_session_priv(session);
u32 ip = 0;
u16 port = 0;
if (tunnel->sock) {
struct inet_sock *inet = inet_sk(tunnel->sock);
ip = ntohl(inet->inet_saddr);
port = ntohs(inet->inet_sport);
}
seq_printf(m, " SESSION '%s' %08X/%d %04X/%04X -> "
"%04X/%04X %d %c\n",
session->name,
ntohl(session->tunnel_addr.addr.sin_addr.s_addr),
ntohs(session->tunnel_addr.addr.sin_port),
session->tunnel_addr.s_tunnel,
session->tunnel_addr.s_session,
session->tunnel_addr.d_tunnel,
session->tunnel_addr.d_session,
session->sock->sk_state,
(session == session->sock->sk_user_data) ?
session->name, ip, port,
tunnel->tunnel_id,
session->session_id,
tunnel->peer_tunnel_id,
session->peer_session_id,
ps->sock->sk_state,
(session == ps->sock->sk_user_data) ?
'Y' : 'N');
seq_printf(m, " %d/%d/%c/%c/%s %08x %u\n",
session->mtu, session->mru,
......@@ -2578,6 +1555,36 @@ static const struct file_operations pppol2tp_proc_fops = {
#endif /* CONFIG_PROC_FS */
/*****************************************************************************
* Network namespace
*****************************************************************************/
static __net_init int pppol2tp_init_net(struct net *net)
{
struct proc_dir_entry *pde;
int err = 0;
pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops);
if (!pde) {
err = -ENOMEM;
goto out;
}
out:
return err;
}
static __net_exit void pppol2tp_exit_net(struct net *net)
{
proc_net_remove(net, "pppol2tp");
}
static struct pernet_operations pppol2tp_net_ops = {
.init = pppol2tp_init_net,
.exit = pppol2tp_exit_net,
.id = &pppol2tp_net_id,
};
/*****************************************************************************
* Init and cleanup
*****************************************************************************/
......@@ -2607,74 +1614,45 @@ static struct pppox_proto pppol2tp_proto = {
.ioctl = pppol2tp_ioctl
};
static __net_init int pppol2tp_init_net(struct net *net)
{
struct pppol2tp_net *pn = pppol2tp_pernet(net);
struct proc_dir_entry *pde;
INIT_LIST_HEAD(&pn->pppol2tp_tunnel_list);
rwlock_init(&pn->pppol2tp_tunnel_list_lock);
pde = proc_net_fops_create(net, "pppol2tp", S_IRUGO, &pppol2tp_proc_fops);
#ifdef CONFIG_PROC_FS
if (!pde)
return -ENOMEM;
#endif
return 0;
}
static __net_exit void pppol2tp_exit_net(struct net *net)
{
proc_net_remove(net, "pppol2tp");
}
static struct pernet_operations pppol2tp_net_ops = {
.init = pppol2tp_init_net,
.exit = pppol2tp_exit_net,
.id = &pppol2tp_net_id,
.size = sizeof(struct pppol2tp_net),
};
static int __init pppol2tp_init(void)
{
int err;
err = proto_register(&pppol2tp_sk_proto, 0);
err = register_pernet_device(&pppol2tp_net_ops);
if (err)
goto out;
err = register_pppox_proto(PX_PROTO_OL2TP, &pppol2tp_proto);
err = proto_register(&pppol2tp_sk_proto, 0);
if (err)
goto out_unregister_pppol2tp_proto;
goto out_unregister_pppol2tp_pernet;
err = register_pernet_device(&pppol2tp_net_ops);
err = register_pppox_proto(PX_PROTO_OL2TP, &pppol2tp_proto);
if (err)
goto out_unregister_pppox_proto;
goto out_unregister_pppol2tp_proto;
printk(KERN_INFO "PPPoL2TP kernel driver, %s\n",
PPPOL2TP_DRV_VERSION);
out:
return err;
out_unregister_pppox_proto:
unregister_pppox_proto(PX_PROTO_OL2TP);
out_unregister_pppol2tp_proto:
proto_unregister(&pppol2tp_sk_proto);
out_unregister_pppol2tp_pernet:
unregister_pernet_device(&pppol2tp_net_ops);
goto out;
}
static void __exit pppol2tp_exit(void)
{
unregister_pppox_proto(PX_PROTO_OL2TP);
unregister_pernet_device(&pppol2tp_net_ops);
proto_unregister(&pppol2tp_sk_proto);
unregister_pernet_device(&pppol2tp_net_ops);
}
module_init(pppol2tp_init);
module_exit(pppol2tp_exit);
MODULE_AUTHOR("Martijn van Oosterhout <kleptog@svana.org>, "
"James Chapman <jchapman@katalix.com>");
MODULE_AUTHOR("James Chapman <jchapman@katalix.com>");
MODULE_DESCRIPTION("PPP over L2TP over UDP");
MODULE_LICENSE("GPL");
MODULE_VERSION(PPPOL2TP_DRV_VERSION);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment