]> 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>
Mon, 29 Mar 2021 02:27:01 +0000 (19:27 -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

index 636e183f7a8b03b3ce0bb8a158acc3abb0af0db1..6b512500ec166f3a293d592e5d96cddaa4840cff 100644 (file)
@@ -30,7 +30,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 auth-html.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 auth-html.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_pulse = pulse.c
diff --git a/cstp.c b/cstp.c
index 280e42427632842a8f84763a16cbd95b5320c915..c2c6c83b8c9f30182addcb6acf9b4de4323c82ce 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;
@@ -275,7 +275,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 42d290d6b60e17bf43ea194005f8dd3ff6d1c3b8..345d5143d52e57db3641029775ad276437c6d1c2 100644 (file)
--- a/gpst.c
+++ b/gpst.c
@@ -318,93 +318,6 @@ out:
 
 #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)
@@ -746,7 +659,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..7da9ebf
--- /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 removing %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 removing protocol specific overhead (%d unpadded, %d padded, %d blocksize), MTU of %d\n"),
+                    unpadded_overhead, padded_overhead, block_size, mtu);
+
+       return mtu;
+}
index b9a00456c1731dc7206bde5fa0ba5c9b70ff9e7e..974248dc3c2ce8cb0f0627a4e80f1b907f52beff 100644 (file)
@@ -351,6 +351,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 */)
@@ -910,6 +911,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 */
 int check_address_sanity(struct openconnect_info *vpninfo, const char *old_addr, const char *old_netmask, const char *old_addr6, const char *old_netmask6);
 void cstp_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);