diff options
-rw-r--r-- | accel-pppd/ctrl/l2tp/l2tp.c | 214 |
1 files changed, 146 insertions, 68 deletions
diff --git a/accel-pppd/ctrl/l2tp/l2tp.c b/accel-pppd/ctrl/l2tp/l2tp.c index edbdb98..39d7481 100644 --- a/accel-pppd/ctrl/l2tp/l2tp.c +++ b/accel-pppd/ctrl/l2tp/l2tp.c @@ -77,6 +77,7 @@ struct l2tp_sess_t int state1; int state2; + struct triton_context_t sctx; struct triton_timer_t timeout_timer; struct ap_ctrl ctrl; struct ppp_t ppp; @@ -191,6 +192,18 @@ out_err: return -1; } +static int l2tp_tunnel_disconnect(struct l2tp_conn_t *conn, int res, int err) +{ + log_ppp_debug("l2tp: terminate (%i, %i)\n", res, err); + + if (l2tp_send_StopCCN(conn, res, err) < 0) + return -1; + + conn->state = STATE_FIN; + + return 0; +} + static void __l2tp_session_free(void *data) { struct l2tp_sess_t *sess = data; @@ -220,6 +233,8 @@ static void __l2tp_session_free(void *data) log_ppp_info1("disconnected\n"); + triton_context_unregister(&sess->sctx); + if (sess->timeout_timer.tpd) triton_timer_del(&sess->timeout_timer); if (sess->ppp.fd != -1) @@ -234,12 +249,29 @@ static void __l2tp_session_free(void *data) mempool_free(sess); } -static void l2tp_session_free(struct l2tp_sess_t *sess) +static void __l2tp_tunnel_free_session(void *data) { - if (tdelete(sess, &sess->paren_conn->sessions, sess_cmp) == NULL) { - log_warn("ERROR: impossible to delete unexisting session %hu\n", sess->sid); + struct l2tp_sess_t *sess = data; + + triton_context_call(&sess->sctx, __l2tp_session_free, sess); +} + +static void l2tp_tunnel_free_session(void *sess) +{ + struct triton_context_t *ctx = triton_context_self(); + struct l2tp_conn_t *conn = container_of(ctx, typeof(*conn), ctx); + + tdelete(sess, &conn->sessions, sess_cmp); + __l2tp_tunnel_free_session(sess); + if (conn->sessions == NULL) { + l2tp_tunnel_disconnect(conn, 1, 0); } - __l2tp_session_free(sess); +} + +static void l2tp_session_free(struct l2tp_sess_t *sess) +{ + triton_context_call(&sess->paren_conn->ctx, l2tp_tunnel_free_session, + sess); } static void l2tp_tunnel_free(struct l2tp_conn_t *conn) @@ -249,7 +281,7 @@ static void l2tp_tunnel_free(struct l2tp_conn_t *conn) if (conn->state == STATE_CLOSE) return; - tdestroy(conn->sessions, __l2tp_session_free); + tdestroy(conn->sessions, __l2tp_tunnel_free_session); triton_md_unregister_handler(&conn->hnd); close(conn->hnd.fd); @@ -286,18 +318,6 @@ static void l2tp_tunnel_free(struct l2tp_conn_t *conn) mempool_free(conn); } -static int l2tp_tunnel_disconnect(struct l2tp_conn_t *conn, int res, int err) -{ - log_ppp_debug("l2tp: terminate (%i, %i)\n", res, err); - - if (l2tp_send_StopCCN(conn, res, err) < 0) - return -1; - - conn->state = STATE_FIN; - - return 0; -} - static int l2tp_session_disconnect(struct l2tp_sess_t *sess, uint16_t res, uint16_t err) { @@ -305,11 +325,7 @@ static int l2tp_session_disconnect(struct l2tp_sess_t *sess, return -1; l2tp_session_free(sess); - /* Free the tunnel when all sessions have been closed */ - if (sess->paren_conn->sessions == NULL) - return l2tp_tunnel_disconnect(sess->paren_conn, 1, 0); - else - return 0; + return 0; } static void l2tp_ppp_session_disconnect(void *param) @@ -324,12 +340,6 @@ static void l2tp_ppp_session_disconnect(void *param) Now, call __l2tp_session_free() again to finish cleanup. */ __l2tp_session_free(sess); } - - /* Disconnect the tunnel when all sessions have been closed */ - if (sess->paren_conn->sessions == NULL) { - l2tp_tunnel_disconnect(sess->paren_conn, 1, 0); - sess->paren_conn->state = STATE_FIN; - } } static void l2tp_ppp_started(struct ap_session *ses) @@ -343,22 +353,16 @@ static void l2tp_ppp_finished(struct ap_session *ses) struct l2tp_sess_t *sess = container_of(ppp, typeof(*sess), ppp); log_ppp_debug("l2tp: ppp finished\n"); - triton_context_call(&sess->paren_conn->ctx, - l2tp_ppp_session_disconnect, sess); + triton_context_call(&sess->sctx, l2tp_ppp_session_disconnect, sess); } static void l2tp_session_timeout(struct triton_timer_t *t) { struct l2tp_sess_t *sess = container_of(t, typeof(*sess), timeout_timer); + log_ppp_debug("l2tp: session timeout\n"); l2tp_session_free(sess); - - /* Disconnect the tunnel when all sessions have been closed */ - if (sess->paren_conn->sessions == NULL) { - l2tp_tunnel_disconnect(sess->paren_conn, 1, 0); - sess->paren_conn->state = STATE_FIN; - } } static struct l2tp_sess_t *l2tp_tunnel_new_session(struct l2tp_conn_t *conn) @@ -406,7 +410,14 @@ out_err: return NULL; } -static struct l2tp_sess_t *l2tp_session_alloc(struct l2tp_conn_t *conn) +static void l2tp_sess_close(struct triton_context_t *ctx) +{ + struct l2tp_sess_t *sess = container_of(ctx, typeof(*sess), sctx); + + l2tp_session_disconnect(sess, 3, 0); +} + +static struct l2tp_sess_t *l2tp_tunnel_alloc_session(struct l2tp_conn_t *conn) { struct l2tp_sess_t *sess = NULL; @@ -419,7 +430,10 @@ static struct l2tp_sess_t *l2tp_session_alloc(struct l2tp_conn_t *conn) sess->state1 = STATE_CLOSE; sess->state2 = STATE_CLOSE; - sess->ctrl.ctx = &conn->ctx; + sess->sctx.before_switch = log_switch; + sess->sctx.close = l2tp_sess_close; + + sess->ctrl.ctx = &sess->sctx; sess->ctrl.type = CTRL_TYPE_L2TP; sess->ctrl.ppp = 1; sess->ctrl.name = "l2tp"; @@ -441,9 +455,31 @@ static struct l2tp_sess_t *l2tp_session_alloc(struct l2tp_conn_t *conn) sess->ppp.ses.ctrl = &sess->ctrl; sess->ppp.fd = -1; + return sess; +} + +static int l2tp_tunnel_confirm_session(struct l2tp_sess_t *sess) +{ + if (triton_context_register(&sess->sctx, &sess->ppp.ses) < 0) + return -1; + triton_context_wakeup(&sess->sctx); __sync_add_and_fetch(&stat_starting, 1); - return sess; + return 0; +} + +static int l2tp_tunnel_cancel_session(struct l2tp_sess_t *sess, + uint16_t res, uint16_t err) +{ + l2tp_send_CDN(sess, res, err); + tdelete(sess, &sess->paren_conn->sessions, sess_cmp); + if (sess->ctrl.calling_station_id) + _free(sess->ctrl.calling_station_id); + if (sess->ctrl.called_station_id) + _free(sess->ctrl.called_station_id); + mempool_free(sess); + + return 0; } static void l2tp_conn_close(struct triton_context_t *ctx) @@ -1019,13 +1055,15 @@ static int l2tp_recv_ICRQ(struct l2tp_conn_t *conn, struct l2tp_packet_t *pack) struct l2tp_attr_t *attr; struct l2tp_attr_t *assigned_sid = NULL; struct l2tp_sess_t *sess = NULL; + uint16_t res = 0; + uint16_t err = 0; if (conn->state != STATE_ESTB) { log_ppp_warn("l2tp: unexpected ICRQ\n"); return 0; } - sess = l2tp_session_alloc(conn); + sess = l2tp_tunnel_alloc_session(conn); if (sess == NULL) { log_ppp_warn("l2tp: no more session available\n"); return 0; @@ -1048,30 +1086,40 @@ static int l2tp_recv_ICRQ(struct l2tp_conn_t *conn, struct l2tp_packet_t *pack) if (attr->M) { if (conf_verbose) { log_ppp_warn("l2tp: ICRQ: unknown attribute %i\n", attr->attr->id); - if (l2tp_session_disconnect(sess, 2, 8) < 0) - return -1; - return 0; } + res = 2; + err = 8; } + break; } } if (!assigned_sid) { if (conf_verbose) log_ppp_warn("l2tp: ICRQ: no Assigned-Session-ID attribute present in message\n"); - if (l2tp_session_disconnect(sess, 0, 0) < 0) - return -1; + res = 0; + err = 0; + goto out_reject; } sess->peer_sid = assigned_sid->val.uint16; + if (err) + goto out_reject; + + if (l2tp_tunnel_confirm_session(sess) < 0) { + res = 2; + err = 4; + goto out_reject; + } if (l2tp_send_ICRP(sess)) return -1; - - /*if (l2tp_send_OCRQ(conn)) - return -1;*/ - + return 0; + +out_reject: + l2tp_tunnel_cancel_session(sess, res, err); + return -1; } static int l2tp_recv_ICCN(struct l2tp_sess_t *sess, struct l2tp_packet_t *pack) @@ -1131,11 +1179,7 @@ static int l2tp_recv_CDN(struct l2tp_sess_t *sess, struct l2tp_packet_t *pack) l2tp_session_free(sess); - /* Free the tunnel when all sessions have been closed */ - if (sess->paren_conn->sessions == NULL) - return l2tp_tunnel_disconnect(sess->paren_conn, 1, 0); - else - return 0; + return 0; } static int l2tp_recv_SLI(struct l2tp_conn_t *conn, struct l2tp_packet_t *pack) @@ -1143,6 +1187,51 @@ static int l2tp_recv_SLI(struct l2tp_conn_t *conn, struct l2tp_packet_t *pack) return 0; } +static void l2tp_session_recv(void *data) +{ + struct triton_context_t *ctx = triton_context_self(); + struct l2tp_sess_t *sess = container_of(ctx, typeof(*sess), sctx); + struct l2tp_packet_t *pack = data; + struct l2tp_attr_t *msg_type = NULL; + + msg_type = list_entry(pack->attrs.next, typeof(*msg_type), entry); + if (msg_type->attr->id != Message_Type) { + if (conf_verbose) + log_ppp_error("l2tp: first attribute is not" + " Message-Type, dropping session...\n"); + l2tp_session_disconnect(sess, 2, 3); + goto out; + } + + switch (msg_type->val.uint16) { + case Message_Type_Incoming_Call_Connected: + l2tp_recv_ICCN(sess, pack); + break; + case Message_Type_Outgoing_Call_Reply: + l2tp_recv_OCRP(sess, pack); + break; + case Message_Type_Outgoing_Call_Connected: + l2tp_recv_OCCN(sess, pack); + break; + case Message_Type_Call_Disconnect_Notify: + l2tp_recv_CDN(sess, pack); + break; + default: + if (conf_verbose) { + log_warn("l2tp: unexpected Message-Type %hu\n", + msg_type->val.uint16); + } + if (msg_type->M) { + l2tp_session_disconnect(sess, 2, 8); + } + break; + } + +out: + l2tp_packet_free(pack); + return; +} + static int l2tp_conn_read(struct triton_md_handler_t *h) { struct l2tp_conn_t *conn = container_of(h, typeof(*conn), hnd); @@ -1239,25 +1328,14 @@ static int l2tp_conn_read(struct triton_md_handler_t *h) goto drop; break; case Message_Type_Incoming_Call_Connected: - sess = l2tp_tunnel_get_session(conn, ntohs(pack->hdr.sid)); - if (sess == NULL || l2tp_recv_ICCN(sess, pack) < 0) - goto drop; - break; case Message_Type_Outgoing_Call_Reply: - sess = l2tp_tunnel_get_session(conn, ntohs(pack->hdr.sid)); - if (sess == NULL || l2tp_recv_OCRP(sess, pack) < 0) - goto drop; - break; case Message_Type_Outgoing_Call_Connected: - sess = l2tp_tunnel_get_session(conn, ntohs(pack->hdr.sid)); - if (sess == NULL || l2tp_recv_OCCN(sess, pack) < 0) - goto drop; - break; case Message_Type_Call_Disconnect_Notify: sess = l2tp_tunnel_get_session(conn, ntohs(pack->hdr.sid)); - if (sess == NULL || l2tp_recv_CDN(sess, pack) < 0) + if (sess == NULL) goto drop; - break; + triton_context_call(&sess->sctx, l2tp_session_recv, pack); + continue; case Message_Type_Set_Link_Info: if (l2tp_recv_SLI(conn, pack)) goto drop; |