Set X-CSTP-Base-MTU: for new servers
authorDavid Woodhouse <David.Woodhouse@intel.com>
Fri, 8 Jun 2012 13:25:15 +0000 (14:25 +0100)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Fri, 8 Jun 2012 13:27:49 +0000 (14:27 +0100)
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
cstp.c
main.c
openconnect-internal.h
openconnect.8.in

diff --git a/cstp.c b/cstp.c
index dbc24b9..0e9e6c1 100644 (file)
--- a/cstp.c
+++ b/cstp.c
@@ -32,6 +32,9 @@
 #include <errno.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include <netinet/tcp.h>
+#include <sys/types.h>
+#include <sys/socket.h>
 
 #include <openssl/ssl.h>
 #include <openssl/err.h>
@@ -86,6 +89,64 @@ static int  __attribute__ ((format (printf, 3, 4)))
        return ret;
 }
 
+/* Calculate MTU to request. Old servers simply use the X-CSTP-MTU: header,
+ * which represents the tunnel MTU, while new servers do calculations on the
+ * X-CSTP-Base-MTU: header which represents the cleartext MTU between client
+ * and server.
+ *
+ * If possible, the legacy MTU value should be the TCP MSS less 5 bytes of
+ * TLS and 8 bytes of CSTP overhead. We can get the MSS from either the
+ * TCP_INFO or TCP_MAXSEG sockopts.
+ *
+ * The base MTU comes from the TCP_INFO sockopt under Linux, but I don't know
+ * how to work it out on other systems. So leave it blank and do things the
+ * legacy way there. Contributions welcome...
+ *
+ * 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)
+{
+       *mtu = vpninfo->mtu;
+       *base_mtu = vpninfo->basemtu;
+
+#ifdef TCP_INFO
+       if (!*mtu || !*base_mtu) {
+               struct tcp_info ti;
+               socklen_t ti_size = sizeof(ti);
+
+               if (!getsockopt(vpninfo->ssl_fd, SOL_TCP, TCP_INFO,
+                               &ti, &ti_size)) {
+                       vpn_progress(vpninfo, PRG_TRACE,
+                                    _("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;
+                       if (!*mtu) {
+                               if (ti.tcpi_rcv_mss < ti.tcpi_snd_mss)
+                                       *mtu = ti.tcpi_rcv_mss - 13;
+                               else
+                                       *mtu = ti.tcpi_snd_mss - 13;
+                       }
+               }
+       }
+#endif
+#ifdef TCP_MAXSEG
+       if (!*mtu) {
+               int mss;
+               socklen_t mss_size = sizeof(mss);
+               if (!getsockopt(vpninfo->ssl_fd, SOL_TCP, TCP_MAXSEG,
+                               &mss, &mss_size)) {
+                       vpn_progress(vpninfo, PRG_TRACE, _("TCP_MAXSEG %d\n"), mss);
+                       *mtu = mss - 13;
+               }
+       }
+#endif
+       if (!*mtu) {
+               /* Default */
+               *mtu = 1406;
+       }
+}
+
 static int start_cstp_connection(struct openconnect_info *vpninfo)
 {
        char buf[65536];
@@ -100,6 +161,7 @@ static int start_cstp_connection(struct openconnect_info *vpninfo)
        const char *old_addr6 = vpninfo->vpn_addr6;
        const char *old_netmask6 = vpninfo->vpn_netmask6;
        struct split_include *inc;
+       int base_mtu, mtu;
 
        /* Clear old options which will be overwritten */
        vpninfo->vpn_addr = vpninfo->vpn_netmask = NULL;
@@ -132,6 +194,8 @@ static int start_cstp_connection(struct openconnect_info *vpninfo)
        }
 
  retry:
+       calculate_mtu(vpninfo, &base_mtu, &mtu);
+
        buf[0] = 0;
        buf_append(buf, sizeof(buf), "CONNECT /CSCOSSLC/tunnel HTTP/1.1\r\n");
        buf_append(buf, sizeof(buf), "Host: %s\r\n", vpninfo->hostname);
@@ -141,7 +205,9 @@ static int start_cstp_connection(struct openconnect_info *vpninfo)
        buf_append(buf, sizeof(buf), "X-CSTP-Hostname: %s\r\n", vpninfo->localname);
        if (vpninfo->deflate && i < sizeof(buf))
                buf_append(buf, sizeof(buf), "X-CSTP-Accept-Encoding: deflate;q=1.0\r\n");
-       buf_append(buf, sizeof(buf), "X-CSTP-MTU: %d\r\n", vpninfo->mtu);
+       if (base_mtu)
+               buf_append(buf, sizeof(buf), "X-CSTP-Base-MTU: %d\r\n", base_mtu);
+       buf_append(buf, sizeof(buf), "X-CSTP-MTU: %d\r\n", mtu);
        buf_append(buf, sizeof(buf), "X-CSTP-Address-Type: %s\r\n",
                               vpninfo->disable_ipv6?"IPv4":"IPv6,IPv4");
        buf_append(buf, sizeof(buf), "X-DTLS-Master-Secret: ");
diff --git a/main.c b/main.c
index 6f7f86c..fa03558 100644 (file)
--- a/main.c
+++ b/main.c
@@ -82,6 +82,7 @@ int non_inter;
 
 enum {
        OPT_AUTHGROUP = 0x100,
+       OPT_BASEMTU,
        OPT_CAFILE,
        OPT_CONFIGFILE,
        OPT_COOKIEONLY,
@@ -130,6 +131,7 @@ static struct option long_options[] = {
        OPTION("help", 0, 'h'),
        OPTION("interface", 1, 'i'),
        OPTION("mtu", 1, 'm'),
+       OPTION("base-mtu", 1, OPT_BASEMTU),
        OPTION("setuid", 1, 'U'),
        OPTION("script", 1, 's'),
        OPTION("script-tun", 0, 'S'),
@@ -199,6 +201,7 @@ static void usage(void)
        printf("      --csd-user=USER             %s\n", _("Drop privileges during CSD execution"));
        printf("      --csd-wrapper=SCRIPT        %s\n", _("Run SCRIPT instead of CSD binary"));
        printf("  -m, --mtu=MTU                   %s\n", _("Request MTU from server"));
+       printf("      --base-mtu=MTU              %s\n", _("Indicate path MTU to/from server"));
        printf("  -p, --key-password=PASS         %s\n", _("Set key passphrase or TPM SRK PIN"));
        printf("      --key-password-from-fsid    %s\n", _("Key passphrase is fsid of file system"));
        printf("  -P, --proxy=URL                 %s\n", _("Set proxy server"));
@@ -412,7 +415,7 @@ int main(int argc, char **argv)
        /* Set up some defaults */
        vpninfo->tun_fd = vpninfo->ssl_fd = vpninfo->dtls_fd = vpninfo->new_dtls_fd = -1;
        vpninfo->useragent = openconnect_create_useragent("Open AnyConnect VPN Agent");
-       vpninfo->mtu = 1406;
+       vpninfo->mtu = 0;
        vpninfo->deflate = 1;
        vpninfo->dtls_attempt_period = 60;
        vpninfo->max_qlen = 10;
@@ -548,6 +551,13 @@ int main(int argc, char **argv)
                                vpninfo->mtu = 576;
                        }
                        break;
+               case OPT_BASEMTU:
+                       vpninfo->basemtu = atol(config_arg);
+                       if (vpninfo->basemtu < 576) {
+                               fprintf(stderr, _("MTU %d too small\n"), vpninfo->basemtu);
+                               vpninfo->basemtu = 576;
+                       }
+                       break;
                case 'p':
                        vpninfo->cert_password = strdup(config_arg);
                        break;
index 0235c33..340fc6c 100644 (file)
@@ -199,7 +199,7 @@ struct openconnect_info {
        int script_tun;
        char *ifname;
 
-       int mtu;
+       int mtu, basemtu;
        const char *banner;
        const char *vpn_addr;
        const char *vpn_netmask;
index 97b183e..0902556 100644 (file)
@@ -22,6 +22,7 @@ openconnect \- Connect to Cisco AnyConnect VPN
 .OP \-U,\-\-setuid user
 .OP \-\-csd\-user user
 .OP \-m,\-\-mtu mtu
+.OP \-\-basemtu mtu
 .OP \-p,\-\-key\-password pass
 .OP \-P,\-\-proxy proxyurl
 .OP \-\-no\-proxy
@@ -159,7 +160,14 @@ instead of the CSD (Cisco Secure Desktop) script.
 .B \-m,\-\-mtu=MTU
 Request
 .I MTU
-from server
+from server as the MTU of the tunnel.
+.TP
+.B \-\-basemtu=MTU
+Indicate
+.I MTU
+as the path MTU between client and server on the unencrypted network. Newer
+servers will automatically calculate the MTU to be used on the tunnel from
+this value.
 .TP
 .B \-p,\-\-key\-password=PASS
 Provide passphrase for certificate file, or SRK (System Root Key) PIN for TPM