diff options
author | Guillaume Nault <g.nault@alphalink.fr> | 2014-04-08 22:55:28 +0200 |
---|---|---|
committer | Dmitry Kozlov <xeb@mail.ru> | 2014-04-11 06:47:27 +0400 |
commit | 634c92cf8acf9e8a0e915c82bb114a791850348a (patch) | |
tree | b166fc485d9340ebb1039825f7d91c5f2c2f1321 /accel-pppd/ctrl | |
parent | 221f2f9ab55dcdc7028669e75f619ba87503a8aa (diff) | |
download | accel-ppp-634c92cf8acf9e8a0e915c82bb114a791850348a.tar.gz accel-ppp-634c92cf8acf9e8a0e915c82bb114a791850348a.zip |
l2tp: drop tunnel/session messages from send queue upon disconnection
Add a per session send queue. Messages sent by a session are added to
both tunnel and session queues. This allows sessions to remove their
unsent messages from tunnel's send queue before they disconnect.
The same approach is used for tunnels. Before disconnecting, they clear
their send queue to avoid sending useless messages.
Signed-off-by: Guillaume Nault <g.nault@alphalink.fr>
Diffstat (limited to 'accel-pppd/ctrl')
-rw-r--r-- | accel-pppd/ctrl/l2tp/l2tp.c | 76 | ||||
-rw-r--r-- | accel-pppd/ctrl/l2tp/l2tp.h | 1 |
2 files changed, 69 insertions, 8 deletions
diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index e8a4ab3f..e7b6955c 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -108,6 +108,7 @@ struct l2tp_sess_t int reorder_timeout; struct triton_timer_t timeout_timer; + struct list_head send_queue; pthread_mutex_t apses_lock; struct triton_context_t apses_ctx; @@ -433,6 +434,33 @@ static int l2tp_tunnel_checkchallresp(uint8_t msgident, return 0; } +static void l2tp_tunnel_clear_sendqueue(struct l2tp_conn_t *conn) +{ + struct l2tp_packet_t *pack; + + while (!list_empty(&conn->send_queue)) { + pack = list_first_entry(&conn->send_queue, typeof(*pack), + entry); + if (pack->sess_entry.next) + list_del(&pack->sess_entry); + list_del(&pack->entry); + l2tp_packet_free(pack); + } +} + +static void l2tp_session_clear_sendqueue(struct l2tp_sess_t *sess) +{ + struct l2tp_packet_t *pack; + + while (!list_empty(&sess->send_queue)) { + pack = list_first_entry(&sess->send_queue, typeof(*pack), + sess_entry); + list_del(&pack->sess_entry); + list_del(&pack->entry); + l2tp_packet_free(pack); + } +} + static int __l2tp_tunnel_send(const struct l2tp_conn_t *conn, struct l2tp_packet_t *pack) { @@ -537,6 +565,11 @@ static int l2tp_tunnel_push_sendqueue(struct l2tp_conn_t *conn) return -1; } + if (pack->sess_entry.next) { + list_del(&pack->sess_entry); + pack->sess_entry.next = NULL; + pack->sess_entry.prev = NULL; + } list_move_tail(&pack->entry, &conn->rtms_queue); ++conn->Ns; ++pkt_sent; @@ -597,7 +630,12 @@ static int l2tp_session_send(struct l2tp_sess_t *sess, pack->hdr.sid = htons(sess->peer_sid); - return l2tp_tunnel_send(sess->paren_conn, pack); + if (l2tp_tunnel_send(sess->paren_conn, pack) < 0) + return -1; + + list_add_tail(&pack->sess_entry, &sess->send_queue); + + return 0; } static int l2tp_send_StopCCN(struct l2tp_conn_t *conn, @@ -758,6 +796,11 @@ static int l2tp_tunnel_disconnect(struct l2tp_conn_t *conn, return 0; } + /* Discard unsent messages so that StopCCN will be the only one in the + * send queue (to minimise delay in case of congestion). + */ + l2tp_tunnel_clear_sendqueue(conn); + if (l2tp_send_StopCCN(conn, res, err) < 0) { log_tunnel(log_error, conn, "impossible to notify peer of tunnel disconnection:" @@ -871,6 +914,7 @@ static void session_hold(struct l2tp_sess_t *sess) static void l2tp_session_free(struct l2tp_sess_t *sess) { + struct l2tp_packet_t *pack; intptr_t cause = TERM_NAS_REQUEST; int res = 1; @@ -923,6 +967,19 @@ static void l2tp_session_free(struct l2tp_sess_t *sess) if (sess->timeout_timer.tpd) triton_timer_del(&sess->timeout_timer); + /* Packets in the send queue must not reference the session anymore. + * They aren't removed from tunnel's queue because they have to be sent + * even though session is getting destroyed (useless messages are + * dropped from send queues before calling l2tp_session_free()). + */ + while (!list_empty(&sess->send_queue)) { + pack = list_first_entry(&sess->send_queue, typeof(*pack), + sess_entry); + list_del(&pack->sess_entry); + pack->sess_entry.next = NULL; + pack->sess_entry.prev = NULL; + } + if (sess->paren_conn->sessions) { if (!tdelete(sess, &sess->paren_conn->sessions, sess_cmp)) { log_session(log_error, sess, @@ -1015,11 +1072,7 @@ static void l2tp_tunnel_free(struct l2tp_conn_t *conn) list_del(&pack->entry); l2tp_packet_free(pack); } - while (!list_empty(&conn->send_queue)) { - pack = list_entry(conn->send_queue.next, typeof(*pack), entry); - list_del(&pack->entry); - l2tp_packet_free(pack); - } + l2tp_tunnel_clear_sendqueue(conn); if (conn->sessions) l2tp_tunnel_free_sessions(conn); @@ -1036,10 +1089,13 @@ static void l2tp_tunnel_free(struct l2tp_conn_t *conn) static void l2tp_session_disconnect(struct l2tp_sess_t *sess, uint16_t res, uint16_t err) { + /* Session is closing, unsent messages are now useless */ + l2tp_session_clear_sendqueue(sess); + if (l2tp_send_CDN(sess, res, err) < 0) log_session(log_error, sess, - "impossible to notify peer of session" - " disconnection, disconnecting anyway\n"); + "impossible to notify peer of session disconnection:" + " sending CDN failed, deleting session anyway\n"); l2tp_session_free(sess); } @@ -1337,6 +1393,7 @@ static struct l2tp_sess_t *l2tp_tunnel_alloc_session(struct l2tp_conn_t *conn) (conf_dataseq == L2TP_DATASEQ_REQUIRE); sess->recv_seq = (conf_dataseq == L2TP_DATASEQ_REQUIRE); sess->reorder_timeout = conf_reorder_timeout; + INIT_LIST_HEAD(&sess->send_queue); sess->timeout_timer.expire = l2tp_session_timeout; sess->timeout_timer.period = conf_timeout * 1000; @@ -3544,6 +3601,9 @@ static int l2tp_recv_CDN(struct l2tp_sess_t *sess, if (err_msg) _free(err_msg); + /* Too late to send outstanding messages */ + l2tp_session_clear_sendqueue(sess); + l2tp_session_free(sess); return 0; diff --git a/accel-pppd/ctrl/l2tp/l2tp.h b/accel-pppd/ctrl/l2tp/l2tp.h index 9d4dd2cb..12a2dbf1 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.h +++ b/accel-pppd/ctrl/l2tp/l2tp.h @@ -64,6 +64,7 @@ struct l2tp_attr_t struct l2tp_packet_t { struct list_head entry; + struct list_head sess_entry; struct sockaddr_in addr; struct l2tp_hdr_t hdr; struct list_head attrs; |