kernel: backport an upstream PPPoE shutdown fix
[openwrt.git] / target / linux / generic / patches-3.18 / 081-pppoe-Use-workqueue-to-die-properly-when-a-PADT-is-r.patch
1 From: Simon Farnsworth <simon@farnz.org.uk>
2 Date: Sun, 1 Mar 2015 10:54:39 +0000
3 Subject: [PATCH] pppoe: Use workqueue to die properly when a PADT is received
4
5 When a PADT frame is received, the socket may not be in a good state to
6 close down the PPP interface. The current implementation handles this by
7 simply blocking all further PPP traffic, and hoping that the lack of traffic
8 will trigger the user to investigate.
9
10 Use schedule_work to get to a process context from which we clear down the
11 PPP interface, in a fashion analogous to hangup on a TTY-based PPP
12 interface. This causes pppd to disconnect immediately, and allows tools to
13 take immediate corrective action.
14
15 Note that pppd's rp_pppoe.so plugin has code in it to disable the session
16 when it disconnects; however, as a consequence of this patch, the session is
17 already disabled before rp_pppoe.so is asked to disable the session. The
18 result is a harmless error message:
19
20 Failed to disconnect PPPoE socket: 114 Operation already in progress
21
22 This message is safe to ignore, as long as the error is 114 Operation
23 already in progress; in that specific case, it means that the PPPoE session
24 has already been disabled before pppd tried to disable it.
25
26 Signed-off-by: Simon Farnsworth <simon@farnz.org.uk>
27 Tested-by: Dan Williams <dcbw@redhat.com>
28 Tested-by: Christoph Schulz <develop@kristov.de>
29 Signed-off-by: David S. Miller <davem@davemloft.net>
30 ---
31
32 --- a/drivers/net/ppp/pppoe.c
33 +++ b/drivers/net/ppp/pppoe.c
34 @@ -455,6 +455,18 @@ out:
35         return NET_RX_DROP;
36  }
37  
38 +static void pppoe_unbind_sock_work(struct work_struct *work)
39 +{
40 +       struct pppox_sock *po = container_of(work, struct pppox_sock,
41 +                                            proto.pppoe.padt_work);
42 +       struct sock *sk = sk_pppox(po);
43 +
44 +       lock_sock(sk);
45 +       pppox_unbind_sock(sk);
46 +       release_sock(sk);
47 +       sock_put(sk);
48 +}
49 +
50  /************************************************************************
51   *
52   * Receive a PPPoE Discovery frame.
53 @@ -500,7 +512,8 @@ static int pppoe_disc_rcv(struct sk_buff
54                 }
55  
56                 bh_unlock_sock(sk);
57 -               sock_put(sk);
58 +               if (!schedule_work(&po->proto.pppoe.padt_work))
59 +                       sock_put(sk);
60         }
61  
62  abort:
63 @@ -613,6 +626,8 @@ static int pppoe_connect(struct socket *
64  
65         lock_sock(sk);
66  
67 +       INIT_WORK(&po->proto.pppoe.padt_work, pppoe_unbind_sock_work);
68 +
69         error = -EINVAL;
70         if (sp->sa_protocol != PX_PROTO_OE)
71                 goto end;
72 --- a/include/linux/if_pppox.h
73 +++ b/include/linux/if_pppox.h
74 @@ -19,6 +19,7 @@
75  #include <linux/netdevice.h>
76  #include <linux/ppp_channel.h>
77  #include <linux/skbuff.h>
78 +#include <linux/workqueue.h>
79  #include <uapi/linux/if_pppox.h>
80  
81  static inline struct pppoe_hdr *pppoe_hdr(const struct sk_buff *skb)
82 @@ -32,6 +33,7 @@ struct pppoe_opt {
83         struct pppoe_addr       pa;       /* what this socket is bound to*/
84         struct sockaddr_pppox   relay;    /* what socket data will be
85                                              relayed to (PPPoE relaying) */
86 +       struct work_struct      padt_work;/* Work item for handling PADT */
87  };
88  
89  struct pptp_opt {