summaryrefslogtreecommitdiff
path: root/accel-pppd/ctrl
diff options
context:
space:
mode:
authorGuillaume Nault <g.nault@alphalink.fr>2014-02-06 17:20:08 +0100
committerDmitry Kozlov <xeb@mail.ru>2014-02-08 09:31:00 +0400
commit3b5fb8b711bf79c19150d150525a6b8cf9c003c6 (patch)
tree753c641634489a7a3bd8e0a85de0fe487d970438 /accel-pppd/ctrl
parent1744cf7d08f80e6d91423642b8c3b77d73b7321d (diff)
downloadaccel-ppp-xebd-3b5fb8b711bf79c19150d150525a6b8cf9c003c6.tar.gz
accel-ppp-xebd-3b5fb8b711bf79c19150d150525a6b8cf9c003c6.zip
l2tp: handle tunnel/session dependencies with reference counters
Use reference counters to track tunnels and sessions usage. Tunnels hold a reference to each of their sessions while sessions hold a reference to the tunnel they belong to. Tunnels and sessions also hold themselves to ensure that l2tp_{tunnel_session}_free() will be called before their reference counter drops to zero. Once the reference counter drops to zero, the tunnel or session is destroyed. Destruction may happen in any context, so context dependant operations still have to be done in l2tp_{tunnel,session}_free(). On the other hand l2tp_{tunnel,session}_free() must not free data wich may be required by their reference holders. Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
Diffstat (limited to 'accel-pppd/ctrl')
-rw-r--r--accel-pppd/ctrl/l2tp/l2tp.c109
1 files changed, 79 insertions, 30 deletions
diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c
index 26d6c14..552433f 100644
--- a/accel-pppd/ctrl/l2tp/l2tp.c
+++ b/accel-pppd/ctrl/l2tp/l2tp.c
@@ -85,6 +85,7 @@ struct l2tp_sess_t
uint16_t sid;
uint16_t peer_sid;
+ unsigned int ref_count;
int state1;
uint16_t lns_mode:1;
uint16_t hide_avps:1;
@@ -123,6 +124,7 @@ struct l2tp_conn_t
uint16_t Ns, Nr;
struct list_head send_queue;
+ unsigned int ref_count;
int state;
void *sessions;
unsigned int sess_count;
@@ -562,6 +564,63 @@ static void l2tp_tunnel_disconnect(struct l2tp_conn_t *conn, int res, int err)
conn->state = STATE_FIN;
}
+static void __tunnel_destroy(struct l2tp_conn_t *conn)
+{
+ if (conn->challenge)
+ _free(conn->challenge);
+ if (conn->secret)
+ _free(conn->secret);
+
+ log_tunnel(log_info2, conn, "tunnel destroyed\n");
+
+ mempool_free(conn);
+}
+
+static void tunnel_put(struct l2tp_conn_t *conn)
+{
+ if (__sync_sub_and_fetch(&conn->ref_count, 1) == 0)
+ __tunnel_destroy(conn);
+}
+
+static void tunnel_hold(struct l2tp_conn_t *conn)
+{
+ __sync_add_and_fetch(&conn->ref_count, 1);
+}
+
+static void __session_destroy(struct l2tp_sess_t *sess)
+{
+ struct l2tp_conn_t *conn = sess->paren_conn;
+
+ if (sess->ppp.fd >= 0)
+ close(sess->ppp.fd);
+ if (sess->ppp.ses.chan_name)
+ _free(sess->ppp.ses.chan_name);
+ if (sess->ctrl.calling_station_id)
+ _free(sess->ctrl.calling_station_id);
+ if (sess->ctrl.called_station_id)
+ _free(sess->ctrl.called_station_id);
+
+ log_session(log_info2, sess, "session destroyed\n");
+
+ mempool_free(sess);
+
+ /* Now that the session is fully destroyed,
+ * drop the reference to the tunnel.
+ */
+ tunnel_put(conn);
+}
+
+static void session_put(struct l2tp_sess_t *sess)
+{
+ if (__sync_sub_and_fetch(&sess->ref_count, 1) == 0)
+ __session_destroy(sess);
+}
+
+static void session_hold(struct l2tp_sess_t *sess)
+{
+ __sync_add_and_fetch(&sess->ref_count, 1);
+}
+
static void __l2tp_session_free(void *data)
{
struct l2tp_sess_t *sess = data;
@@ -596,24 +655,17 @@ static void __l2tp_session_free(void *data)
triton_timer_del(&sess->timeout_timer);
triton_context_unregister(&sess->sctx);
- if (sess->ppp.fd != -1)
- close(sess->ppp.fd);
- if (sess->ppp.ses.chan_name)
- _free(sess->ppp.ses.chan_name);
- if (sess->ctrl.calling_station_id)
- _free(sess->ctrl.calling_station_id);
- if (sess->ctrl.called_station_id)
- _free(sess->ctrl.called_station_id);
-
if (triton_context_call(&sess->paren_conn->ctx,
l2tp_tunnel_session_freed, NULL) < 0)
log_session(log_error, sess,
"impossible to notify parent tunnel that"
" session has been freed\n");
- log_session(log_info2, sess, "destroyed\n");
-
- mempool_free(sess);
+ /* Only drop the reference the session holds to itself.
+ * Reference to the parent tunnel will be dropped by
+ * __session_destroy().
+ */
+ session_put(sess);
}
static void __l2tp_tunnel_free_session(void *data)
@@ -625,6 +677,8 @@ static void __l2tp_tunnel_free_session(void *data)
"impossible to free session %hu/%hu:"
" call to child session failed\n",
sess->sid, sess->peer_sid);
+
+ session_put(sess);
}
static void l2tp_tunnel_free_session(void *sess)
@@ -697,25 +751,20 @@ static void l2tp_tunnel_free(struct l2tp_conn_t *conn)
return;
}
+ pthread_mutex_lock(&l2tp_lock);
+ l2tp_conn[conn->tid] = NULL;
+ pthread_mutex_unlock(&l2tp_lock);
+
if (conn->hnd.tpd)
triton_md_unregister_handler(&conn->hnd);
-
if (conn->hnd.fd >= 0)
close(conn->hnd.fd);
-
if (conn->timeout_timer.tpd)
triton_timer_del(&conn->timeout_timer);
-
if (conn->rtimeout_timer.tpd)
triton_timer_del(&conn->rtimeout_timer);
-
if (conn->hello_timer.tpd)
triton_timer_del(&conn->hello_timer);
-
- pthread_mutex_lock(&l2tp_lock);
- l2tp_conn[conn->tid] = NULL;
- pthread_mutex_unlock(&l2tp_lock);
-
if (conn->ctx.tpd)
triton_context_unregister(&conn->ctx);
@@ -725,15 +774,8 @@ static void l2tp_tunnel_free(struct l2tp_conn_t *conn)
l2tp_packet_free(pack);
}
- if (conn->challenge)
- _free(conn->challenge);
-
- if (conn->secret)
- _free(conn->secret);
-
- log_tunnel(log_info2, conn, "destroyed\n");
-
- mempool_free(conn);
+ /* Drop the reference the tunnel holds to itself */
+ tunnel_put(conn);
}
static void l2tp_tunnel_session_freed(void *data)
@@ -915,6 +957,12 @@ static struct l2tp_sess_t *l2tp_tunnel_alloc_session(struct l2tp_conn_t *conn)
if (conf_ip_pool)
sess->ppp.ses.ipv4_pool_name = _strdup(conf_ip_pool);
+ /* The tunnel holds a reference to the session */
+ session_hold(sess);
+ /* The session holds a reference to the tunnel and to itself */
+ tunnel_hold(conn);
+ session_hold(sess);
+
return sess;
}
@@ -1145,6 +1193,7 @@ static struct l2tp_conn_t *l2tp_tunnel_alloc(const struct sockaddr_in *peer,
conn->lns_mode = lns_mode;
conn->port_set = port_set;
conn->hide_avps = hide_avps;
+ tunnel_hold(conn);
return conn;