]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Split out core ppp_mainloop() and add basic DTLS support to it
authorDavid Woodhouse <dwmw2@infradead.org>
Mon, 12 Apr 2021 13:42:56 +0000 (14:42 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Wed, 21 Apr 2021 12:39:15 +0000 (13:39 +0100)
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 <dlenski@gmail.com>
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
library.c
nullppp.c
openconnect-internal.h
ppp.c

index 7b84b5c65483f677bfa2511347da20d61cdec9fc..7dbe0cbfb3b3eba4da3897ea5d282e9ae7626745 100644 (file)
--- 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",
index ef772f56a0e9d0ba061620cf0fd22c48fb2d1bdf..c6e882cd985a3ec7229dcb374e77cb712bc612b2 100644 (file)
--- 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);
 }
index a121e5cf610bc90b272426605684ed28086c8f3b..b5f1083c0391391d7e1084b660266d5c60be7cd8 100644 (file)
@@ -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 6e0b2919783c7f5063ed2496c68a2dea98a9474f..88b2d62a75ec0ba61256894b466fed53bc71636b 100644 (file)
--- 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);
+}