l2tp: remove pppol2tp_tunnel_ioctl()
authorGuillaume Nault <g.nault@alphalink.fr>
Fri, 10 Aug 2018 11:22:00 +0000 (13:22 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sat, 11 Aug 2018 19:13:49 +0000 (12:13 -0700)
Handle PPPIOCGL2TPSTATS in pppol2tp_ioctl() if the socket represents a
tunnel. This one is a bit special because the caller may use the tunnel
socket to retrieve statistics of one of its sessions. If the session_id
is set, the corresponding session's statistics are returned, instead of
those of the tunnel. This is handled by the new
pppol2tp_tunnel_copy_stats() helper function.

Set ->tunnel_id and ->using_ipsec out of the conditional, so
that it can be used by the 'else' branch in the following patch.
We cannot do that for ->session_id, because tunnel sockets have to
report the value that was originally passed in 'stats.session_id',
while session sockets have to report their own session_id.

Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/l2tp/l2tp_ppp.c

index f4ec6b2a093e67053029b36e9461a85dfe42620a..2afd3ab8a551c86ac9834ecefc792b146643e623 100644 (file)
@@ -1038,6 +1038,36 @@ static void pppol2tp_copy_stats(struct pppol2tp_ioc_stats *dest,
        dest->rx_errors = atomic_long_read(&stats->rx_errors);
 }
 
+static int pppol2tp_tunnel_copy_stats(struct pppol2tp_ioc_stats *stats,
+                                     struct l2tp_tunnel *tunnel)
+{
+       struct l2tp_session *session;
+
+       if (!stats->session_id) {
+               memset(stats, 0, sizeof(*stats));
+               pppol2tp_copy_stats(stats, &tunnel->stats);
+               return 0;
+       }
+
+       /* If session_id is set, search the corresponding session in the
+        * context of this tunnel and record the session's statistics.
+        */
+       session = l2tp_tunnel_get_session(tunnel, stats->session_id);
+       if (!session)
+               return -EBADR;
+
+       if (session->pwtype != L2TP_PWTYPE_PPP) {
+               l2tp_session_dec_refcount(session);
+               return -EBADR;
+       }
+
+       memset(stats, 0, sizeof(*stats));
+       pppol2tp_copy_stats(stats, &session->stats);
+       l2tp_session_dec_refcount(session);
+
+       return 0;
+}
+
 /* Session ioctl helper.
  */
 static int pppol2tp_session_ioctl(struct l2tp_session *session,
@@ -1084,84 +1114,10 @@ static int pppol2tp_session_ioctl(struct l2tp_session *session,
        return err;
 }
 
-/* Tunnel ioctl helper.
- *
- * Note the special handling for PPPIOCGL2TPSTATS below. If the ioctl data
- * 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 l2tp_tunnel *tunnel,
-                                unsigned int cmd, unsigned long arg)
-{
-       int err = 0;
-       struct sock *sk;
-       struct pppol2tp_ioc_stats stats;
-
-       l2tp_dbg(tunnel, L2TP_MSG_CONTROL,
-                "%s: pppol2tp_tunnel_ioctl(cmd=%#x, arg=%#lx)\n",
-                tunnel->name, cmd, arg);
-
-       sk = tunnel->sock;
-       sock_hold(sk);
-
-       switch (cmd) {
-       case PPPIOCGL2TPSTATS:
-               err = -ENXIO;
-               if (!(sk->sk_state & PPPOX_CONNECTED))
-                       break;
-
-               if (copy_from_user(&stats, (void __user *) arg,
-                                  sizeof(stats))) {
-                       err = -EFAULT;
-                       break;
-               }
-               if (stats.session_id != 0) {
-                       /* resend to session ioctl handler */
-                       struct l2tp_session *session;
-
-                       session = l2tp_tunnel_get_session(tunnel,
-                                                         stats.session_id);
-                       if (!session) {
-                               err = -EBADR;
-                               break;
-                       }
-                       if (session->pwtype != L2TP_PWTYPE_PPP) {
-                               l2tp_session_dec_refcount(session);
-                               err = -EBADR;
-                               break;
-                       }
-
-                       err = pppol2tp_session_ioctl(session, cmd, arg);
-                       l2tp_session_dec_refcount(session);
-                       break;
-               }
-               stats.using_ipsec = l2tp_tunnel_uses_xfrm(tunnel);
-               pppol2tp_copy_stats(&stats, &tunnel->stats);
-               if (copy_to_user((void __user *) arg, &stats, sizeof(stats))) {
-                       err = -EFAULT;
-                       break;
-               }
-               l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: get L2TP stats\n",
-                         tunnel->name);
-               err = 0;
-               break;
-
-       default:
-               err = -ENOSYS;
-               break;
-       }
-
-       sock_put(sk);
-
-       return err;
-}
-
-/* Main ioctl() handler.
- * Dispatch to tunnel or session helpers depending on the socket.
- */
 static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd,
                          unsigned long arg)
 {
+       struct pppol2tp_ioc_stats stats;
        struct l2tp_session *session;
        int val;
 
@@ -1200,11 +1156,29 @@ static int pppol2tp_ioctl(struct socket *sock, unsigned int cmd,
                        return -ENOTCONN;
 
                /* Session 0 represents the parent tunnel */
-               if (!session->session_id && !session->peer_session_id)
-                       return pppol2tp_tunnel_ioctl(session->tunnel, cmd,
-                                                    arg);
-               else
+               if (!session->session_id && !session->peer_session_id) {
+                       u32 session_id;
+                       int err;
+
+                       if (copy_from_user(&stats, (void __user *)arg,
+                                          sizeof(stats)))
+                               return -EFAULT;
+
+                       session_id = stats.session_id;
+                       err = pppol2tp_tunnel_copy_stats(&stats,
+                                                        session->tunnel);
+                       if (err < 0)
+                               return err;
+
+                       stats.session_id = session_id;
+               } else {
                        return pppol2tp_session_ioctl(session, cmd, arg);
+               }
+               stats.tunnel_id = session->tunnel->tunnel_id;
+               stats.using_ipsec = l2tp_tunnel_uses_xfrm(session->tunnel);
+
+               if (copy_to_user((void __user *)arg, &stats, sizeof(stats)))
+                       return -EFAULT;
                break;
 
        default: