]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
allegedly universal MTU calculator: use for GPST and PPP
authorDaniel Lenski <dlenski@gmail.com>
Sat, 16 May 2020 22:51:46 +0000 (15:51 -0700)
committerDaniel Lenski <dlenski@gmail.com>
Sun, 17 May 2020 13:53:26 +0000 (06:53 -0700)
Signed-off-by: Daniel Lenski <dlenski@gmail.com>
Makefile.am
cstp.c
gpst.c
mtucalc.c [new file with mode: 0644]
openconnect-internal.h
ppp.c

index c7b7ac628cdfd1ae6a7d6080f4bf66dbe746087c..fcbee7008d3b945e5fbf3404d27bdc5507b6124b 100644 (file)
@@ -27,7 +27,7 @@ openconnect_LDADD = libopenconnect.la $(SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIB
 if OPENCONNECT_WIN32
 openconnect_SOURCES += openconnect.rc
 endif
-library_srcs = ssl.c http.c http-auth.c auth-common.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c openconnect-internal.h
+library_srcs = ssl.c http.c http-auth.c auth-common.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c mtucalc.c openconnect-internal.h
 lib_srcs_cisco = auth.c cstp.c
 lib_srcs_juniper = oncp.c lzo.c auth-juniper.c
 lib_srcs_nx = nx.c
diff --git a/cstp.c b/cstp.c
index 1e38f56efbd95e5ad0a3481b0cef4d107e265437..138a5897b1ab9b01d7fabe1fa91275f0c1f6a82c 100644 (file)
--- a/cstp.c
+++ b/cstp.c
@@ -96,7 +96,7 @@ static const struct pkt dpd_resp_pkt = {
  * If we don't even have TCP_MAXSEG, then default to sending a legacy MTU of
  * 1406 which is what we always used to do.
  */
-static void calculate_mtu(struct openconnect_info *vpninfo, int *base_mtu, int *mtu)
+static void calculate_dtls_mtu(struct openconnect_info *vpninfo, int *base_mtu, int *mtu)
 {
        *mtu = vpninfo->reqmtu;
        *base_mtu = vpninfo->basemtu;
@@ -240,7 +240,7 @@ static int start_cstp_connection(struct openconnect_info *vpninfo)
        free_split_routes(vpninfo);
 
  retry:
-       calculate_mtu(vpninfo, &base_mtu, &mtu);
+       calculate_dtls_mtu(vpninfo, &base_mtu, &mtu);
        vpninfo->cstp_basemtu = base_mtu;
 
        reqbuf = buf_alloc();
diff --git a/gpst.c b/gpst.c
index a32668773addf45a8a8c05005ee2ea8a58c57722..833e3bc8feac1fbdca5c7fec0e17dbc6815dec65 100644 (file)
--- a/gpst.c
+++ b/gpst.c
@@ -314,96 +314,8 @@ out:
        return result;
 }
 
-
 #define ESP_HEADER_SIZE (4 /* SPI */ + 4 /* sequence number */)
 #define ESP_FOOTER_SIZE (1 /* pad length */ + 1 /* next header */)
-#define UDP_HEADER_SIZE 8
-#define TCP_HEADER_SIZE 20 /* with no options */
-#define IPV4_HEADER_SIZE 20
-#define IPV6_HEADER_SIZE 40
-
-/* Based on cstp.c's calculate_mtu().
- *
- * With HTTPS tunnel, there are 21 bytes of overhead beyond the
- * TCP MSS: 5 bytes for TLS and 16 for GPST.
- */
-static int calculate_mtu(struct openconnect_info *vpninfo, int can_use_esp)
-{
-       int mtu = vpninfo->reqmtu, base_mtu = vpninfo->basemtu;
-       int mss = 0;
-
-#if defined(__linux__) && defined(TCP_INFO)
-       if (!mtu) {
-               struct tcp_info ti;
-               socklen_t ti_size = sizeof(ti);
-
-               if (!getsockopt(vpninfo->ssl_fd, IPPROTO_TCP, TCP_INFO,
-                               &ti, &ti_size)) {
-                       vpn_progress(vpninfo, PRG_DEBUG,
-                                    _("TCP_INFO rcv mss %d, snd mss %d, adv mss %d, pmtu %d\n"),
-                                    ti.tcpi_rcv_mss, ti.tcpi_snd_mss, ti.tcpi_advmss, ti.tcpi_pmtu);
-
-                       if (!base_mtu) {
-                               base_mtu = ti.tcpi_pmtu;
-                       }
-
-                       /* XXX: GlobalProtect has no mechanism to inform the server about the
-                        * desired MTU, so could just ignore the "incoming" MSS (tcpi_rcv_mss).
-                        */
-                       mss = MIN(ti.tcpi_rcv_mss, ti.tcpi_snd_mss);
-               }
-       }
-#endif
-#ifdef TCP_MAXSEG
-       if (!mtu && !mss) {
-               socklen_t mss_size = sizeof(mss);
-               if (!getsockopt(vpninfo->ssl_fd, IPPROTO_TCP, TCP_MAXSEG,
-                               &mss, &mss_size)) {
-                       vpn_progress(vpninfo, PRG_DEBUG, _("TCP_MAXSEG %d\n"), mss);
-               }
-       }
-#endif
-       if (!base_mtu) {
-               /* Default */
-               base_mtu = 1406;
-       }
-
-       if (base_mtu < 1280)
-               base_mtu = 1280;
-
-#ifdef HAVE_ESP
-       /* If we can use the ESP tunnel then we should pick the optimal MTU for ESP. */
-       if (!mtu && can_use_esp) {
-               /* remove ESP, UDP, IP headers from base (wire) MTU */
-               mtu = ( base_mtu - UDP_HEADER_SIZE - ESP_HEADER_SIZE
-                       - vpninfo->hmac_out_len
-                       - MAX_IV_SIZE);
-               if (vpninfo->peer_addr->sa_family == AF_INET6)
-                       mtu -= IPV6_HEADER_SIZE;
-               else
-                       mtu -= IPV4_HEADER_SIZE;
-               /* round down to a multiple of blocksize (16 bytes for both AES-128 and AES-256) */
-               mtu -= mtu % 16;
-               /* subtract ESP footer, which is included in the payload before padding to the blocksize */
-               mtu -= ESP_FOOTER_SIZE;
-
-       } else
-#endif
-
-    /* We are definitely using the TLS tunnel, so we should base our MTU on the TCP MSS. */
-       if (!mtu) {
-               if (mss)
-                       mtu = mss - 21;
-               else {
-                       mtu = base_mtu - TCP_HEADER_SIZE - 21;
-                       if (vpninfo->peer_addr->sa_family == AF_INET6)
-                               mtu -= IPV6_HEADER_SIZE;
-                       else
-                               mtu -= IPV4_HEADER_SIZE;
-               }
-       }
-       return mtu;
-}
 
 #ifdef HAVE_ESP
 static int check_hmac_algo(struct openconnect_info *v, const char *s)
@@ -661,7 +573,15 @@ static int gpst_get_config(struct openconnect_info *vpninfo)
 #else
                no_esp_reason = _("ESP support not available in this build");
 #endif
-               vpninfo->ip_info.mtu = calculate_mtu(vpninfo, !no_esp_reason);
+               if (!no_esp_reason)
+                       vpninfo->ip_info.mtu = calculate_mtu(
+                               vpninfo, 1,
+                               ESP_HEADER_SIZE + vpninfo->hmac_out_len + MAX_IV_SIZE, /* ESP header size */
+                               ESP_FOOTER_SIZE, /* ESP footer (contributes to payload before padding) */
+                               16 /* blocksize for both AES-128 and AES-256 */ );
+               else
+                       vpninfo->ip_info.mtu = calculate_mtu(vpninfo, 0, TLS_OVERHEAD, 0, 1);
+
                vpn_progress(vpninfo, PRG_ERR,
                             _("No MTU received. Calculated %d for %s%s\n"), vpninfo->ip_info.mtu,
                             no_esp_reason ? "SSL tunnel. " : "ESP tunnel", no_esp_reason ? : "");
diff --git a/mtucalc.c b/mtucalc.c
new file mode 100644 (file)
index 0000000..22cc3da
--- /dev/null
+++ b/mtucalc.c
@@ -0,0 +1,119 @@
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2020 Daniel Lenski
+ *
+ * Authors: Daniel Lenski <dlenski@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include <config.h>
+#include "openconnect-internal.h"
+
+#if defined(__linux__)
+/* For TCP_INFO */
+# include <linux/tcp.h>
+#endif
+
+#define ESP_HEADER_SIZE (4 /* SPI */ + 4 /* sequence number */)
+#define ESP_FOOTER_SIZE (1 /* pad length */ + 1 /* next header */)
+#define UDP_HEADER_SIZE 8
+#define TCP_HEADER_SIZE 20 /* with no options */
+#define IPV4_HEADER_SIZE 20
+#define IPV6_HEADER_SIZE 40
+
+/* Calculate MTU of a tunnel.
+ *
+ * is_udp: 1 if outer packet is UDP, 0 if TCP
+ * unpadded_overhead: overhead that does not get padded (headers or footers)
+ * padded_overhead:   overhead that gets before padding the payload (typically footers)
+ * block_size:        block size that payload will be padded to AFTER adding padded overhead
+ */
+
+int calculate_mtu(struct openconnect_info *vpninfo, int is_udp,
+                 int unpadded_overhead, int padded_overhead, int block_size)
+{
+       int mtu = vpninfo->reqmtu, base_mtu = vpninfo->basemtu;
+       int mss = 0;
+
+       /* Try to figure out base_mtu (from TCP PMTU), and save TCP MSS for later */
+#if defined(__linux__) && defined(TCP_INFO)
+       if (!mtu) {
+               struct tcp_info ti;
+               socklen_t ti_size = sizeof(ti);
+
+               if (!getsockopt(vpninfo->ssl_fd, IPPROTO_TCP, TCP_INFO,
+                               &ti, &ti_size)) {
+                       vpn_progress(vpninfo, PRG_DEBUG,
+                                    _("TCP_INFO rcv mss %d, snd mss %d, adv mss %d, pmtu %d\n"),
+                                    ti.tcpi_rcv_mss, ti.tcpi_snd_mss, ti.tcpi_advmss, ti.tcpi_pmtu);
+
+                       /* If base_mtu unknown, use TCP PMTU */
+                       if (!base_mtu) {
+                               base_mtu = ti.tcpi_pmtu;
+                       }
+
+                       /* Use largest MSS */
+                       mss = MAX(ti.tcpi_rcv_mss, ti.tcpi_snd_mss);
+                       mss = MAX(mss, ti.tcpi_advmss);
+               }
+       }
+#endif
+#ifdef TCP_MAXSEG
+       if (!mtu && !mss) {
+               socklen_t mss_size = sizeof(mss);
+               if (!getsockopt(vpninfo->ssl_fd, IPPROTO_TCP, TCP_MAXSEG,
+                               &mss, &mss_size)) {
+                       vpn_progress(vpninfo, PRG_DEBUG, _("TCP_MAXSEG %d\n"), mss);
+               }
+       }
+#endif
+
+       /* Default base_mtu needs to be big enough for IPv6 (1280 minimum) */
+       if (!base_mtu) {
+               /* Default */
+               base_mtu = 1406;
+       }
+
+       if (base_mtu < 1280)
+               base_mtu = 1280;
+
+       vpn_progress(vpninfo, PRG_TRACE, _("Using base_mtu of %d\n"), base_mtu);
+
+        /* base_mtu is now (we hope) the PMTU between our external network interface
+        * and the VPN gateway */
+
+       if (!mtu) {
+               if (!is_udp && mss)
+                       /* MSS already has IP, TCP header size removed */
+                       mtu = mss;
+               else {
+                       /* remove TCP/UDP, IP headers from base (wire) MTU */
+                       mtu = base_mtu - (is_udp ? UDP_HEADER_SIZE : TCP_HEADER_SIZE);
+                       mtu -= (vpninfo->peer_addr->sa_family == AF_INET6) ? IPV6_HEADER_SIZE : IPV4_HEADER_SIZE;
+               }
+       }
+
+       vpn_progress(vpninfo, PRG_TRACE, _("After remove %s/IPv%d headers, mtu of %d\n"),
+                    (is_udp ? "UDP" : "TCP"), vpninfo->peer_addr->sa_family == AF_INET6 ? 6 : 4, mtu);
+
+        /* MTU is now (we hope) the number of payload bytes that can fit in a UDP or
+        * TCP packet exchanged with the VPN gateway. */
+
+       mtu -= unpadded_overhead; /* remove protocol-specific overhead that isn't affected by padding */
+       mtu -= mtu % block_size;  /* round down to a multiple of blocksize */
+       mtu -= padded_overhead;   /* remove protocol-specific overhead that contributes to payload padding */
+
+       vpn_progress(vpninfo, PRG_TRACE, _("After remove protocol specific overhead (%d unpadded, %d padded, %d blocksize), mtu of %d\n"),
+                    unpadded_overhead, padded_overhead, block_size, mtu);
+
+       return mtu;
+}
index 890c264a9512dac15adbf753c4003dc33d468da5..f36bd61fbf3ba0016c83ce3987d791b6b6963595 100644 (file)
@@ -353,6 +353,7 @@ static inline void init_pkt_queue(struct pkt_q *q)
        q->tail = &q->head;
 }
 
+#define TLS_OVERHEAD 5 /* packet + header */
 #define DTLS_OVERHEAD (1 /* packet + header */ + 13 /* DTLS header */ + \
         20 /* biggest supported MAC (SHA1) */ +  32 /* biggest supported IV (AES-256) */ + \
         16 /* max padding */)
@@ -907,6 +908,10 @@ int openconnect_dtls_write(struct openconnect_info *vpninfo, void *buf, size_t l
 char *openconnect_bin2hex(const char *prefix, const uint8_t *data, unsigned len);
 char *openconnect_bin2base64(const char *prefix, const uint8_t *data, unsigned len);
 
+/* mtucalc.c */
+
+int calculate_mtu(struct openconnect_info *vpninfo, int is_udp, int unpadded_overhead, int padded_overhead, int block_size);
+
 /* cstp.c */
 void cstp_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);
 int cstp_connect(struct openconnect_info *vpninfo);
diff --git a/ppp.c b/ppp.c
index fad9476fa9f20e331a79b01af110d6e996c508ce..0a08ed25db9f63cecbbf3102160d709bbe0d8e50 100644 (file)
--- a/ppp.c
+++ b/ppp.c
@@ -466,8 +466,15 @@ static int queue_config_request(struct openconnect_info *vpninfo, int proto)
        switch (proto) {
        case PPP_LCP:
                ncp = &ppp->lcp;
-               if (!vpninfo->ip_info.mtu)
-                       vpninfo->ip_info.mtu = 1300; /* FIXME */
+               if (!vpninfo->ip_info.mtu) {
+                       vpninfo->ip_info.mtu = calculate_mtu(vpninfo, 0 /* not UDP */,
+                                                            /* 1-byte PPP header with ACCOMP, PFCOMP; 4-bytes HDLC framing and FCS */
+                                                            ppp->encap_len + 1 + (ppp->hdlc ? 4 : 0),
+                                                            0, 1); /* no footer or block padding */
+                       /* XX: HDLC fudge factor (average overhead on random payload is 1/128, we'll use more like 2%) */
+                       if (ppp->hdlc)
+                               vpninfo->ip_info.mtu -= vpninfo->ip_info.mtu / 50;
+               }
 
                if (ppp->out_lcp_opts & BIT_MRU)
                        buf_append_ppp_tlv_be16(buf, LCP_MRU, vpninfo->ip_info.mtu);