diff options
-rw-r--r-- | net/l2tp/l2tp_core.c | 70 | ||||
-rw-r--r-- | net/l2tp/l2tp_eth.c | 10 | ||||
-rw-r--r-- | net/l2tp/l2tp_ppp.c | 60 |
3 files changed, 84 insertions, 56 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 8a067536d15c..46b450a1bc21 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c | |||
@@ -374,6 +374,48 @@ struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname) | |||
374 | } | 374 | } |
375 | EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname); | 375 | EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname); |
376 | 376 | ||
377 | static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel, | ||
378 | struct l2tp_session *session) | ||
379 | { | ||
380 | struct l2tp_session *session_walk; | ||
381 | struct hlist_head *g_head; | ||
382 | struct hlist_head *head; | ||
383 | struct l2tp_net *pn; | ||
384 | |||
385 | head = l2tp_session_id_hash(tunnel, session->session_id); | ||
386 | |||
387 | write_lock_bh(&tunnel->hlist_lock); | ||
388 | hlist_for_each_entry(session_walk, head, hlist) | ||
389 | if (session_walk->session_id == session->session_id) | ||
390 | goto exist; | ||
391 | |||
392 | if (tunnel->version == L2TP_HDR_VER_3) { | ||
393 | pn = l2tp_pernet(tunnel->l2tp_net); | ||
394 | g_head = l2tp_session_id_hash_2(l2tp_pernet(tunnel->l2tp_net), | ||
395 | session->session_id); | ||
396 | |||
397 | spin_lock_bh(&pn->l2tp_session_hlist_lock); | ||
398 | hlist_for_each_entry(session_walk, g_head, global_hlist) | ||
399 | if (session_walk->session_id == session->session_id) | ||
400 | goto exist_glob; | ||
401 | |||
402 | hlist_add_head_rcu(&session->global_hlist, g_head); | ||
403 | spin_unlock_bh(&pn->l2tp_session_hlist_lock); | ||
404 | } | ||
405 | |||
406 | hlist_add_head(&session->hlist, head); | ||
407 | write_unlock_bh(&tunnel->hlist_lock); | ||
408 | |||
409 | return 0; | ||
410 | |||
411 | exist_glob: | ||
412 | spin_unlock_bh(&pn->l2tp_session_hlist_lock); | ||
413 | exist: | ||
414 | write_unlock_bh(&tunnel->hlist_lock); | ||
415 | |||
416 | return -EEXIST; | ||
417 | } | ||
418 | |||
377 | /* Lookup a tunnel by id | 419 | /* Lookup a tunnel by id |
378 | */ | 420 | */ |
379 | struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id) | 421 | struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id) |
@@ -1785,6 +1827,7 @@ EXPORT_SYMBOL_GPL(l2tp_session_set_header_len); | |||
1785 | struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) | 1827 | struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) |
1786 | { | 1828 | { |
1787 | struct l2tp_session *session; | 1829 | struct l2tp_session *session; |
1830 | int err; | ||
1788 | 1831 | ||
1789 | session = kzalloc(sizeof(struct l2tp_session) + priv_size, GFP_KERNEL); | 1832 | session = kzalloc(sizeof(struct l2tp_session) + priv_size, GFP_KERNEL); |
1790 | if (session != NULL) { | 1833 | if (session != NULL) { |
@@ -1840,6 +1883,13 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn | |||
1840 | 1883 | ||
1841 | l2tp_session_set_header_len(session, tunnel->version); | 1884 | l2tp_session_set_header_len(session, tunnel->version); |
1842 | 1885 | ||
1886 | err = l2tp_session_add_to_tunnel(tunnel, session); | ||
1887 | if (err) { | ||
1888 | kfree(session); | ||
1889 | |||
1890 | return ERR_PTR(err); | ||
1891 | } | ||
1892 | |||
1843 | /* Bump the reference count. The session context is deleted | 1893 | /* Bump the reference count. The session context is deleted |
1844 | * only when this drops to zero. | 1894 | * only when this drops to zero. |
1845 | */ | 1895 | */ |
@@ -1849,28 +1899,14 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn | |||
1849 | /* Ensure tunnel socket isn't deleted */ | 1899 | /* Ensure tunnel socket isn't deleted */ |
1850 | sock_hold(tunnel->sock); | 1900 | sock_hold(tunnel->sock); |
1851 | 1901 | ||
1852 | /* Add session to the tunnel's hash list */ | ||
1853 | write_lock_bh(&tunnel->hlist_lock); | ||
1854 | hlist_add_head(&session->hlist, | ||
1855 | l2tp_session_id_hash(tunnel, session_id)); | ||
1856 | write_unlock_bh(&tunnel->hlist_lock); | ||
1857 | |||
1858 | /* And to the global session list if L2TPv3 */ | ||
1859 | if (tunnel->version != L2TP_HDR_VER_2) { | ||
1860 | struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net); | ||
1861 | |||
1862 | spin_lock_bh(&pn->l2tp_session_hlist_lock); | ||
1863 | hlist_add_head_rcu(&session->global_hlist, | ||
1864 | l2tp_session_id_hash_2(pn, session_id)); | ||
1865 | spin_unlock_bh(&pn->l2tp_session_hlist_lock); | ||
1866 | } | ||
1867 | |||
1868 | /* Ignore management session in session count value */ | 1902 | /* Ignore management session in session count value */ |
1869 | if (session->session_id != 0) | 1903 | if (session->session_id != 0) |
1870 | atomic_inc(&l2tp_session_count); | 1904 | atomic_inc(&l2tp_session_count); |
1905 | |||
1906 | return session; | ||
1871 | } | 1907 | } |
1872 | 1908 | ||
1873 | return session; | 1909 | return ERR_PTR(-ENOMEM); |
1874 | } | 1910 | } |
1875 | EXPORT_SYMBOL_GPL(l2tp_session_create); | 1911 | EXPORT_SYMBOL_GPL(l2tp_session_create); |
1876 | 1912 | ||
diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 8bf18a5f66e0..6fd41d7afe1e 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c | |||
@@ -221,12 +221,6 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p | |||
221 | goto out; | 221 | goto out; |
222 | } | 222 | } |
223 | 223 | ||
224 | session = l2tp_session_find(net, tunnel, session_id); | ||
225 | if (session) { | ||
226 | rc = -EEXIST; | ||
227 | goto out; | ||
228 | } | ||
229 | |||
230 | if (cfg->ifname) { | 224 | if (cfg->ifname) { |
231 | dev = dev_get_by_name(net, cfg->ifname); | 225 | dev = dev_get_by_name(net, cfg->ifname); |
232 | if (dev) { | 226 | if (dev) { |
@@ -240,8 +234,8 @@ static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 p | |||
240 | 234 | ||
241 | session = l2tp_session_create(sizeof(*spriv), tunnel, session_id, | 235 | session = l2tp_session_create(sizeof(*spriv), tunnel, session_id, |
242 | peer_session_id, cfg); | 236 | peer_session_id, cfg); |
243 | if (!session) { | 237 | if (IS_ERR(session)) { |
244 | rc = -ENOMEM; | 238 | rc = PTR_ERR(session); |
245 | goto out; | 239 | goto out; |
246 | } | 240 | } |
247 | 241 | ||
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index 827e55c41ba2..26501902d1a7 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c | |||
@@ -583,6 +583,7 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, | |||
583 | int error = 0; | 583 | int error = 0; |
584 | u32 tunnel_id, peer_tunnel_id; | 584 | u32 tunnel_id, peer_tunnel_id; |
585 | u32 session_id, peer_session_id; | 585 | u32 session_id, peer_session_id; |
586 | bool drop_refcnt = false; | ||
586 | int ver = 2; | 587 | int ver = 2; |
587 | int fd; | 588 | int fd; |
588 | 589 | ||
@@ -684,36 +685,36 @@ static int pppol2tp_connect(struct socket *sock, struct sockaddr *uservaddr, | |||
684 | if (tunnel->peer_tunnel_id == 0) | 685 | if (tunnel->peer_tunnel_id == 0) |
685 | tunnel->peer_tunnel_id = peer_tunnel_id; | 686 | tunnel->peer_tunnel_id = peer_tunnel_id; |
686 | 687 | ||
687 | /* Create session if it doesn't already exist. We handle the | 688 | session = l2tp_session_get(sock_net(sk), tunnel, session_id, false); |
688 | * case where a session was previously created by the netlink | 689 | if (session) { |
689 | * interface by checking that the session doesn't already have | 690 | drop_refcnt = true; |
690 | * a socket and its tunnel socket are what we expect. If any | 691 | ps = l2tp_session_priv(session); |
691 | * of those checks fail, return EEXIST to the caller. | 692 | |
692 | */ | 693 | /* Using a pre-existing session is fine as long as it hasn't |
693 | session = l2tp_session_find(sock_net(sk), tunnel, session_id); | 694 | * been connected yet. |
694 | if (session == NULL) { | ||
695 | /* Default MTU must allow space for UDP/L2TP/PPP | ||
696 | * headers. | ||
697 | */ | 695 | */ |
698 | cfg.mtu = cfg.mru = 1500 - PPPOL2TP_HEADER_OVERHEAD; | 696 | if (ps->sock) { |
697 | error = -EEXIST; | ||
698 | goto end; | ||
699 | } | ||
699 | 700 | ||
700 | /* Allocate and initialize a new session context. */ | 701 | /* consistency checks */ |
701 | session = l2tp_session_create(sizeof(struct pppol2tp_session), | 702 | if (ps->tunnel_sock != tunnel->sock) { |
702 | tunnel, session_id, | 703 | error = -EEXIST; |
703 | peer_session_id, &cfg); | ||
704 | if (session == NULL) { | ||
705 | error = -ENOMEM; | ||
706 | goto end; | 704 | goto end; |
707 | } | 705 | } |
708 | } else { | 706 | } else { |
709 | ps = l2tp_session_priv(session); | 707 | /* Default MTU must allow space for UDP/L2TP/PPP headers */ |
710 | error = -EEXIST; | 708 | cfg.mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD; |
711 | if (ps->sock != NULL) | 709 | cfg.mru = cfg.mtu; |
712 | goto end; | ||
713 | 710 | ||
714 | /* consistency checks */ | 711 | session = l2tp_session_create(sizeof(struct pppol2tp_session), |
715 | if (ps->tunnel_sock != tunnel->sock) | 712 | tunnel, session_id, |
713 | peer_session_id, &cfg); | ||
714 | if (IS_ERR(session)) { | ||
715 | error = PTR_ERR(session); | ||
716 | goto end; | 716 | goto end; |
717 | } | ||
717 | } | 718 | } |
718 | 719 | ||
719 | /* Associate session with its PPPoL2TP socket */ | 720 | /* Associate session with its PPPoL2TP socket */ |
@@ -778,6 +779,8 @@ out_no_ppp: | |||
778 | session->name); | 779 | session->name); |
779 | 780 | ||
780 | end: | 781 | end: |
782 | if (drop_refcnt) | ||
783 | l2tp_session_dec_refcount(session); | ||
781 | release_sock(sk); | 784 | release_sock(sk); |
782 | 785 | ||
783 | return error; | 786 | return error; |
@@ -805,12 +808,6 @@ static int pppol2tp_session_create(struct net *net, u32 tunnel_id, u32 session_i | |||
805 | if (tunnel->sock == NULL) | 808 | if (tunnel->sock == NULL) |
806 | goto out; | 809 | goto out; |
807 | 810 | ||
808 | /* Check that this session doesn't already exist */ | ||
809 | error = -EEXIST; | ||
810 | session = l2tp_session_find(net, tunnel, session_id); | ||
811 | if (session != NULL) | ||
812 | goto out; | ||
813 | |||
814 | /* Default MTU values. */ | 811 | /* Default MTU values. */ |
815 | if (cfg->mtu == 0) | 812 | if (cfg->mtu == 0) |
816 | cfg->mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD; | 813 | cfg->mtu = 1500 - PPPOL2TP_HEADER_OVERHEAD; |
@@ -818,12 +815,13 @@ static int pppol2tp_session_create(struct net *net, u32 tunnel_id, u32 session_i | |||
818 | cfg->mru = cfg->mtu; | 815 | cfg->mru = cfg->mtu; |
819 | 816 | ||
820 | /* Allocate and initialize a new session context. */ | 817 | /* Allocate and initialize a new session context. */ |
821 | error = -ENOMEM; | ||
822 | session = l2tp_session_create(sizeof(struct pppol2tp_session), | 818 | session = l2tp_session_create(sizeof(struct pppol2tp_session), |
823 | tunnel, session_id, | 819 | tunnel, session_id, |
824 | peer_session_id, cfg); | 820 | peer_session_id, cfg); |
825 | if (session == NULL) | 821 | if (IS_ERR(session)) { |
822 | error = PTR_ERR(session); | ||
826 | goto out; | 823 | goto out; |
824 | } | ||
827 | 825 | ||
828 | ps = l2tp_session_priv(session); | 826 | ps = l2tp_session_priv(session); |
829 | ps->tunnel_sock = tunnel->sock; | 827 | ps->tunnel_sock = tunnel->sock; |