net: PPPoPNS and PPPoLAC fixes.
authorChia-chi Yeh <chiachi@android.com>
Fri, 12 Jun 2009 18:29:04 +0000 (02:29 +0800)
committerColin Cross <ccross@android.com>
Tue, 14 Jun 2011 16:09:14 +0000 (09:09 -0700)
net: Fix a bitmask in PPPoPNS and rename constants in PPPoPNS and PPPoLAC.

Signed-off-by: Chia-chi Yeh <chiachi@android.com>
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 <chiachi@android.com>
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 <chiachi@android.com>
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 <chiachi@android.com>
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 <chiachi@android.com>
drivers/net/pppolac.c
drivers/net/pppopns.c
include/linux/if_pppox.h

index 8843a9d30911452014cde6afd590a37b939d857c..af3202a920a005b1f2d379def77a73f8cf3f40af 100644 (file)
@@ -3,7 +3,6 @@
  * 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
  * only works on IPv4 due to the lack of UDP encapsulation support in IPv6. */
 
 #include <linux/module.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>
 #include <linux/if_pppox.h>
 #include <linux/ppp_channel.h>
 #include <net/tcp_states.h>
+#include <asm/uaccess.h>
 
-#define L2TP_CONTROL_MASK      0x80
-#define L2TP_VERSION_MASK      0x0F
+#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_LENGTH_MASK       0x40
-#define L2TP_OFFSET_MASK       0x02
-#define L2TP_SEQUENCE_MASK     0x08
+#define L2TP_VERSION_MASK      0x0F
 
 #define PPP_ADDR       0xFF
 #define PPP_CTRL       0x03
@@ -51,10 +53,10 @@ static inline union unaligned *unaligned(void *ptr)
        return (union unaligned *)ptr;
 }
 
-static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb)
+static int pppolac_recv_core(struct sock *sk_udp, struct sk_buff *skb)
 {
-       struct sock *sk;
-       struct pppolac_opt *opt;
+       struct sock *sk = (struct sock *)sk_udp->sk_user_data;
+       struct pppolac_opt *opt = &pppox_sk(sk)->proto.lac;
        __u8 bits;
        __u8 *ptr;
 
@@ -63,10 +65,10 @@ static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb)
                goto drop;
 
        /* Put it back if it is a control packet. */
-       if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_MASK)
-               return 1;
+       if (skb->data[sizeof(struct udphdr)] & L2TP_CONTROL_BIT)
+               return opt->backlog_rcv(sk_udp, skb);
 
-       /* Now the packet is ours. Skip UDP header. */
+       /* Skip UDP header. */
        skb_pull(skb, sizeof(struct udphdr));
 
        /* Check the version. */
@@ -76,44 +78,30 @@ static int pppolac_recv(struct sock *sk_udp, struct sk_buff *skb)
        ptr = &skb->data[2];
 
        /* Check the length if it is present. */
-       if (bits & L2TP_LENGTH_MASK) {
+       if (bits & L2TP_LENGTH_BIT) {
                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_MASK ? 4 : 0) +
-                       (bits & L2TP_LENGTH_MASK ? 2 : 0) +
-                       (bits & L2TP_OFFSET_MASK ? 2 : 0)))
+       if (!skb_pull(skb, 6 + (bits & L2TP_SEQUENCE_BIT ? 4 : 0) +
+                       (bits & L2TP_LENGTH_BIT ? 2 : 0) +
+                       (bits & L2TP_OFFSET_BIT ? 2 : 0)))
                goto drop;
 
        /* Skip the offset padding if it is present. */
-       if (bits & L2TP_OFFSET_MASK &&
+       if (bits & L2TP_OFFSET_BIT &&
                        !skb_pull(skb, skb->data[-2] << 8 | skb->data[-1]))
                goto drop;
 
-       /* 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 tunnel and the session. */
-       if (unaligned(ptr)->u32 != opt->local) {
-               sock_put(sk);
+       if (unaligned(ptr)->u32 != opt->local)
                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;
+       /* Check the sequence if it is present. According to RFC 2661 section
+        * 5.4, the only thing to do is to update opt->sequencing. */
+       opt->sequencing = bits & L2TP_SEQUENCE_BIT;
 
        /* Skip PPP address and control if they are present. */
        if (skb->len >= 2 && skb->data[0] == PPP_ADDR &&
@@ -124,26 +112,50 @@ static int pppolac_recv(struct sock *sk_udp, 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. We have to lock sk to
-        * prevent another thread from calling pppox_unbind_sock(). */
+       /* Finally, deliver the packet to PPP channel. */
        skb_orphan(skb);
-       lock_sock(sk);
        ppp_input(&pppox_sk(sk)->chan, skb);
-       release_sock(sk);
-       sock_put(sk);
-       return 0;
-
+       return NET_RX_SUCCESS;
 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);
@@ -153,7 +165,7 @@ static int pppolac_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        /* Install L2TP header. */
        if (opt->sequencing) {
                skb_push(skb, 10);
-               skb->data[0] = L2TP_SEQUENCE_MASK;
+               skb->data[0] = L2TP_SEQUENCE_BIT;
                skb->data[6] = opt->sequence >> 8;
                skb->data[7] = opt->sequence;
                skb->data[8] = 0;
@@ -166,11 +178,10 @@ 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 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);
+       /* 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);
        return 1;
 }
 
@@ -220,6 +231,14 @@ 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;
@@ -227,6 +246,7 @@ static int pppolac_connect(struct socket *sock, struct sockaddr *useraddr,
        po->chan.mtu = PPP_MTU - 80;
        po->proto.lac.local = unaligned(&addr->local)->u32;
        po->proto.lac.remote = unaligned(&addr->remote)->u32;
+       po->proto.lac.backlog_rcv = sk_udp->sk_backlog_rcv;
 
        error = ppp_register_channel(&po->chan);
        if (error)
@@ -235,8 +255,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);
@@ -263,12 +283,11 @@ 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);
-
                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);
        }
@@ -342,6 +361,8 @@ 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;
 }
 
index 8885eba8968e80df85c11d01824aa74439f38e5c..298097127c90feb003dd3c54cfa5b93a640e19d0 100644 (file)
@@ -3,7 +3,6 @@
  * Driver for PPP on PPTP Network Server / PPPoPNS Socket (RFC 2637)
  *
  * 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
  * and IPv6. */
 
 #include <linux/module.h>
+#include <linux/workqueue.h>
 #include <linux/skbuff.h>
 #include <linux/file.h>
+#include <linux/netdevice.h>
 #include <linux/net.h>
 #include <linux/ppp_defs.h>
 #include <linux/if.h>
 #include <linux/if_ppp.h>
 #include <linux/if_pppox.h>
 #include <linux/ppp_channel.h>
+#include <asm/uaccess.h>
 
 #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;
 }
 
index 7f7bc0e119739f2e3268cc9847fef23a58553f6e..3e837352b4e1ec7e093210e1a89da3761fcce0b6 100644 (file)
@@ -177,12 +177,15 @@ struct pppolac_opt {
        __u32   remote;
        __u16   sequence;
        __u8    sequencing;
+       int     (*backlog_rcv)(struct sock *sk_udp, struct sk_buff *skb);
 };
 
 struct pppopns_opt {
        __u16   local;
        __u16   remote;
        __u32   sequence;
+       void    (*data_ready)(struct sock *sk_raw, int length);
+       int     (*backlog_rcv)(struct sock *sk_raw, struct sk_buff *skb);
 };
 
 #include <net/sock.h>