aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuillaume Nault <g.nault@alphalink.fr>2017-03-31 07:02:30 -0400
committerDavid S. Miller <davem@davemloft.net>2017-04-01 23:16:41 -0400
commit2777e2ab5a9cf2b4524486c6db1517a6ded25261 (patch)
tree7143c7c597ae638f5b9587c576824e8aec91523d
parent5e6a9e5a3554a5b3db09cdc22253af1849c65dff (diff)
l2tp: take a reference on sessions used in genetlink handlers
Callers of l2tp_nl_session_find() need to hold a reference on the returned session since there's no guarantee that it isn't going to disappear from under them. Relying on the fact that no l2tp netlink message may be processed concurrently isn't enough: sessions can be deleted by other means (e.g. by closing the PPPOL2TP socket of a ppp pseudowire). l2tp_nl_cmd_session_delete() is a bit special: it runs a callback function that may require a previous call to session->ref(). In particular, for ppp pseudowires, the callback is l2tp_session_delete(), which then calls pppol2tp_session_close() and dereferences the PPPOL2TP socket. The socket might already be gone at the moment l2tp_session_delete() calls session->ref(), so we need to take a reference during the session lookup. So we need to pass the do_ref variable down to l2tp_session_get() and l2tp_session_get_by_ifname(). Since all callers have to be updated, l2tp_session_find_by_ifname() and l2tp_nl_session_find() are renamed to reflect their new behaviour. Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP") Signed-off-by: Guillaume Nault <g.nault@alphalink.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--net/l2tp/l2tp_core.c9
-rw-r--r--net/l2tp/l2tp_core.h3
-rw-r--r--net/l2tp/l2tp_netlink.c39
3 files changed, 35 insertions, 16 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 46b450a1bc21..e927422d8c58 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -352,7 +352,8 @@ EXPORT_SYMBOL_GPL(l2tp_session_find_nth);
352/* Lookup a session by interface name. 352/* Lookup a session by interface name.
353 * This is very inefficient but is only used by management interfaces. 353 * This is very inefficient but is only used by management interfaces.
354 */ 354 */
355struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname) 355struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
356 bool do_ref)
356{ 357{
357 struct l2tp_net *pn = l2tp_pernet(net); 358 struct l2tp_net *pn = l2tp_pernet(net);
358 int hash; 359 int hash;
@@ -362,7 +363,11 @@ struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname)
362 for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) { 363 for (hash = 0; hash < L2TP_HASH_SIZE_2; hash++) {
363 hlist_for_each_entry_rcu(session, &pn->l2tp_session_hlist[hash], global_hlist) { 364 hlist_for_each_entry_rcu(session, &pn->l2tp_session_hlist[hash], global_hlist) {
364 if (!strcmp(session->ifname, ifname)) { 365 if (!strcmp(session->ifname, ifname)) {
366 l2tp_session_inc_refcount(session);
367 if (do_ref && session->ref)
368 session->ref(session);
365 rcu_read_unlock_bh(); 369 rcu_read_unlock_bh();
370
366 return session; 371 return session;
367 } 372 }
368 } 373 }
@@ -372,7 +377,7 @@ struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname)
372 377
373 return NULL; 378 return NULL;
374} 379}
375EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname); 380EXPORT_SYMBOL_GPL(l2tp_session_get_by_ifname);
376 381
377static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel, 382static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel,
378 struct l2tp_session *session) 383 struct l2tp_session *session)
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 4544e81a3d27..3b9b704a84e4 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -237,7 +237,8 @@ struct l2tp_session *l2tp_session_find(struct net *net,
237 struct l2tp_tunnel *tunnel, 237 struct l2tp_tunnel *tunnel,
238 u32 session_id); 238 u32 session_id);
239struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth); 239struct l2tp_session *l2tp_session_find_nth(struct l2tp_tunnel *tunnel, int nth);
240struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname); 240struct l2tp_session *l2tp_session_get_by_ifname(struct net *net, char *ifname,
241 bool do_ref);
241struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id); 242struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id);
242struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth); 243struct l2tp_tunnel *l2tp_tunnel_find_nth(struct net *net, int nth);
243 244
diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c
index f1b68effb077..93e317377c66 100644
--- a/net/l2tp/l2tp_netlink.c
+++ b/net/l2tp/l2tp_netlink.c
@@ -48,7 +48,8 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 portid, u32 seq,
48/* Accessed under genl lock */ 48/* Accessed under genl lock */
49static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX]; 49static const struct l2tp_nl_cmd_ops *l2tp_nl_cmd_ops[__L2TP_PWTYPE_MAX];
50 50
51static struct l2tp_session *l2tp_nl_session_find(struct genl_info *info) 51static struct l2tp_session *l2tp_nl_session_get(struct genl_info *info,
52 bool do_ref)
52{ 53{
53 u32 tunnel_id; 54 u32 tunnel_id;
54 u32 session_id; 55 u32 session_id;
@@ -59,14 +60,15 @@ static struct l2tp_session *l2tp_nl_session_find(struct genl_info *info)
59 60
60 if (info->attrs[L2TP_ATTR_IFNAME]) { 61 if (info->attrs[L2TP_ATTR_IFNAME]) {
61 ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]); 62 ifname = nla_data(info->attrs[L2TP_ATTR_IFNAME]);
62 session = l2tp_session_find_by_ifname(net, ifname); 63 session = l2tp_session_get_by_ifname(net, ifname, do_ref);
63 } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) && 64 } else if ((info->attrs[L2TP_ATTR_SESSION_ID]) &&
64 (info->attrs[L2TP_ATTR_CONN_ID])) { 65 (info->attrs[L2TP_ATTR_CONN_ID])) {
65 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]); 66 tunnel_id = nla_get_u32(info->attrs[L2TP_ATTR_CONN_ID]);
66 session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]); 67 session_id = nla_get_u32(info->attrs[L2TP_ATTR_SESSION_ID]);
67 tunnel = l2tp_tunnel_find(net, tunnel_id); 68 tunnel = l2tp_tunnel_find(net, tunnel_id);
68 if (tunnel) 69 if (tunnel)
69 session = l2tp_session_find(net, tunnel, session_id); 70 session = l2tp_session_get(net, tunnel, session_id,
71 do_ref);
70 } 72 }
71 73
72 return session; 74 return session;
@@ -660,7 +662,7 @@ static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *inf
660 struct l2tp_session *session; 662 struct l2tp_session *session;
661 u16 pw_type; 663 u16 pw_type;
662 664
663 session = l2tp_nl_session_find(info); 665 session = l2tp_nl_session_get(info, true);
664 if (session == NULL) { 666 if (session == NULL) {
665 ret = -ENODEV; 667 ret = -ENODEV;
666 goto out; 668 goto out;
@@ -674,6 +676,10 @@ static int l2tp_nl_cmd_session_delete(struct sk_buff *skb, struct genl_info *inf
674 if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete) 676 if (l2tp_nl_cmd_ops[pw_type] && l2tp_nl_cmd_ops[pw_type]->session_delete)
675 ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session); 677 ret = (*l2tp_nl_cmd_ops[pw_type]->session_delete)(session);
676 678
679 if (session->deref)
680 session->deref(session);
681 l2tp_session_dec_refcount(session);
682
677out: 683out:
678 return ret; 684 return ret;
679} 685}
@@ -683,7 +689,7 @@ static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *inf
683 int ret = 0; 689 int ret = 0;
684 struct l2tp_session *session; 690 struct l2tp_session *session;
685 691
686 session = l2tp_nl_session_find(info); 692 session = l2tp_nl_session_get(info, false);
687 if (session == NULL) { 693 if (session == NULL) {
688 ret = -ENODEV; 694 ret = -ENODEV;
689 goto out; 695 goto out;
@@ -718,6 +724,8 @@ static int l2tp_nl_cmd_session_modify(struct sk_buff *skb, struct genl_info *inf
718 ret = l2tp_session_notify(&l2tp_nl_family, info, 724 ret = l2tp_session_notify(&l2tp_nl_family, info,
719 session, L2TP_CMD_SESSION_MODIFY); 725 session, L2TP_CMD_SESSION_MODIFY);
720 726
727 l2tp_session_dec_refcount(session);
728
721out: 729out:
722 return ret; 730 return ret;
723} 731}
@@ -813,29 +821,34 @@ static int l2tp_nl_cmd_session_get(struct sk_buff *skb, struct genl_info *info)
813 struct sk_buff *msg; 821 struct sk_buff *msg;
814 int ret; 822 int ret;
815 823
816 session = l2tp_nl_session_find(info); 824 session = l2tp_nl_session_get(info, false);
817 if (session == NULL) { 825 if (session == NULL) {
818 ret = -ENODEV; 826 ret = -ENODEV;
819 goto out; 827 goto err;
820 } 828 }
821 829
822 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 830 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
823 if (!msg) { 831 if (!msg) {
824 ret = -ENOMEM; 832 ret = -ENOMEM;
825 goto out; 833 goto err_ref;
826 } 834 }
827 835
828 ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq, 836 ret = l2tp_nl_session_send(msg, info->snd_portid, info->snd_seq,
829 0, session, L2TP_CMD_SESSION_GET); 837 0, session, L2TP_CMD_SESSION_GET);
830 if (ret < 0) 838 if (ret < 0)
831 goto err_out; 839 goto err_ref_msg;
832 840
833 return genlmsg_unicast(genl_info_net(info), msg, info->snd_portid); 841 ret = genlmsg_unicast(genl_info_net(info), msg, info->snd_portid);
834 842
835err_out: 843 l2tp_session_dec_refcount(session);
836 nlmsg_free(msg);
837 844
838out: 845 return ret;
846
847err_ref_msg:
848 nlmsg_free(msg);
849err_ref:
850 l2tp_session_dec_refcount(session);
851err:
839 return ret; 852 return ret;
840} 853}
841 854