diff options
Diffstat (limited to 'accel-pppd/ctrl/l2tp/l2tp.c')
-rw-r--r-- | accel-pppd/ctrl/l2tp/l2tp.c | 401 |
1 files changed, 291 insertions, 110 deletions
diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index 932b9c4..bede32d 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -141,7 +141,9 @@ struct l2tp_conn_t int retransmit; uint16_t Ns, Nr; + uint16_t peer_Nr; struct list_head send_queue; + struct list_head rtms_queue; unsigned int ref_count; int state; @@ -158,10 +160,6 @@ static mempool_t l2tp_sess_pool; static void l2tp_tunnel_timeout(struct triton_timer_t *t); static void l2tp_rtimeout(struct triton_timer_t *t); static void l2tp_send_HELLO(struct triton_timer_t *t); -static int l2tp_tunnel_send(struct l2tp_conn_t *conn, - struct l2tp_packet_t *pack); -static int l2tp_session_send(struct l2tp_sess_t *sess, - struct l2tp_packet_t *pack); static int l2tp_conn_read(struct triton_md_handler_t *); static void l2tp_session_free(struct l2tp_sess_t *sess); static void l2tp_tunnel_free(struct l2tp_conn_t *conn); @@ -435,6 +433,173 @@ static int l2tp_tunnel_checkchallresp(uint8_t msgident, return 0; } +static int __l2tp_tunnel_send(const struct l2tp_conn_t *conn, + struct l2tp_packet_t *pack) +{ + const struct l2tp_attr_t *msg_type; + void (*log_func)(const char *fmt, ...); + + pack->hdr.Nr = htons(conn->Nr); + + if (conf_verbose) { + if (l2tp_packet_is_ZLB(pack)) { + log_func = log_debug; + } else { + msg_type = list_first_entry(&pack->attrs, + typeof(*msg_type), entry); + if (msg_type->val.uint16 == Message_Type_Hello) + log_func = log_debug; + else + log_func = log_info2; + } + log_tunnel(log_func, conn, "send "); + l2tp_packet_print(pack, log_func); + } + + return l2tp_packet_send(conn->hnd.fd, pack); +} + +/* Drop acknowledged packets from tunnel's retransmission queue */ +static int l2tp_tunnel_clean_rtmsqueue(struct l2tp_conn_t *conn) +{ + struct l2tp_packet_t *pack; + unsigned int pkt_freed = 0; + + while (!list_empty(&conn->rtms_queue)) { + pack = list_first_entry(&conn->rtms_queue, typeof(*pack), + entry); + if (nsnr_cmp(ntohs(pack->hdr.Ns), conn->peer_Nr) >= 0) + break; + + list_del(&pack->entry); + l2tp_packet_free(pack); + ++pkt_freed; + } + + log_tunnel(log_debug, conn, "%u message%s acked by peer\n", pkt_freed, + pkt_freed > 1 ? "s" : ""); + + if (pkt_freed == 0) + return 0; + + /* Oldest message from retransmission queue has been acknowledged, + * reset retransmission counter and timer. + */ + conn->retransmit = 0; + + /* Stop timer if retransmission queue is empty */ + if (list_empty(&conn->rtms_queue)) { + if (conn->rtimeout_timer.tpd) + triton_timer_del(&conn->rtimeout_timer); + + return 0; + } + + /* Some messages haven't been acknowledged yet, restart timer */ + if (conn->rtimeout_timer.tpd) { + if (triton_timer_mod(&conn->rtimeout_timer, 0) < 0) { + log_tunnel(log_error, conn, + "impossible to clean retransmission queue:" + " updating retransmission timer failed\n"); + + return -1; + } + } else { + if (triton_timer_add(&conn->ctx, + &conn->rtimeout_timer, 0) < 0) { + log_tunnel(log_error, conn, + "impossible to clean retransmission queue:" + " starting retransmission timer failed\n"); + + return -1; + } + } + + return 0; +} + +static int l2tp_tunnel_push_sendqueue(struct l2tp_conn_t *conn) +{ + struct l2tp_packet_t *pack; + unsigned int pkt_sent = 0; + + while (!list_empty(&conn->send_queue)) { + pack = list_first_entry(&conn->send_queue, typeof(*pack), + entry); + + pack->hdr.Ns = htons(conn->Ns); + + if (__l2tp_tunnel_send(conn, pack) < 0) { + log_tunnel(log_error, conn, + "impossible to process the send queue:" + " sending packet %hu failed\n", conn->Ns); + + return -1; + } + + list_move_tail(&pack->entry, &conn->rtms_queue); + ++conn->Ns; + ++pkt_sent; + } + + log_tunnel(log_debug, conn, "%u message%s sent from send queue\n", + pkt_sent, pkt_sent > 1 ? "s" : ""); + + if (pkt_sent == 0) + return 0; + + /* At least one message sent, restart retransmission timer if necessary + * (timer may be stopped, e.g. because there was no message left in the + * retransmission queue). + */ + if (conn->rtimeout_timer.tpd == NULL) { + if (triton_timer_add(&conn->ctx, + &conn->rtimeout_timer, 0) < 0) { + log_tunnel(log_error, conn, + "impossible to process the send queue:" + " setting retransmission timer failed\n"); + + return -1; + } + } + + return 0; +} + +static int l2tp_tunnel_send(struct l2tp_conn_t *conn, + struct l2tp_packet_t *pack) +{ + if (conn->state == STATE_FIN || conn->state == STATE_CLOSE) { + log_tunnel(log_info2, conn, + "discarding outgoing message, tunnel is closing\n"); + l2tp_packet_free(pack); + + return -1; + } + + pack->hdr.tid = htons(conn->peer_tid); + list_add_tail(&pack->entry, &conn->send_queue); + + return 0; +} + +static int l2tp_session_send(struct l2tp_sess_t *sess, + struct l2tp_packet_t *pack) +{ + if (sess->state1 == STATE_CLOSE) { + log_session(log_info2, sess, + "discarding outgoing message," + " session is closing\n"); + l2tp_packet_free(pack); + + return -1; + } + + pack->hdr.sid = htons(sess->peer_sid); + + return l2tp_tunnel_send(sess->paren_conn, pack); +} + static int l2tp_send_StopCCN(struct l2tp_conn_t *conn, uint16_t res, uint16_t err) { @@ -630,6 +795,25 @@ static int l2tp_tunnel_disconnect(struct l2tp_conn_t *conn, return 0; } +static int l2tp_tunnel_disconnect_push(struct l2tp_conn_t *conn, + uint16_t res, uint16_t err) +{ + if (l2tp_tunnel_disconnect(conn, res, err) < 0) + return -1; + + if (l2tp_tunnel_push_sendqueue(conn) < 0) { + log_tunnel(log_error, conn, + "impossible to notify peer of tunnel disconnection:" + " transmitting messages from send queue failed," + " deleting tunnel anyway\n"); + l2tp_tunnel_free(conn); + + return -1; + } + + return 0; +} + static void __tunnel_destroy(struct l2tp_conn_t *conn) { pthread_mutex_destroy(&conn->ctx_lock); @@ -770,7 +954,7 @@ static void l2tp_session_free(struct l2tp_sess_t *sess) case STATE_ESTB: log_tunnel(log_info1, sess->paren_conn, "no more session, disconnecting tunnel\n"); - l2tp_tunnel_disconnect(sess->paren_conn, 1, 0); + l2tp_tunnel_disconnect_push(sess->paren_conn, 1, 0); break; case STATE_FIN: case STATE_CLOSE: @@ -837,6 +1021,12 @@ static void l2tp_tunnel_free(struct l2tp_conn_t *conn) if (conn->hello_timer.tpd) triton_timer_del(&conn->hello_timer); + while (!list_empty(&conn->rtms_queue)) { + pack = list_first_entry(&conn->rtms_queue, typeof(*pack), + entry); + 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); @@ -866,6 +1056,22 @@ static void l2tp_session_disconnect(struct l2tp_sess_t *sess, l2tp_session_free(sess); } +static void l2tp_session_disconnect_push(struct l2tp_sess_t *sess, + uint16_t res, uint16_t err) +{ + if (l2tp_send_CDN(sess, res, err) < 0) + log_session(log_error, sess, + "impossible to notify peer of session disconnection," + " sending CDN failed, deleting session anyway\n"); + else if (l2tp_tunnel_push_sendqueue(sess->paren_conn) < 0) + log_session(log_error, sess, + "impossible to notify peer of session disconnection:" + " transmitting messages from send queue failed," + " deleting session anyway\n"); + + l2tp_session_free(sess); +} + static void l2tp_session_apses_finished(void *data) { struct l2tp_conn_t *conn = l2tp_tunnel_self(); @@ -884,7 +1090,7 @@ static void l2tp_session_apses_finished(void *data) if (sess->state1 == STATE_ESTB) { log_session(log_info1, sess, "data channel closed, disconnecting session\n"); - l2tp_session_disconnect(sess, 2, 0); + l2tp_session_disconnect_push(sess, 2, 0); } else { log_session(log_warn, sess, "avoiding disconnection of session with no data channel:" @@ -1070,7 +1276,7 @@ static void l2tp_session_timeout(struct triton_timer_t *t) triton_timer_del(t); log_session(log_info1, sess, "session establishment timeout," " disconnecting session\n"); - l2tp_session_disconnect(sess, 10, 0); + l2tp_session_disconnect_push(sess, 10, 0); } static struct l2tp_sess_t *l2tp_tunnel_new_session(struct l2tp_conn_t *conn) @@ -1167,7 +1373,7 @@ static void l2tp_conn_close(struct triton_context_t *ctx) log_tunnel(log_info1, conn, "context thread is closing," " disconnecting tunnel\n"); - l2tp_tunnel_disconnect(conn, 0, 0); + l2tp_tunnel_disconnect_push(conn, 0, 0); } static int l2tp_tunnel_start(struct l2tp_conn_t *conn, @@ -1231,6 +1437,7 @@ static struct l2tp_conn_t *l2tp_tunnel_alloc(const struct sockaddr_in *peer, memset(conn, 0, sizeof(*conn)); pthread_mutex_init(&conn->ctx_lock, NULL); INIT_LIST_HEAD(&conn->send_queue); + INIT_LIST_HEAD(&conn->rtms_queue); conn->hnd.fd = socket(PF_INET, SOCK_DGRAM, 0); if (conn->hnd.fd < 0) { @@ -1671,15 +1878,14 @@ static int l2tp_retransmit(struct l2tp_conn_t *conn) { struct l2tp_packet_t *pack; - pack = list_entry(conn->send_queue.next, typeof(*pack), entry); - pack->hdr.Nr = htons(conn->Nr); + pack = list_first_entry(&conn->rtms_queue, typeof(*pack), entry); log_tunnel(log_info2, conn, "retransmitting packet %hu\n", ntohs(pack->hdr.Ns)); if (conf_verbose) { log_tunnel(log_info2, conn, "retransmit (duplicate) "); l2tp_packet_print(pack, log_info2); } - if (l2tp_packet_send(conn->hnd.fd, pack) < 0) { + if (__l2tp_tunnel_send(conn, pack) < 0) { log_tunnel(log_error, conn, "packet retransmission failure\n"); return -1; @@ -1693,10 +1899,10 @@ static void l2tp_rtimeout(struct triton_timer_t *t) struct l2tp_conn_t *conn = container_of(t, typeof(*conn), rtimeout_timer); struct l2tp_packet_t *pack; - if (!list_empty(&conn->send_queue)) { + if (!list_empty(&conn->rtms_queue)) { if (++conn->retransmit <= conf_retransmit) { - pack = list_entry(conn->send_queue.next, typeof(*pack), entry); - pack->hdr.Nr = htons(conn->Nr); + pack = list_first_entry(&conn->rtms_queue, + typeof(*pack), entry); log_tunnel(log_info2, conn, "retransmission %i (packet %hu)\n", conn->retransmit, ntohs(pack->hdr.Ns)); @@ -1705,7 +1911,7 @@ static void l2tp_rtimeout(struct triton_timer_t *t) "retransmit (timeout) "); l2tp_packet_print(pack, log_info2); } - if (l2tp_packet_send(conn->hnd.fd, pack) < 0) + if (__l2tp_tunnel_send(conn, pack) < 0) log_tunnel(log_error, conn, "packet retransmission failure\n"); } else { @@ -1724,72 +1930,13 @@ static void l2tp_tunnel_timeout(struct triton_timer_t *t) triton_timer_del(t); log_tunnel(log_info1, conn, "tunnel establishment timeout," " disconnecting tunnel\n"); - l2tp_tunnel_disconnect(conn, 1, 0); -} - -static int l2tp_tunnel_send(struct l2tp_conn_t *conn, - struct l2tp_packet_t *pack) -{ - const struct l2tp_attr_t *msg_type = NULL; - void (*log_func)(const char *fmt, ...) = NULL; - - conn->retransmit = 0; - - pack->hdr.tid = htons(conn->peer_tid); - pack->hdr.Nr = htons(conn->Nr); - pack->hdr.Ns = htons(conn->Ns); - - if (conf_verbose) { - if (list_empty(&pack->attrs)) - log_func = log_debug; - else { - msg_type = list_entry(pack->attrs.next, - typeof(*msg_type), entry); - if (msg_type->val.uint16 == Message_Type_Hello) - log_func = log_debug; - else - log_func = log_info2; - } - log_tunnel(log_func, conn, "send "); - l2tp_packet_print(pack, log_func); - } - - if (l2tp_packet_send(conn->hnd.fd, pack)) { - log_tunnel(log_error, conn, "packet transmission failure\n"); - goto out_err; - } - - if (!list_empty(&pack->attrs)) { - conn->Ns++; - list_add_tail(&pack->entry, &conn->send_queue); - if (!conn->rtimeout_timer.tpd) - if (triton_timer_add(&conn->ctx, - &conn->rtimeout_timer, 0) < 0) - log_tunnel(log_warn, conn, - "setting retransmission timer for" - " packet %hu failed\n", - conn->Ns - 1); - } else - l2tp_packet_free(pack); - - return 0; - -out_err: - l2tp_packet_free(pack); - return -1; -} - -static int l2tp_session_send(struct l2tp_sess_t *sess, - struct l2tp_packet_t *pack) -{ - pack->hdr.sid = htons(sess->peer_sid); - - return l2tp_tunnel_send(sess->paren_conn, pack); + l2tp_tunnel_disconnect_push(conn, 1, 0); } static int l2tp_send_ZLB(struct l2tp_conn_t *conn) { struct l2tp_packet_t *pack; + int res; log_tunnel(log_debug, conn, "sending ZLB\n"); @@ -1800,13 +1947,22 @@ static int l2tp_send_ZLB(struct l2tp_conn_t *conn) return -1; } - if (l2tp_tunnel_send(conn, pack) < 0) { + /* ZLB messages are special: they take no slot in the control message + * sequence number space and never have to be retransmitted. So they're + * sent directly by __l2tp_tunnel_send(), thus bypassing the send and + * retransmission queues. + */ + pack->hdr.tid = htons(conn->peer_tid); + pack->hdr.Ns = htons(conn->Ns); + + res = __l2tp_tunnel_send(conn, pack); + if (res < 0) log_tunnel(log_error, conn, "impossible to send ZLB:" " sending packet failed\n"); - return -1; - } - return 0; + l2tp_packet_free(pack); + + return res; } static void l2tp_send_HELLO(struct triton_timer_t *t) @@ -1821,13 +1977,23 @@ static void l2tp_send_HELLO(struct triton_timer_t *t) conn->secret_len); if (!pack) { log_tunnel(log_error, conn, "impossible to send HELLO:" - " packet allocation failed\n"); - return; + " packet allocation failed, deleting tunnel\n"); + goto err; } - if (l2tp_tunnel_send(conn, pack) < 0) + l2tp_tunnel_send(conn, pack); + + if (l2tp_tunnel_push_sendqueue(conn) < 0) { log_tunnel(log_error, conn, "impossible to send HELLO:" - " sending packet failed\n"); + " transmitting messages from send queue failed," + " deleting tunnel\n"); + goto err; + } + + return; + +err: + l2tp_tunnel_free(conn); } static void l2tp_send_SCCRQ(void *peer_addr) @@ -1895,9 +2061,11 @@ static void l2tp_send_SCCRQ(void *peer_addr) goto pack_err; } - if (l2tp_tunnel_send(conn, pack) < 0) { + l2tp_tunnel_send(conn, pack); + + if (l2tp_tunnel_push_sendqueue(conn) < 0) { log_tunnel(log_error, conn, "impossible to send SCCRQ:" - " sending packet failed\n"); + " transmitting messages from send queue failed\n"); goto err; } @@ -1982,9 +2150,11 @@ static void l2tp_send_SCCRP(struct l2tp_conn_t *conn) goto out_err; } - if (l2tp_tunnel_send(conn, pack) < 0) { + l2tp_tunnel_send(conn, pack); + + if (l2tp_tunnel_push_sendqueue(conn) < 0) { log_tunnel(log_error, conn, "impossible to send SCCRP:" - " sending packet failed\n"); + " transmitting messages from send queue failed\n"); goto out; } @@ -3562,6 +3732,14 @@ static void l2tp_tunnel_create_session(void *data) return; } + if (l2tp_tunnel_push_sendqueue(conn) < 0) { + log_tunnel(log_error, conn, "impossible to create session:" + " transmitting messages from send queue failed\n"); + l2tp_session_free(sess); + + return; + } + log_tunnel(log_info1, conn, "new session %hu created following" " request from command line interface\n", sid); } @@ -3674,7 +3852,7 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) { struct l2tp_conn_t *conn = container_of(h, typeof(*conn), hnd); struct l2tp_sess_t *sess = NULL; - struct l2tp_packet_t *pack, *p; + struct l2tp_packet_t *pack; const struct l2tp_attr_t *msg_type; uint16_t m_type; uint16_t m_sid; @@ -3727,6 +3905,17 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) continue; } + if (nsnr_cmp(ntohs(pack->hdr.Nr), conn->peer_Nr) > 0) + conn->peer_Nr = ntohs(pack->hdr.Nr); + + /* Drop acknowledged packets from retransmission queue */ + if (l2tp_tunnel_clean_rtmsqueue(conn) < 0) { + log_tunnel(log_error, conn, + "impossible to handle incoming message:" + " cleaning retransmission queue failed\n"); + goto drop; + } + res = nsnr_cmp(ntohs(pack->hdr.Ns), conn->Nr); if (res < 0) { /* Duplicate message */ @@ -3735,7 +3924,7 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) " %hu/%hu, tunnel Ns/Nr: %hu/%hu)\n", ntohs(pack->hdr.Ns), ntohs(pack->hdr.Nr), conn->Ns, conn->Nr); - if (!list_empty(&conn->send_queue)) + if (!list_empty(&conn->rtms_queue)) res = l2tp_retransmit(conn); else res = l2tp_send_ZLB(conn); @@ -3757,26 +3946,9 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) } else { if (!list_empty(&pack->attrs)) conn->Nr++; - while (!list_empty(&conn->send_queue)) { - /* Flush retransmission queue up - to the last acknowledged message */ - p = list_entry(conn->send_queue.next, - typeof(*pack), entry); - if (nsnr_cmp(ntohs(p->hdr.Ns), - ntohs(pack->hdr.Nr)) >= 0) - break; - list_del(&p->entry); - l2tp_packet_free(p); - conn->retransmit = 0; - } - if (!list_empty(&conn->send_queue)) - triton_timer_mod(&conn->rtimeout_timer, 0); - else { - if (conn->rtimeout_timer.tpd) - triton_timer_del(&conn->rtimeout_timer); - if (conn->state == STATE_FIN) - goto drop; - } + + if (conn->state == STATE_FIN) + goto drop; } if (list_empty(&pack->attrs)) { @@ -3824,6 +3996,15 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) } else { l2tp_tunnel_recv(conn, pack, m_type, msg_type->M); } + + if (l2tp_tunnel_push_sendqueue(conn) < 0) { + log_tunnel(log_error, conn, + "impossible to reply to incoming message:" + " transmitting messages from send queue failed," + " deleting tunnel\n"); + goto drop; + } + l2tp_packet_free(pack); } |