|
@@ -374,6 +374,48 @@ struct l2tp_session *l2tp_session_find_by_ifname(struct net *net, char *ifname)
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(l2tp_session_find_by_ifname);
|
|
|
|
|
|
+static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel,
|
|
|
+ struct l2tp_session *session)
|
|
|
+{
|
|
|
+ struct l2tp_session *session_walk;
|
|
|
+ struct hlist_head *g_head;
|
|
|
+ struct hlist_head *head;
|
|
|
+ struct l2tp_net *pn;
|
|
|
+
|
|
|
+ head = l2tp_session_id_hash(tunnel, session->session_id);
|
|
|
+
|
|
|
+ write_lock_bh(&tunnel->hlist_lock);
|
|
|
+ hlist_for_each_entry(session_walk, head, hlist)
|
|
|
+ if (session_walk->session_id == session->session_id)
|
|
|
+ goto exist;
|
|
|
+
|
|
|
+ if (tunnel->version == L2TP_HDR_VER_3) {
|
|
|
+ pn = l2tp_pernet(tunnel->l2tp_net);
|
|
|
+ g_head = l2tp_session_id_hash_2(l2tp_pernet(tunnel->l2tp_net),
|
|
|
+ session->session_id);
|
|
|
+
|
|
|
+ spin_lock_bh(&pn->l2tp_session_hlist_lock);
|
|
|
+ hlist_for_each_entry(session_walk, g_head, global_hlist)
|
|
|
+ if (session_walk->session_id == session->session_id)
|
|
|
+ goto exist_glob;
|
|
|
+
|
|
|
+ hlist_add_head_rcu(&session->global_hlist, g_head);
|
|
|
+ spin_unlock_bh(&pn->l2tp_session_hlist_lock);
|
|
|
+ }
|
|
|
+
|
|
|
+ hlist_add_head(&session->hlist, head);
|
|
|
+ write_unlock_bh(&tunnel->hlist_lock);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+
|
|
|
+exist_glob:
|
|
|
+ spin_unlock_bh(&pn->l2tp_session_hlist_lock);
|
|
|
+exist:
|
|
|
+ write_unlock_bh(&tunnel->hlist_lock);
|
|
|
+
|
|
|
+ return -EEXIST;
|
|
|
+}
|
|
|
+
|
|
|
/* Lookup a tunnel by id
|
|
|
*/
|
|
|
struct l2tp_tunnel *l2tp_tunnel_find(struct net *net, u32 tunnel_id)
|
|
@@ -1785,6 +1827,7 @@ EXPORT_SYMBOL_GPL(l2tp_session_set_header_len);
|
|
|
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)
|
|
|
{
|
|
|
struct l2tp_session *session;
|
|
|
+ int err;
|
|
|
|
|
|
session = kzalloc(sizeof(struct l2tp_session) + priv_size, GFP_KERNEL);
|
|
|
if (session != NULL) {
|
|
@@ -1840,6 +1883,13 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn
|
|
|
|
|
|
l2tp_session_set_header_len(session, tunnel->version);
|
|
|
|
|
|
+ err = l2tp_session_add_to_tunnel(tunnel, session);
|
|
|
+ if (err) {
|
|
|
+ kfree(session);
|
|
|
+
|
|
|
+ return ERR_PTR(err);
|
|
|
+ }
|
|
|
+
|
|
|
/* Bump the reference count. The session context is deleted
|
|
|
* only when this drops to zero.
|
|
|
*/
|
|
@@ -1849,28 +1899,14 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn
|
|
|
/* Ensure tunnel socket isn't deleted */
|
|
|
sock_hold(tunnel->sock);
|
|
|
|
|
|
- /* Add session to the tunnel's hash list */
|
|
|
- write_lock_bh(&tunnel->hlist_lock);
|
|
|
- hlist_add_head(&session->hlist,
|
|
|
- l2tp_session_id_hash(tunnel, session_id));
|
|
|
- write_unlock_bh(&tunnel->hlist_lock);
|
|
|
-
|
|
|
- /* And to the global session list if L2TPv3 */
|
|
|
- if (tunnel->version != L2TP_HDR_VER_2) {
|
|
|
- struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net);
|
|
|
-
|
|
|
- spin_lock_bh(&pn->l2tp_session_hlist_lock);
|
|
|
- hlist_add_head_rcu(&session->global_hlist,
|
|
|
- l2tp_session_id_hash_2(pn, session_id));
|
|
|
- spin_unlock_bh(&pn->l2tp_session_hlist_lock);
|
|
|
- }
|
|
|
-
|
|
|
/* Ignore management session in session count value */
|
|
|
if (session->session_id != 0)
|
|
|
atomic_inc(&l2tp_session_count);
|
|
|
+
|
|
|
+ return session;
|
|
|
}
|
|
|
|
|
|
- return session;
|
|
|
+ return ERR_PTR(-ENOMEM);
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(l2tp_session_create);
|
|
|
|