#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>
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];
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;
}
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);
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: ");
enum {
OPT_AUTHGROUP = 0x100,
+ OPT_BASEMTU,
OPT_CAFILE,
OPT_CONFIGFILE,
OPT_COOKIEONLY,
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'),
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"));
/* 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;
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;