l2tp: take reference on sessions being dumped
authorGuillaume Nault <g.nault@alphalink.fr>
Mon, 3 Apr 2017 10:03:13 +0000 (12:03 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 7 Jun 2017 22:47:08 +0000 (00:47 +0200)
commit e08293a4ccbcc993ded0fdc46f1e57926b833d63 upstream.

Take a reference on the sessions returned by l2tp_session_find_nth()
(and rename it l2tp_session_get_nth() to reflect this change), so that
caller is assured that the session isn't going to disappear while
processing it.

For procfs and debugfs handlers, the session is held in the .start()
callback and dropped in .show(). Given that pppol2tp_seq_session_show()
dereferences the associated PPPoL2TP socket and that
l2tp_dfs_seq_session_show() might call pppol2tp_show(), we also need to
call the session's .ref() callback to prevent the socket from going
away from under us.

Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts")
Fixes: 0ad6614048cf ("l2tp: Add debugfs files for dumping l2tp debug info")
Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP")
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
Signed-off-by: Willy Tarreau <w@1wt.eu>
net/l2tp/l2tp_core.c
net/l2tp/l2tp_core.h
net/l2tp/l2tp_debugfs.c
net/l2tp/l2tp_netlink.c
net/l2tp/l2tp_ppp.c

index 797ff373e4869b18895b1d809380822720c781e4..787ac0ef1823b6ac3583b055b5c71af50a434dee 100644 (file)
@@ -280,7 +280,8 @@ struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunn
 }
 EXPORT_SYMBOL_GPL(l2tp_session_find);
 
-struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth)
+struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
+                                         bool do_ref)
 {
        int hash;
        struct l2tp_session *session;
@@ -290,6 +291,9 @@ struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth)
        for (hash = 0; hash < L2TP_HASH_SIZE; hash++) {
                hlist_for_each_entry(session, &tunnel->session_hlist[hash], hlist) {
                        if (++count > nth) {
+                               l2tp_session_inc_refcount(session);
+                               if (do_ref && session->ref)
+                                       session->ref(session);
                                read_unlock_bh(&tunnel->hlist_lock);
                                return session;
                        }
@@ -300,7 +304,7 @@ struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth)
 
        return NULL;
 }
-EXPORT_SYMBOL_GPL(l2tp_session_find_nth);
+EXPORT_SYMBOL_GPL(l2tp_session_get_nth);
 
 /* Lookup a session by interface name.
  * This is very inefficient but is only used by management interfaces.
index a98c854c25276d5392086fa0231c1ab13ad9f81d..54f89f38386e47b96da93aa3650183175a39ba9f 100644 (file)
@@ -236,7 +236,8 @@ out:
 extern struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel);
 extern void l2tp_tunnel_sock_put(struct sock *sk);
 extern struct l2tp_session *l2tp_session_find(struct net *net, struct l2tp_tunnel *tunnel, u32 session_id);
-extern struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth);
+extern struct l2tp_session *l2tp_session_get_nth(struct l2tp_tunnel *tunnel, int nth,
+                                                bool do_ref);
 extern struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname);
 extern struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
 extern struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
index 072d7202e182ffa2125fe7de9c54c584626fdeb0..c6bd783cfb1b27dd578fb345fc20bbc2343962f7 100644 (file)
@@ -53,7 +53,7 @@ static void l2tp_dfs_next_tunnel(struct l2tp_dfs_seq_data *pd)
 
 static void l2tp_dfs_next_session(struct l2tp_dfs_seq_data *pd)
 {
-       pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx);
+       pd->session = l2tp_session_get_nth(pd->tunnel, pd->session_idx, true);
        pd->session_idx++;
 
        if (pd->session == NULL) {
@@ -237,10 +237,14 @@ static int l2tp_dfs_seq_show(struct seq_file *m, void *v)
        }
 
        /* Show the tunnel or session context */
-       if (pd->session == NULL)
+       if (!pd->session) {
                l2tp_dfs_seq_tunnel_show(m, pd->tunnel);
-       else
+       } else {
                l2tp_dfs_seq_session_show(m, pd->session);
+               if (pd->session->deref)
+                       pd->session->deref(pd->session);
+               l2tp_session_dec_refcount(pd->session);
+       }
 
 out:
        return 0;
index 0825ff26e113f3e2acdbbe2b88bd0f4fbfd92410..490024eaece876e4e149ca9fd98bd8ce222c62e1 100644 (file)
@@ -719,7 +719,7 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback
                                goto out;
                }
 
-               session = l2tp_session_find_nth(tunnel, si);
+               session = l2tp_session_get_nth(tunnel, si, false);
                if (session == NULL) {
                        ti++;
                        tunnel = NULL;
@@ -729,8 +729,11 @@ static int l2tp_nl_cmd_session_dump(struct sk_buff *skb, struct netlink_callback
 
                if (l2tp_nl_session_send(skb, NETLINK_CB(cb->skb).portid,
                                         cb->nlh->nlmsg_seq, NLM_F_MULTI,
-                                        session) <= 0)
+                                        session) <= 0) {
+                       l2tp_session_dec_refcount(session);
                        break;
+               }
+               l2tp_session_dec_refcount(session);
 
                si++;
        }
index c3ae2411650c703171e61fda811add6ad281c614..c06c7ed47b692f909da543b2b60a1700ffbdd735 100644 (file)
@@ -1576,7 +1576,7 @@ static void pppol2tp_next_tunnel(struct net *net, struct pppol2tp_seq_data *pd)
 
 static void pppol2tp_next_session(struct net *net, struct pppol2tp_seq_data *pd)
 {
-       pd->session = l2tp_session_find_nth(pd->tunnel, pd->session_idx);
+       pd->session = l2tp_session_get_nth(pd->tunnel, pd->session_idx, true);
        pd->session_idx++;
 
        if (pd->session == NULL) {
@@ -1703,10 +1703,14 @@ static int pppol2tp_seq_show(struct seq_file *m, void *v)
 
        /* Show the tunnel or session context.
         */
-       if (pd->session == NULL)
+       if (!pd->session) {
                pppol2tp_seq_tunnel_show(m, pd->tunnel);
-       else
+       } else {
                pppol2tp_seq_session_show(m, pd->session);
+               if (pd->session->deref)
+                       pd->session->deref(pd->session);
+               l2tp_session_dec_refcount(pd->session);
+       }
 
 out:
        return 0;