From d2402eb30377c3c494f6b3c475b5a5ffc24b5547 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 12 Apr 2021 14:42:56 +0100 Subject: [PATCH] Split out core ppp_mainloop() and add basic DTLS support to it Add a ppp_tcp_mainloop() to replace the existing version. We rely on the fact that there will only be one of DTLS and TLS active at the same time. Signed-off-by: Daniel Lenski Signed-off-by: David Woodhouse --- library.c | 4 +- nullppp.c | 2 +- openconnect-internal.h | 2 +- ppp.c | 84 +++++++++++++++++++++++++++--------------- 4 files changed, 58 insertions(+), 34 deletions(-) diff --git a/library.c b/library.c index 7b84b5c6..7dbe0cbf 100644 --- a/library.c +++ b/library.c @@ -200,7 +200,7 @@ static const struct vpn_proto openconnect_protos[] = { .flags = OC_PROTO_PROXY | OC_PROTO_AUTH_CERT, .vpn_close_session = f5_bye, .tcp_connect = f5_connect, - .tcp_mainloop = ppp_mainloop, + .tcp_mainloop = ppp_tcp_mainloop, .add_http_headers = http_common_headers, .obtain_cookie = f5_obtain_cookie, .secure_cookie = "MRHSession", @@ -221,7 +221,7 @@ static const struct vpn_proto openconnect_protos[] = { .flags = OC_PROTO_PROXY | OC_PROTO_AUTH_CERT | OC_PROTO_AUTH_OTP | OC_PROTO_AUTH_STOKEN, .vpn_close_session = fortinet_bye, .tcp_connect = fortinet_connect, - .tcp_mainloop = ppp_mainloop, + .tcp_mainloop = ppp_tcp_mainloop, .add_http_headers = fortinet_common_headers, .obtain_cookie = fortinet_obtain_cookie, .secure_cookie = "SVPNCOOKIE", diff --git a/nullppp.c b/nullppp.c index ef772f56..c6e882cd 100644 --- a/nullppp.c +++ b/nullppp.c @@ -80,5 +80,5 @@ int nullppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readabl _("Terminating because nullppp has reached network state.\n")); } - return ppp_mainloop(vpninfo, timeout, readable); + return ppp_tcp_mainloop(vpninfo, timeout, readable); } diff --git a/openconnect-internal.h b/openconnect-internal.h index a121e5cf..b5f1083c 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -1049,7 +1049,7 @@ struct oc_ppp; void buf_append_ppphdlc(struct oc_text_buf *buf, const unsigned char *bytes, int len, uint32_t asyncmap); void buf_append_ppp_hdr(struct oc_text_buf *buf, struct oc_ppp *ppp, uint16_t proto, uint8_t code, uint8_t id); int ppp_negotiate_config(struct openconnect_info *vpninfo); -int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable); +int ppp_tcp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable); int openconnect_ppp_new(struct openconnect_info *vpninfo, int encap, int want_ipv4, int want_ipv6); int ppp_reset(struct openconnect_info *vpninfo); diff --git a/ppp.c b/ppp.c index 6e0b2919..88b2d62a 100644 --- a/ppp.c +++ b/ppp.c @@ -869,7 +869,8 @@ static int handle_config_packet(struct openconnect_info *vpninfo, return ret; } -static int handle_state_transition(struct openconnect_info *vpninfo, int *timeout) +static int handle_state_transition(struct openconnect_info *vpninfo, int dtls, + struct keepalive_info *kai, int *timeout) { struct oc_ppp *ppp = vpninfo->ppp; time_t now = time(NULL); @@ -878,7 +879,7 @@ static int handle_state_transition(struct openconnect_info *vpninfo, int *timeou switch (ppp->ppp_state) { case PPPS_DEAD: /* Prevent race conditions after recovering dead peer connection */ - vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = now; + kai->last_rx = kai->last_tx = now; /* Drop any failed outgoing packet from previous connection; * we need to reconfigure before we can send data packets. */ @@ -1012,8 +1013,9 @@ static int handle_state_transition(struct openconnect_info *vpninfo, int *timeou if (last_state != ppp->ppp_state) { vpn_progress(vpninfo, PRG_DEBUG, - _("PPP state transition from %s to %s\n"), - ppps_names[last_state], ppps_names[ppp->ppp_state]); + _("PPP state transition from %s to %s on %s channel\n"), + ppps_names[last_state], ppps_names[ppp->ppp_state], + dtls ? "DTLS" : "TLS"); print_ppp_state(vpninfo, PRG_TRACE); return 1; } @@ -1034,7 +1036,8 @@ static inline void add_ppp_header(struct pkt *p, struct oc_ppp *ppp, int proto) p->ppp.hlen = p->data - ph; } -int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) +static int ppp_mainloop(struct openconnect_info *vpninfo, int dtls, + struct keepalive_info *kai, int *timeout, int readable) { int ret, magic, rsv_hdr_size; int work_done = 0; @@ -1042,10 +1045,10 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) struct oc_ppp *ppp = vpninfo->ppp; int proto; - if (vpninfo->ssl_fd == -1) + if ((dtls ? vpninfo->dtls_fd : vpninfo->ssl_fd) == -1) goto do_reconnect; - handle_state_transition(vpninfo, timeout); + handle_state_transition(vpninfo, dtls, kai, timeout); /* FIXME: The poll() handling here is fairly simplistic. Actually, if the SSL connection stalls it could return a WANT_WRITE error @@ -1077,7 +1080,7 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) /* Load the encap header to end up with the payload where we expect it */ eh = this->data - rsv_hdr_size; - len = ssl_nonblock_read(vpninfo, 0, eh, receive_mtu + rsv_hdr_size); + len = ssl_nonblock_read(vpninfo, dtls, eh, receive_mtu + rsv_hdr_size); if (!len) break; if (len < 0) @@ -1210,7 +1213,7 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) * payload_len: number of bytes in PPP *payload* */ - vpninfo->ssl_times.last_rx = time(NULL); + kai->last_rx = time(NULL); switch (proto) { case PPP_LCP: @@ -1222,7 +1225,7 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) goto short_pkt; if ((ret = handle_config_packet(vpninfo, proto, pp, payload_len)) < 0) return ret; - else if ((ret = handle_state_transition(vpninfo, timeout)) < 0) + else if ((ret = handle_state_transition(vpninfo, dtls, kai, timeout)) < 0) return ret; break; @@ -1235,8 +1238,9 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) dump_buf_hex(vpninfo, PRG_ERR, '<', pp, payload_len); } else { vpn_progress(vpninfo, PRG_TRACE, - _("Received IPv%d data packet of %d bytes\n"), - proto == PPP_IP6 ? 6 : 4, payload_len); + _("Received IPv%d data packet of %d bytes over %s\n"), + proto == PPP_IP6 ? 6 : 4, payload_len, + dtls ? "DTLS" : "TLS"); if (pp != this->data) { vpn_progress(vpninfo, PRG_TRACE, @@ -1299,10 +1303,13 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) packet we had before.... */ if ((this = vpninfo->current_ssl_pkt)) { handle_outgoing: - vpninfo->ssl_times.last_tx = time(NULL); - unmonitor_write_fd(vpninfo, ssl); + kai->last_tx = time(NULL); + if (dtls) + unmonitor_write_fd(vpninfo, dtls); + else + unmonitor_write_fd(vpninfo, ssl); - ret = ssl_nonblock_write(vpninfo, 0, this->data - this->ppp.hlen, this->len + this->ppp.hlen); + ret = ssl_nonblock_write(vpninfo, dtls, this->data - this->ppp.hlen, this->len + this->ppp.hlen); if (ret < 0) goto do_reconnect; else if (!ret) { @@ -1310,7 +1317,7 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) fd to ->select_wfds if appropriate, so we can just return and wait. Unless it's been stalled for so long that DPD kicks in and we kill the connection. */ - switch (ka_stalled_action(&vpninfo->ssl_times, timeout)) { + switch (ka_stalled_action(kai, timeout)) { case KA_DPD_DEAD: goto peer_dead; case KA_REKEY: @@ -1336,7 +1343,7 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) vpninfo->current_ssl_pkt = NULL; } - switch (keepalive_action(&vpninfo->ssl_times, timeout)) { + switch (keepalive_action(kai, timeout)) { case KA_DPD_DEAD: peer_dead: vpn_progress(vpninfo, PRG_ERR, @@ -1344,11 +1351,17 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) /* fall through */ case KA_REKEY: do_reconnect: - ret = ssl_reconnect(vpninfo); - if (ret) { - vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n")); - vpninfo->quit_reason = "PPP reconnect failed"; - return ret; + if (dtls) { + /* This leaves it in state DTLS_SLEEPING, and will allow + * the protocol to handle any magic required to reopen it. */ + dtls_close(vpninfo); + } else { + ret = ssl_reconnect(vpninfo); + if (ret) { + vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n")); + vpninfo->quit_reason = "PPP reconnect failed"; + return ret; + } } return 1; @@ -1356,7 +1369,7 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) /* No need to send an explicit keepalive if we have real data to send */ if (vpninfo->tcp_control_queue.head || - (vpninfo->dtls_state != DTLS_CONNECTED && ppp->ppp_state == PPPS_NETWORK && vpninfo->outgoing_queue.head)) + (ppp->ppp_state == PPPS_NETWORK && vpninfo->outgoing_queue.head)) break; vpn_progress(vpninfo, PRG_DEBUG, _("Send PPP discard request as keepalive\n")); queue_config_packet(vpninfo, PPP_LCP, ++ppp->lcp.id, DISCREQ, 0, NULL); @@ -1370,9 +1383,8 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) if ((this = vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->tcp_control_queue))) { /* XX: We pre-stash the PPP protocol field in the header for control packets */ proto = this->ppp.proto; - handle_state_transition(vpninfo, timeout); - } else if (vpninfo->dtls_state != DTLS_CONNECTED && - ppp->ppp_state == PPPS_NETWORK && + handle_state_transition(vpninfo, dtls, kai, timeout); + } else if (ppp->ppp_state == PPPS_NETWORK && (this = vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->outgoing_queue))) { /* XX: Set protocol for IP packets */ proto = (this->len && (this->data[0] & 0xf0) == 0x60) ? PPP_IP6 : PPP_IP; @@ -1429,12 +1441,16 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) if (lcp) vpn_progress(vpninfo, PRG_TRACE, - _("Sending PPP %s %s packet (id %d, %d bytes total)\n"), - proto_names(proto), lcp, id, this->len + this->ppp.hlen); + _("Sending PPP %s %s packet over %s (id %d, %d bytes total)\n"), + proto_names(proto), lcp, + dtls ? "DTLS" : "TLS", + id, this->len + this->ppp.hlen); else vpn_progress(vpninfo, PRG_TRACE, - _("Sending PPP %s packet (%d bytes total)\n"), - proto_names(proto), this->len + this->ppp.hlen); + _("Sending PPP %s packet over %s (%d bytes total)\n"), + proto_names(proto), + dtls ? "DTLS" : "TLS", + this->len + this->ppp.hlen); if (vpninfo->dump_http_traffic) dump_buf_hex(vpninfo, PRG_TRACE, '>', this->data - this->ppp.hlen, this->len + this->ppp.hlen); @@ -1446,3 +1462,11 @@ int ppp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) /* Work is not done if we just got rid of packets off the queue */ return work_done; } + +int ppp_tcp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) +{ + if (vpninfo->dtls_state >= DTLS_SLEEPING) + return 0; + + return ppp_mainloop(vpninfo, 0, &vpninfo->ssl_times, timeout, readable); +} -- 2.50.1