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
* 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;
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();
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)
#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 ? : "");
--- /dev/null
+/*
+ * 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;
+}
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 */)
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);
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);