summaryrefslogtreecommitdiff
path: root/drivers/net/ppp
diff options
context:
space:
mode:
authorChia-chi Yeh <chiachi@android.com>2012-04-16 13:03:18 +0530
committerVarun Wadekar <vwadekar@nvidia.com>2012-04-16 15:15:55 +0530
commitec776dc4c443f95d1a0f22a27e3940a30e244ba6 (patch)
treeb95d0283373ac3caf77432386b4d697433d70ced /drivers/net/ppp
parent2f4d3bce3ebbccc71b7f1266354e817f3a86d20e (diff)
net: add PPP on L2TP Access Concentrator (PPPoLAC) driver.
Change-Id: I3ae3ee7520951ae24269db0ef2898c6455cf6bcc Signed-off-by: Chia-chi Yeh <chiachi@android.com> ppolac: dont include px_proto define in if_pppolac.h Change-Id: I55bc9cf91ea0e9e8f7bf5d6e241d188e1269343a Signed-off-by: Dima Zavin <dima@android.com> Conflicts: drivers/net/ppp/Kconfig drivers/net/ppp/Makefile drivers/net/ppp/pppolac.c include/linux/if_pppox.h Signed-off-by: Varun Wadekar <vwadekar@nvidia.com>
Diffstat (limited to 'drivers/net/ppp')
-rw-r--r--drivers/net/ppp/Kconfig8
-rw-r--r--drivers/net/ppp/Makefile2
-rw-r--r--drivers/net/ppp/pppolac.c218
3 files changed, 65 insertions, 163 deletions
diff --git a/drivers/net/ppp/Kconfig b/drivers/net/ppp/Kconfig
index 7936ae4cbf7a..382bd6fb07a7 100644
--- a/drivers/net/ppp/Kconfig
+++ b/drivers/net/ppp/Kconfig
@@ -157,14 +157,6 @@ config PPPOLAC
and a PPP channel, but only permits one session per socket. Thus it is
fairly simple and suited for clients.
-config PPPOPNS
- tristate "PPP on PPTP Network Server"
- depends on PPP && INET
- help
- PPTP (RFC 2637) is a tunneling protocol widely used in virtual private
- networks. This driver handles PPTP data packets between a RAW socket
- and a PPP channel. It is fairly simple and easy to use.
-
config PPP_ASYNC
tristate "PPP support for async serial ports"
depends on PPP
diff --git a/drivers/net/ppp/Makefile b/drivers/net/ppp/Makefile
index d283d03c4683..647e11d47377 100644
--- a/drivers/net/ppp/Makefile
+++ b/drivers/net/ppp/Makefile
@@ -12,4 +12,4 @@ obj-$(CONFIG_PPPOE) += pppox.o pppoe.o
obj-$(CONFIG_PPPOL2TP) += pppox.o
obj-$(CONFIG_PPTP) += pppox.o pptp.o
obj-$(CONFIG_PPPOLAC) += pppox.o pppolac.o
-obj-$(CONFIG_PPPOPNS) += pppox.o pppopns.o
+
diff --git a/drivers/net/ppp/pppolac.c b/drivers/net/ppp/pppolac.c
index a5d3d634fd9a..8843a9d30911 100644
--- a/drivers/net/ppp/pppolac.c
+++ b/drivers/net/ppp/pppolac.c
@@ -3,6 +3,7 @@
* Driver for PPP on L2TP Access Concentrator / PPPoLAC Socket (RFC 2661)
*
* Copyright (C) 2009 Google, Inc.
+ * Author: Chia-chi Yeh <chiachi@android.com>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -15,19 +16,14 @@
*/
/* This driver handles L2TP data packets between a UDP socket and a PPP channel.
- * The socket must keep connected, and only one session per socket is permitted.
- * Sequencing of outgoing packets is controlled by LNS. Incoming packets with
- * sequences are reordered within a sliding window of one second. Currently
- * reordering only happens when a packet is received. It is done for simplicity
- * since no additional locks or threads are required. This driver only works on
- * IPv4 due to the lack of UDP encapsulation support in IPv6. */
+ * To keep things simple, only one session per socket is permitted. Packets are
+ * sent via the socket, so it must keep connected to the same address. One must
+ * not set sequencing in ICCN but let LNS controll it. Currently this driver
+ * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */
#include <linux/module.h>
-#include <linux/jiffies.h>
-#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/file.h>
-#include <linux/netdevice.h>
#include <linux/net.h>
#include <linux/udp.h>
#include <linux/ppp_defs.h>
@@ -35,14 +31,13 @@
#include <linux/if_pppox.h>
#include <linux/ppp_channel.h>
#include <net/tcp_states.h>
-#include <asm/uaccess.h>
-#define L2TP_CONTROL_BIT 0x80
-#define L2TP_LENGTH_BIT 0x40
-#define L2TP_SEQUENCE_BIT 0x08
-#define L2TP_OFFSET_BIT 0x02
-#define L2TP_VERSION 0x02
+#define L2TP_CONTROL_MASK 0x80
#define L2TP_VERSION_MASK 0x0F
+#define L2TP_VERSION 0x02
+#define L2TP_LENGTH_MASK 0x40
+#define L2TP_OFFSET_MASK 0x02
+#define L2TP_SEQUENCE_MASK 0x08
#define PPP_ADDR 0xFF
#define PPP_CTRL 0x03
@@ -56,36 +51,22 @@ static inline union unaligned *unaligned(void *ptr)
return (union unaligned *)ptr;
}
-struct meta {
- __u32 sequence;
- __u32 timestamp;
-};
-
-static inline struct meta *skb_meta(struct sk_buff *skb)
-{
- return (struct meta *)skb->cb;
-}
-
-/******************************************************************************/
-
-static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
+static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb)
{
- struct sock *sk = (struct sock *)sk_udp->sk_user_data;
- struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac;
- struct meta *meta = skb_meta(skb);
- __u32 now = jiffies;
+ struct sock *sk;
+ struct pppolac_opt *opt;
__u8 bits;
__u8 *ptr;
- /* Drop the packet if L2TP header is missing. */
+ /* Drop the packet if it is too short. */
if (skb->len < sizeof(struct udphdr) + 6)
goto drop;
/* Put it back if it is a control packet. */
- if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT)
- return opt->backlog_rcv(sk_udp, skb);
+ if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_MASK)
+ return 1;
- /* Skip UDP header. */
+ /* Now the packet is ours. Skip UDP header. */
skb_pull(skb, sizeof(struct udphdr));
/* Check the version. */
@@ -95,34 +76,45 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
ptr = &skb->data[2];
/* Check the length if it is present. */
- if (bits & L2TP_LENGTH_BIT) {
+ if (bits & L2TP_LENGTH_MASK) {
if ((ptr[0] << 8 | ptr[1]) != skb->len)
goto drop;
ptr += 2;
}
/* Skip all fields including optional ones. */
- if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) +
- (bits & L2TP_LENGTH_BIT ? 2 : 0) +
- (bits & L2TP_OFFSET_BIT ? 2 : 0)))
+ if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_MASK ? 4 : 0) +
+ (bits & L2TP_LENGTH_MASK ? 2 : 0) +
+ (bits & L2TP_OFFSET_MASK ? 2 : 0)))
goto drop;
/* Skip the offset padding if it is present. */
- if (bits & L2TP_OFFSET_BIT &&
+ if (bits & L2TP_OFFSET_MASK &&
!skb_pull(skb, skb->data[-2] << 8 | skb->data[-1]))
goto drop;
- /* Check the tunnel and the session. */
- if (unaligned(ptr)->u32 != opt->local)
+ /* Now ptr is pointing to the tunnel and skb is pointing to the payload.
+ * We have to lock sk_udp to prevent sk from being closed. */
+ lock_sock(sk_udp);
+ sk = sk_udp->sk_user_data;
+ if (!sk) {
+ release_sock(sk_udp);
goto drop;
+ }
+ sock_hold(sk);
+ release_sock(sk_udp);
+ opt = &pppox_sk(sk)->proto.lac;
- /* Check the sequence if it is present. */
- if (bits & L2TP_SEQUENCE_BIT) {
- meta->sequence = ptr[4] << 8 | ptr[5];
- if ((__s16)(meta->sequence - opt->recv_sequence) < 0)
- goto drop;
+ /* Check the tunnel and the session. */
+ if (unaligned(ptr)->u32 != opt->local) {
+ sock_put(sk);
+ goto drop;
}
+ /* Check the sequence if it is present. According to RFC 2661 page 10
+ * and 43, the only thing to do is updating opt->sequencing. */
+ opt->sequencing = bits & L2TP_SEQUENCE_MASK;
+
/* Skip PPP address and control if they are present. */
if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
skb->data[1] == PPP_CTRL)
@@ -132,97 +124,26 @@ static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
if (skb->len >= 1 && skb->data[0] & 1)
skb_push(skb, 1)[0] = 0;
- /* Drop the packet if PPP protocol is missing. */
- if (skb->len < 2)
- goto drop;
-
- /* Perform reordering if sequencing is enabled. */
- atomic_set(&opt->sequencing, bits & L2TP_SEQUENCE_BIT);
- if (bits & L2TP_SEQUENCE_BIT) {
- struct sk_buff *skb1;
-
- /* Insert the packet into receive queue in order. */
- skb_set_owner_r(skb, sk);
- skb_queue_walk(&sk->sk_receive_queue, skb1) {
- struct meta *meta1 = skb_meta(skb1);
- __s16 order = meta->sequence - meta1->sequence;
- if (order == 0)
- goto drop;
- if (order < 0) {
- meta->timestamp = meta1->timestamp;
- skb_insert(skb1, skb, &sk->sk_receive_queue);
- skb = NULL;
- break;
- }
- }
- if (skb) {
- meta->timestamp = now;
- skb_queue_tail(&sk->sk_receive_queue, skb);
- }
-
- /* Remove packets from receive queue as long as
- * 1. the receive buffer is full,
- * 2. they are queued longer than one second, or
- * 3. there are no missing packets before them. */
- skb_queue_walk_safe(&sk->sk_receive_queue, skb, skb1) {
- meta = skb_meta(skb);
- if (atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf &&
- now - meta->timestamp < HZ &&
- meta->sequence != opt->recv_sequence)
- break;
- skb_unlink(skb, &sk->sk_receive_queue);
- opt->recv_sequence = (__u16)(meta->sequence + 1);
- skb_orphan(skb);
- ppp_input(&pppox_sk(sk)->chan, skb);
- }
- return NET_RX_SUCCESS;
- }
-
- /* Flush receive queue if sequencing is disabled. */
- skb_queue_purge(&sk->sk_receive_queue);
+ /* Finally, deliver the packet to PPP channel. We have to lock sk to
+ * prevent another thread from calling pppox_unbind_sock(). */
skb_orphan(skb);
+ lock_sock(sk);
ppp_input(&pppox_sk(sk)->chan, skb);
- return NET_RX_SUCCESS;
+ release_sock(sk);
+ sock_put(sk);
+ return 0;
+
drop:
kfree_skb(skb);
- return NET_RX_DROP;
-}
-
-static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb)
-{
- sock_hold(sk_udp);
- sk_receive_skb(sk_udp, skb, 0);
return 0;
}
-static struct sk_buff_head delivery_queue;
-
-static void pppolac_xmit_core(struct work_struct *delivery_work)
-{
- mm_segment_t old_fs = get_fs();
- struct sk_buff *skb;
-
- set_fs(KERNEL_DS);
- while ((skb = skb_dequeue(&delivery_queue))) {
- struct sock *sk_udp = skb->sk;
- struct kvec iov = {.iov_base = skb->data, .iov_len = skb->len};
- struct msghdr msg = {
- .msg_iov = (struct iovec *)&iov,
- .msg_iovlen = 1,
- .msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT,
- };
- sk_udp->sk_prot->sendmsg(NULL, sk_udp, &msg, skb->len);
- kfree_skb(skb);
- }
- set_fs(old_fs);
-}
-
-static DECLARE_WORK(delivery_work, pppolac_xmit_core);
-
static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb)
{
struct sock *sk_udp = (struct sock *)chan->private;
struct pppolac_opt *opt = &pppox_sk(sk_udp->sk_user_data)->proto.lac;
+ struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT};
+ struct kvec iov;
/* Install PPP address and control. */
skb_push(skb, 2);
@@ -230,14 +151,14 @@ static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb)
skb->data[1] = PPP_CTRL;
/* Install L2TP header. */
- if (atomic_read(&opt->sequencing)) {
+ if (opt->sequencing) {
skb_push(skb, 10);
- skb->data[0] = L2TP_SEQUENCE_BIT;
- skb->data[6] = opt->xmit_sequence >> 8;
- skb->data[7] = opt->xmit_sequence;
+ skb->data[0] = L2TP_SEQUENCE_MASK;
+ skb->data[6] = opt->sequence >> 8;
+ skb->data[7] = opt->sequence;
skb->data[8] = 0;
skb->data[9] = 0;
- opt->xmit_sequence++;
+ opt->sequence++;
} else {
skb_push(skb, 6);
skb->data[0] = 0;
@@ -245,10 +166,11 @@ static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb)
skb->data[1] = L2TP_VERSION;
unaligned(&skb->data[2])->u32 = opt->remote;
- /* Now send the packet via the delivery queue. */
- skb_set_owner_w(skb, sk_udp);
- skb_queue_tail(&delivery_queue, skb);
- schedule_work(&delivery_work);
+ /* Now send the packet via UDP socket. */
+ iov.iov_base = skb->data;
+ iov.iov_len = skb->len;
+ kernel_sendmsg(sk_udp->sk_socket, &msg, &iov, 1, skb->len);
+ kfree_skb(skb);
return 1;
}
@@ -298,23 +220,13 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr,
error = -EBUSY;
if (udp_sk(sk_udp)->encap_type || sk_udp->sk_user_data)
goto out;
- if (!sk_udp->sk_bound_dev_if) {
- struct dst_entry *dst = sk_dst_get(sk_udp);
- error = -ENODEV;
- if (!dst)
- goto out;
- sk_udp->sk_bound_dev_if = dst->dev->ifindex;
- dst_release(dst);
- }
po->chan.hdrlen = 12;
po->chan.private = sk_udp;
po->chan.ops = &pppolac_channel_ops;
- po->chan.mtu = PPP_MRU - 80;
+ po->chan.mtu = PPP_MTU - 80;
po->proto.lac.local = unaligned(&addr->local)->u32;
po->proto.lac.remote = unaligned(&addr->remote)->u32;
- atomic_set(&po->proto.lac.sequencing, 1);
- po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv;
error = ppp_register_channel(&po->chan);
if (error)
@@ -323,8 +235,8 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr,
sk->sk_state = PPPOX_CONNECTED;
udp_sk(sk_udp)->encap_type = UDP_ENCAP_L2TPINUDP;
udp_sk(sk_udp)->encap_rcv = pppolac_recv;
- sk_udp->sk_backlog_rcv = pppolac_recv_core;
sk_udp->sk_user_data = sk;
+
out:
if (sock_udp) {
release_sock(sk_udp);
@@ -351,12 +263,12 @@ static int pppolac_release(struct socket *sock)
if (sk->sk_state != PPPOX_NONE) {
struct sock *sk_udp = (struct sock *)pppox_sk(sk)->chan.private;
lock_sock(sk_udp);
- skb_queue_purge(&sk->sk_receive_queue);
+
pppox_unbind_sock(sk);
+ sk_udp->sk_user_data = NULL;
udp_sk(sk_udp)->encap_type = 0;
udp_sk(sk_udp)->encap_rcv = NULL;
- sk_udp->sk_backlog_rcv = pppox_sk(sk)->proto.lac.backlog_rcv;
- sk_udp->sk_user_data = NULL;
+
release_sock(sk_udp);
sockfd_put(sk_udp->sk_socket);
}
@@ -430,8 +342,6 @@ static int __init pppolac_init(void)
error = register_pppox_proto(PX_PROTO_OLAC, &pppolac_pppox_proto);
if (error)
proto_unregister(&pppolac_proto);
- else
- skb_queue_head_init(&delivery_queue);
return error;
}