From f2c1387d79966e950c534be9f020ccbbc75f4aa3 Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Tue, 8 Apr 2014 22:52:59 +0200 Subject: l2tp: allow tunnel deletion in main reception loop Let functions launched by l2tp_conn_read() delete the tunnel. This is done by taking a reference on conn to ensure it'll remain a valid tunnel, even after calling l2tp_tunnel_free(conn). l2tp_conn_read() now detects if conn got deleted, so that we know if the tunnel still holds a reference to itself. If it doesn't, tunnel_put() may free the tunnel and thus the triton handler we're running from. So we have to return -1 in this case. l2tp_tunnel_free() also needs to be modified: it can't safely close conn->hnd.fd anymore. Since l2tp_conn_read() relies on conn->hnd.fd being a valid file descriptor, closing it inside the reception loop would break this assumption in next iteration. Signed-off-by: Guillaume Nault --- accel-pppd/ctrl/l2tp/l2tp.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) (limited to 'accel-pppd/ctrl/l2tp/l2tp.c') diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index 086d2a83..8addf167 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -603,6 +603,8 @@ static void __tunnel_destroy(struct l2tp_conn_t *conn) { pthread_mutex_destroy(&conn->ctx_lock); + if (conn->hnd.fd >= 0) + close(conn->hnd.fd); if (conn->challenge) _free(conn->challenge); if (conn->secret) @@ -797,10 +799,6 @@ static void l2tp_tunnel_free(struct l2tp_conn_t *conn) if (conn->hnd.tpd) triton_md_unregister_handler(&conn->hnd); - if (conn->hnd.fd >= 0) { - close(conn->hnd.fd); - conn->hnd.fd = -1; - } if (conn->timeout_timer.tpd) triton_timer_del(&conn->timeout_timer); if (conn->rtimeout_timer.tpd) @@ -3596,6 +3594,11 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) const struct l2tp_attr_t *msg_type; int res; + /* Hold the tunnel. This allows any function we call to free the + * tunnel while still keeping the tunnel valid until we return. + */ + tunnel_hold(conn); + while (1) { res = l2tp_recv(h->fd, &pack, NULL, conn->secret, conn->secret_len); @@ -3604,11 +3607,10 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) log_tunnel(log_info1, conn, "peer is unreachable," " disconnecting tunnel\n"); - l2tp_tunnel_free(conn); - - return -1; + goto err_tunfree; } - return 0; + + break; } if (!pack) @@ -3779,9 +3781,21 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) l2tp_packet_free(pack); } + /* Use conn->state to detect tunnel deletion */ + if (conn->state == STATE_CLOSE) + goto err; + + tunnel_put(conn); + + return 0; + drop: l2tp_packet_free(pack); +err_tunfree: l2tp_tunnel_free(conn); +err: + tunnel_put(conn); + return -1; } -- cgit v1.2.3