From 9343a72db303eefcbe572fcbc265a84979d3fcec Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 12 Jun 2009 01:09:30 +0800 Subject: net: add PPP on PPTP Network Server (PPPoPNS) driver. Signed-off-by: Chia-chi Yeh ppopns: dont include px_proto define in if_pppopns.h Change-Id: I27e687667db5b45182562f4a517a2e6cec6b1350 Signed-off-by: Dima Zavin --- drivers/net/ppp/pppopns.c | 322 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 322 insertions(+) create mode 100644 drivers/net/ppp/pppopns.c (limited to 'drivers/net/ppp/pppopns.c') diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c new file mode 100644 index 00000000000..8885eba8968 --- /dev/null +++ b/drivers/net/ppp/pppopns.c @@ -0,0 +1,322 @@ +/* drivers/net/pppopns.c + * + * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) + * + * Copyright (C) 2009 Google, Inc. + * Author: Chia-chi Yeh + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* This driver handles PPTP data packets between a RAW socket and a PPP channel. + * The socket is created in the kernel space and connected to the same address + * of the control socket. To keep things simple, packets are always sent with + * sequence but without acknowledgement. This driver should work on both IPv4 + * and IPv6. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GRE_HEADER_SIZE 8 + +#define PPTP_GRE_MASK htons(0x2001) +#define PPTP_GRE_SEQ_MASK htons(0x1000) +#define PPTP_GRE_ACK_MASK htons(0x0080) +#define PPTP_GRE_TYPE htons(0x880B) + +#define PPP_ADDR 0xFF +#define PPP_CTRL 0x03 + +struct header { + __u16 bits; + __u16 type; + __u16 length; + __u16 call; + __u32 sequence; +} __attribute__((packed)); + +static void pppopns_recv(struct sock *sk_raw, int length) +{ + struct sock *sk; + struct pppopns_opt *opt; + struct sk_buff *skb; + struct header *hdr; + + /* Lock sk_raw to prevent sk from being closed. */ + lock_sock(sk_raw); + sk = (struct sock *)sk_raw->sk_user_data; + if (!sk) { + release_sock(sk_raw); + return; + } + sock_hold(sk); + release_sock(sk_raw); + opt = &pppox_sk(sk)->proto.pns; + + /* Process packets from the receive queue. */ + while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { + skb_pull(skb, skb_transport_header(skb) - skb->data); + + /* Drop the packet if it is too short. */ + if (skb->len < GRE_HEADER_SIZE) + goto drop; + + /* Check the header. */ + hdr = (struct header *)skb->data; + if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || + (hdr->bits & PPTP_GRE_MASK) != PPTP_GRE_MASK) + goto drop; + + /* Skip all fields including optional ones. */ + if (!skb_pull(skb, GRE_HEADER_SIZE + + (hdr->bits & PPTP_GRE_SEQ_MASK ? 4 : 0) + + (hdr->bits & PPTP_GRE_ACK_MASK ? 4 : 0))) + goto drop; + + /* Check the length. */ + if (skb->len != ntohs(hdr->length)) + goto drop; + + /* Skip PPP address and control if they are present. */ + if (skb->len >= 2 && skb->data[0] == PPP_ADDR && + skb->data[1] == PPP_CTRL) + skb_pull(skb, 2); + + /* Fix PPP protocol if it is compressed. */ + if (skb->len >= 1 && skb->data[0] & 1) + skb_push(skb, 1)[0] = 0; + + /* 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); + release_sock(sk); + continue; +drop: + kfree_skb(skb); + } + sock_put(sk); +} + +static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) +{ + struct sock *sk_raw = (struct sock *)chan->private; + struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns; + struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; + struct kvec iov; + struct header *hdr; + __u16 length; + + /* Install PPP address and control. */ + skb_push(skb, 2); + skb->data[0] = PPP_ADDR; + skb->data[1] = PPP_CTRL; + length = skb->len; + + /* Install PPTP GRE header. */ + hdr = (struct header *)skb_push(skb, 12); + hdr->bits = PPTP_GRE_MASK | PPTP_GRE_SEQ_MASK; + hdr->type = PPTP_GRE_TYPE; + hdr->length = htons(length); + hdr->call = opt->remote; + hdr->sequence = htonl(opt->sequence); + opt->sequence++; + + /* Now send the packet via RAW socket. */ + iov.iov_base = skb->data; + iov.iov_len = skb->len; + kernel_sendmsg(sk_raw->sk_socket, &msg, &iov, 1, skb->len); + kfree_skb(skb); + return 1; +} + +/******************************************************************************/ + +static struct ppp_channel_ops pppopns_channel_ops = { + .start_xmit = pppopns_xmit, +}; + +static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, + int addrlen, int flags) +{ + struct sock *sk = sock->sk; + struct pppox_sock *po = pppox_sk(sk); + struct sockaddr_pppopns *addr = (struct sockaddr_pppopns *)useraddr; + struct sockaddr_storage ss; + struct socket *sock_tcp = NULL; + struct socket *sock_raw = NULL; + struct sock *sk_raw; + int error; + + if (addrlen != sizeof(struct sockaddr_pppopns)) + return -EINVAL; + + lock_sock(sk); + error = -EALREADY; + if (sk->sk_state != PPPOX_NONE) + goto out; + + sock_tcp = sockfd_lookup(addr->tcp_socket, &error); + if (!sock_tcp) + goto out; + error = -EPROTONOSUPPORT; + if (sock_tcp->sk->sk_protocol != IPPROTO_TCP) + goto out; + addrlen = sizeof(struct sockaddr_storage); + error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen); + if (error) + goto out; + + error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw); + if (error) + goto out; + error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0); + if (error) + goto out; + sk_raw = sock_raw->sk; + + po->chan.hdrlen = 14; + po->chan.private = sk_raw; + po->chan.ops = &pppopns_channel_ops; + po->chan.mtu = PPP_MTU - 80; + po->proto.pns.local = addr->local; + po->proto.pns.remote = addr->remote; + + error = ppp_register_channel(&po->chan); + if (error) + goto out; + + sk->sk_state = PPPOX_CONNECTED; + sk_raw->sk_user_data = sk; + sk_raw->sk_data_ready = pppopns_recv; + +out: + if (sock_tcp) + sockfd_put(sock_tcp); + if (error && sock_raw) + sock_release(sock_raw); + release_sock(sk); + return error; +} + +static int pppopns_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (!sk) + return 0; + + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)) { + release_sock(sk); + return -EBADF; + } + + if (sk->sk_state != PPPOX_NONE) { + struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; + lock_sock(sk_raw); + pppox_unbind_sock(sk); + sk_raw->sk_user_data = NULL; + release_sock(sk_raw); + sock_release(sk_raw->sk_socket); + } + + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); + sock_put(sk); + return 0; +} + +/******************************************************************************/ + +static struct proto pppopns_proto = { + .name = "PPPOPNS", + .owner = THIS_MODULE, + .obj_size = sizeof(struct pppox_sock), +}; + +static struct proto_ops pppopns_proto_ops = { + .family = PF_PPPOX, + .owner = THIS_MODULE, + .release = pppopns_release, + .bind = sock_no_bind, + .connect = pppopns_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = sock_no_getname, + .poll = sock_no_poll, + .ioctl = pppox_ioctl, + .listen = sock_no_listen, + .shutdown = sock_no_shutdown, + .setsockopt = sock_no_setsockopt, + .getsockopt = sock_no_getsockopt, + .sendmsg = sock_no_sendmsg, + .recvmsg = sock_no_recvmsg, + .mmap = sock_no_mmap, +}; + +static int pppopns_create(struct net *net, struct socket *sock) +{ + struct sock *sk; + + sk = sk_alloc(net, PF_PPPOX, GFP_KERNEL, &pppopns_proto); + if (!sk) + return -ENOMEM; + + sock_init_data(sock, sk); + sock->state = SS_UNCONNECTED; + sock->ops = &pppopns_proto_ops; + sk->sk_protocol = PX_PROTO_OPNS; + sk->sk_state = PPPOX_NONE; + return 0; +} + +/******************************************************************************/ + +static struct pppox_proto pppopns_pppox_proto = { + .create = pppopns_create, + .owner = THIS_MODULE, +}; + +static int __init pppopns_init(void) +{ + int error; + + error = proto_register(&pppopns_proto, 0); + if (error) + return error; + + error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto); + if (error) + proto_unregister(&pppopns_proto); + return error; +} + +static void __exit pppopns_exit(void) +{ + unregister_pppox_proto(PX_PROTO_OPNS); + proto_unregister(&pppopns_proto); +} + +module_init(pppopns_init); +module_exit(pppopns_exit); + +MODULE_DESCRIPTION("PPP on PPTP Network Server (PPPoPNS)"); +MODULE_AUTHOR("Chia-chi Yeh "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 6850f796dc74cac0113cbbd5374343b33a1687a8 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Sat, 13 Jun 2009 02:29:04 +0800 Subject: net: PPPoPNS and PPPoLAC fixes. net: Fix a bitmask in PPPoPNS and rename constants in PPPoPNS and PPPoLAC. Signed-off-by: Chia-chi Yeh net: Fix a potential deadlock while releasing PPPoLAC/PPPoPNS socket. PPP driver guarantees that no thread will be executing start_xmit() after returning from ppp_unregister_channel(). To achieve this, a spinlock (downl) is used. In pppolac_release(), ppp_unregister_channel() is called after sk_udp is locked. At the same time, another thread might be running in pppolac_xmit() with downl. Thus a deadlock will occur if the thread tries to lock sk_udp. The same situation might happen on sk_raw in pppopns_release(). Signed-off-by: Chia-chi Yeh net: Force PPPoLAC and PPPoPNS to bind an interface before creating PPP channel. It is common to manipulate the routing table after configuring PPP device. Since both PPPoLAC and PPPoPNS run over IP, care must be taken to make sure that there is no loop in the routing table. Although this can be done by adding a host route, it might still cause problems when the interface is down for some reason. To solve this, this patch forces both drivers to bind an interface before creating PPP channel, so the system will not re-route the tunneling sockets to another interface when the original one is down. Another benefit is that now the host route is no longer required, so there is no need to remove it when PPP channel is closed. Signed-off-by: Chia-chi Yeh net: Avoid sleep-inside-spinlock in PPPoLAC and PPPoPNS. Since recv() and xmit() are called with a spinlock held, routines which might sleep cannot be used. This issue is solved by following changes: Incoming packets are now processed in backlog handler, recv_core(), instead of recv(). Since backlog handler is always executed with socket spinlock held, the requirement of ppp_input() is still satisfied. Outgoing packets are now processed in workqueue handler, xmit_core(), instead of xmit(). Note that kernel_sendmsg() is no longer used to prevent touching dead sockets. In release(), lock_sock() and pppox_unbind_sock() ensure that no thread is in recv_core() or xmit(). Then socket handlers are restored before release_sock(), so no packets will leak in backlog queue. Signed-off-by: Chia-chi Yeh net: Fix msg_iovlen in PPPoLAC and PPPoPNS. Although any positive value should work (which is always true in both drivers), the correct value should be 1. Signed-off-by: Chia-chi Yeh --- drivers/net/ppp/pppopns.c | 177 +++++++++++++++++++++++++++------------------- 1 file changed, 106 insertions(+), 71 deletions(-) (limited to 'drivers/net/ppp/pppopns.c') diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index 8885eba8968..298097127c9 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -3,7 +3,6 @@ * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637) * * Copyright (C) 2009 Google, Inc. - * Author: Chia-chi Yeh * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -22,20 +21,24 @@ * and IPv6. */ #include +#include #include #include +#include #include #include #include #include #include #include +#include #define GRE_HEADER_SIZE 8 -#define PPTP_GRE_MASK htons(0x2001) -#define PPTP_GRE_SEQ_MASK htons(0x1000) -#define PPTP_GRE_ACK_MASK htons(0x0080) +#define PPTP_GRE_BITS htons(0x2001) +#define PPTP_GRE_BITS_MASK htons(0xEF7F) +#define PPTP_GRE_SEQ_BIT htons(0x1000) +#define PPTP_GRE_ACK_BIT htons(0x0080) #define PPTP_GRE_TYPE htons(0x880B) #define PPP_ADDR 0xFF @@ -49,76 +52,90 @@ struct header { __u32 sequence; } __attribute__((packed)); -static void pppopns_recv(struct sock *sk_raw, int length) +static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) { - struct sock *sk; - struct pppopns_opt *opt; - struct sk_buff *skb; + struct sock *sk = (struct sock *)sk_raw->sk_user_data; + struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; struct header *hdr; - /* Lock sk_raw to prevent sk from being closed. */ - lock_sock(sk_raw); - sk = (struct sock *)sk_raw->sk_user_data; - if (!sk) { - release_sock(sk_raw); - return; - } - sock_hold(sk); - release_sock(sk_raw); - opt = &pppox_sk(sk)->proto.pns; + /* Skip transport header */ + skb_pull(skb, skb_transport_header(skb) - skb->data); + + /* Drop the packet if it is too short. */ + if (skb->len < GRE_HEADER_SIZE) + goto drop; + + /* Check the header. */ + hdr = (struct header *)skb->data; + if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || + (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) + goto drop; + + /* Skip all fields including optional ones. */ + if (!skb_pull(skb, GRE_HEADER_SIZE + + (hdr->bits & PPTP_GRE_SEQ_BIT ? 4 : 0) + + (hdr->bits & PPTP_GRE_ACK_BIT ? 4 : 0))) + goto drop; + + /* Check the length. */ + if (skb->len != ntohs(hdr->length)) + goto drop; + + /* Skip PPP address and control if they are present. */ + if (skb->len >= 2 && skb->data[0] == PPP_ADDR && + skb->data[1] == PPP_CTRL) + skb_pull(skb, 2); + + /* Fix PPP protocol if it is compressed. */ + if (skb->len >= 1 && skb->data[0] & 1) + skb_push(skb, 1)[0] = 0; + + /* Finally, deliver the packet to PPP channel. */ + skb_orphan(skb); + ppp_input(&pppox_sk(sk)->chan, skb); + return NET_RX_SUCCESS; +drop: + kfree_skb(skb); + return NET_RX_DROP; +} - /* Process packets from the receive queue. */ +static void pppopns_recv(struct sock *sk_raw, int length) +{ + struct sk_buff *skb; while ((skb = skb_dequeue(&sk_raw->sk_receive_queue))) { - skb_pull(skb, skb_transport_header(skb) - skb->data); - - /* Drop the packet if it is too short. */ - if (skb->len < GRE_HEADER_SIZE) - goto drop; - - /* Check the header. */ - hdr = (struct header *)skb->data; - if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || - (hdr->bits & PPTP_GRE_MASK) != PPTP_GRE_MASK) - goto drop; - - /* Skip all fields including optional ones. */ - if (!skb_pull(skb, GRE_HEADER_SIZE + - (hdr->bits & PPTP_GRE_SEQ_MASK ? 4 : 0) + - (hdr->bits & PPTP_GRE_ACK_MASK ? 4 : 0))) - goto drop; - - /* Check the length. */ - if (skb->len != ntohs(hdr->length)) - goto drop; - - /* Skip PPP address and control if they are present. */ - if (skb->len >= 2 && skb->data[0] == PPP_ADDR && - skb->data[1] == PPP_CTRL) - skb_pull(skb, 2); - - /* Fix PPP protocol if it is compressed. */ - if (skb->len >= 1 && skb->data[0] & 1) - skb_push(skb, 1)[0] = 0; - - /* 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); - release_sock(sk); - continue; -drop: + sock_hold(sk_raw); + sk_receive_skb(sk_raw, skb, 0); + } +} + +static struct sk_buff_head delivery_queue; + +static void pppopns_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_raw = 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_raw->sk_prot->sendmsg(NULL, sk_raw, &msg, skb->len); kfree_skb(skb); } - sock_put(sk); + set_fs(old_fs); } +static DECLARE_WORK(delivery_work, pppopns_xmit_core); + static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) { struct sock *sk_raw = (struct sock *)chan->private; struct pppopns_opt *opt = &pppox_sk(sk_raw->sk_user_data)->proto.pns; - struct msghdr msg = {.msg_flags = MSG_NOSIGNAL | MSG_DONTWAIT}; - struct kvec iov; struct header *hdr; __u16 length; @@ -130,18 +147,17 @@ static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) /* Install PPTP GRE header. */ hdr = (struct header *)skb_push(skb, 12); - hdr->bits = PPTP_GRE_MASK | PPTP_GRE_SEQ_MASK; + hdr->bits = PPTP_GRE_BITS | PPTP_GRE_SEQ_BIT; hdr->type = PPTP_GRE_TYPE; hdr->length = htons(length); hdr->call = opt->remote; hdr->sequence = htonl(opt->sequence); opt->sequence++; - /* Now send the packet via RAW socket. */ - iov.iov_base = skb->data; - iov.iov_len = skb->len; - kernel_sendmsg(sk_raw->sk_socket, &msg, &iov, 1, skb->len); - kfree_skb(skb); + /* Now send the packet via the delivery queue. */ + skb_set_owner_w(skb, sk_raw); + skb_queue_tail(&delivery_queue, skb); + schedule_work(&delivery_work); return 1; } @@ -160,6 +176,7 @@ static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, struct sockaddr_storage ss; struct socket *sock_tcp = NULL; struct socket *sock_raw = NULL; + struct sock *sk_tcp; struct sock *sk_raw; int error; @@ -174,21 +191,31 @@ static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, sock_tcp = sockfd_lookup(addr->tcp_socket, &error); if (!sock_tcp) goto out; + sk_tcp = sock_tcp->sk; error = -EPROTONOSUPPORT; - if (sock_tcp->sk->sk_protocol != IPPROTO_TCP) + if (sk_tcp->sk_protocol != IPPROTO_TCP) goto out; addrlen = sizeof(struct sockaddr_storage); error = kernel_getpeername(sock_tcp, (struct sockaddr *)&ss, &addrlen); if (error) goto out; + if (!sk_tcp->sk_bound_dev_if) { + struct dst_entry *dst = sk_dst_get(sk_tcp); + error = -ENODEV; + if (!dst) + goto out; + sk_tcp->sk_bound_dev_if = dst->dev->ifindex; + dst_release(dst); + } error = sock_create(ss.ss_family, SOCK_RAW, IPPROTO_GRE, &sock_raw); if (error) goto out; + sk_raw = sock_raw->sk; + sk_raw->sk_bound_dev_if = sk_tcp->sk_bound_dev_if; error = kernel_connect(sock_raw, (struct sockaddr *)&ss, addrlen, 0); if (error) goto out; - sk_raw = sock_raw->sk; po->chan.hdrlen = 14; po->chan.private = sk_raw; @@ -196,15 +223,19 @@ static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, po->chan.mtu = PPP_MTU - 80; po->proto.pns.local = addr->local; po->proto.pns.remote = addr->remote; + po->proto.pns.data_ready = sk_raw->sk_data_ready; + po->proto.pns.backlog_rcv = sk_raw->sk_backlog_rcv; error = ppp_register_channel(&po->chan); if (error) goto out; sk->sk_state = PPPOX_CONNECTED; - sk_raw->sk_user_data = sk; + lock_sock(sk_raw); sk_raw->sk_data_ready = pppopns_recv; - + sk_raw->sk_backlog_rcv = pppopns_recv_core; + sk_raw->sk_user_data = sk; + release_sock(sk_raw); out: if (sock_tcp) sockfd_put(sock_tcp); @@ -231,6 +262,8 @@ static int pppopns_release(struct socket *sock) struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_raw); pppox_unbind_sock(sk); + sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; + sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; sk_raw->sk_user_data = NULL; release_sock(sk_raw); sock_release(sk_raw->sk_socket); @@ -305,6 +338,8 @@ static int __init pppopns_init(void) error = register_pppox_proto(PX_PROTO_OPNS, &pppopns_pppox_proto); if (error) proto_unregister(&pppopns_proto); + else + skb_queue_head_init(&delivery_queue); return error; } -- cgit v1.2.3 From 4974de8b5da8c91bfba253a3ac663db30c7f3ad5 Mon Sep 17 00:00:00 2001 From: Chia-chi Yeh Date: Fri, 15 Apr 2011 15:22:09 -0700 Subject: net: Reorder incoming packets in PPPoLAC and PPPoPNS. PPP handles packet loss but does not work with out of order packets. This change performs reordering of incoming data packets within a sliding window of one second. Since sequence number is optional, receiving a packet without it will drop all queued packets. Currently the logic is triggered by incoming packets, so queued packets have to wait till another packet is arrived. It is done for simplicity since no additional locks or threads are required. For reliable protocols, a retransmission will kick it. For unreliable protocols, queued packets just seem like packet loss. Time-critical protocols might be broken, but they never work with queueing anyway. Signed-off-by: Chia-chi Yeh --- drivers/net/ppp/pppopns.c | 87 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 8 deletions(-) (limited to 'drivers/net/ppp/pppopns.c') diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index 298097127c9..fb819844793 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -16,11 +16,14 @@ /* This driver handles PPTP data packets between a RAW socket and a PPP channel. * The socket is created in the kernel space and connected to the same address - * of the control socket. To keep things simple, packets are always sent with - * sequence but without acknowledgement. This driver should work on both IPv4 - * and IPv6. */ + * of the control socket. Outgoing packets are always sent with sequences but + * without acknowledgements. 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 should work on both IPv4 and IPv6. */ #include +#include #include #include #include @@ -52,21 +55,35 @@ struct header { __u32 sequence; } __attribute__((packed)); +struct meta { + __u32 sequence; + __u32 timestamp; +}; + +static inline struct meta *skb_meta(struct sk_buff *skb) +{ + return (struct meta *)skb->cb; +} + +/******************************************************************************/ + static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) { struct sock *sk = (struct sock *)sk_raw->sk_user_data; struct pppopns_opt *opt = &pppox_sk(sk)->proto.pns; + struct meta *meta = skb_meta(skb); + __u32 now = jiffies; struct header *hdr; /* Skip transport header */ skb_pull(skb, skb_transport_header(skb) - skb->data); - /* Drop the packet if it is too short. */ + /* Drop the packet if GRE header is missing. */ if (skb->len < GRE_HEADER_SIZE) goto drop; + hdr = (struct header *)skb->data; /* Check the header. */ - hdr = (struct header *)skb->data; if (hdr->type != PPTP_GRE_TYPE || hdr->call != opt->local || (hdr->bits & PPTP_GRE_BITS_MASK) != PPTP_GRE_BITS) goto drop; @@ -81,6 +98,13 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) if (skb->len != ntohs(hdr->length)) goto drop; + /* Check the sequence if it is present. */ + if (hdr->bits & PPTP_GRE_SEQ_BIT) { + meta->sequence = ntohl(hdr->sequence); + if ((__s32)(meta->sequence - opt->recv_sequence) < 0) + goto drop; + } + /* Skip PPP address and control if they are present. */ if (skb->len >= 2 && skb->data[0] == PPP_ADDR && skb->data[1] == PPP_CTRL) @@ -90,7 +114,53 @@ static int pppopns_recv_core(struct sock *sk_raw, struct sk_buff *skb) if (skb->len >= 1 && skb->data[0] & 1) skb_push(skb, 1)[0] = 0; - /* Finally, deliver the packet to PPP channel. */ + /* Drop the packet if PPP protocol is missing. */ + if (skb->len < 2) + goto drop; + + /* Perform reordering if sequencing is enabled. */ + if (hdr->bits & PPTP_GRE_SEQ_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); + __s32 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 = 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); skb_orphan(skb); ppp_input(&pppox_sk(sk)->chan, skb); return NET_RX_SUCCESS; @@ -151,8 +221,8 @@ static int pppopns_xmit(struct ppp_channel *chan, struct sk_buff *skb) hdr->type = PPTP_GRE_TYPE; hdr->length = htons(length); hdr->call = opt->remote; - hdr->sequence = htonl(opt->sequence); - opt->sequence++; + hdr->sequence = htonl(opt->xmit_sequence); + opt->xmit_sequence++; /* Now send the packet via the delivery queue. */ skb_set_owner_w(skb, sk_raw); @@ -261,6 +331,7 @@ static int pppopns_release(struct socket *sock) if (sk->sk_state != PPPOX_NONE) { struct sock *sk_raw = (struct sock *)pppox_sk(sk)->chan.private; lock_sock(sk_raw); + skb_queue_purge(&sk->sk_receive_queue); pppox_unbind_sock(sk); sk_raw->sk_data_ready = pppox_sk(sk)->proto.pns.data_ready; sk_raw->sk_backlog_rcv = pppox_sk(sk)->proto.pns.backlog_rcv; -- cgit v1.2.3 From df974627d4ac90509b9aca13b5b5ba58e7b94c43 Mon Sep 17 00:00:00 2001 From: JP Abgrall Date: Thu, 20 Sep 2012 16:34:10 -0700 Subject: net: PPPoPNS and PPPoLAC update to use PPP_MRU instead of PPP_MRU Some headers files were moved around and some defines renamed. Signed-off-by: JP Abgrall --- drivers/net/ppp/pppopns.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/net/ppp/pppopns.c') diff --git a/drivers/net/ppp/pppopns.c b/drivers/net/ppp/pppopns.c index fb819844793..6016d29c066 100644 --- a/drivers/net/ppp/pppopns.c +++ b/drivers/net/ppp/pppopns.c @@ -290,7 +290,7 @@ static int pppopns_connect(struct socket *sock, struct sockaddr *useraddr, po->chan.hdrlen = 14; po->chan.private = sk_raw; po->chan.ops = &pppopns_channel_ops; - po->chan.mtu = PPP_MTU - 80; + po->chan.mtu = PPP_MRU - 80; po->proto.pns.local = addr->local; po->proto.pns.remote = addr->remote; po->proto.pns.data_ready = sk_raw->sk_data_ready; -- cgit v1.2.3