From 37316927d310aed0996b34efb5796dd08c2c3369 Mon Sep 17 00:00:00 2001 From: Ralph Schmieder Date: Fri, 22 Jul 2016 09:41:04 +0200 Subject: [PATCH] Add --passtos option to copy TOS/TCLASS from VPN packets This allows prioritised queuing of outbound packets. It is only of local significance (and importance) as it will influence queueing on the CPE which is typically the only place where this will be in effect. And the most effective place as the CPE is usually the bottleneck where all applications compete for limited upstream bandwidth. SPs do set the DSCP to 0 anyway at the trust boundary (which is the next hop from the CPE). Same goes for large corporations which also either reset the DSCP or have it set according to their policy, not the user's. It is implemented as an 'opt-in' using the --passtos command line switch in accordance with the OpenVPN implementation Signed-off-by: Ralph Schmieder Signed-off-by: David Woodhouse --- dtls.c | 32 ++++++++++++++++++++++++++++++++ libopenconnect.map.in | 5 +++++ library.c | 11 +++++++++-- main.c | 6 ++++++ openconnect-internal.h | 6 +++++- openconnect.8.in | 4 ++++ openconnect.h | 10 +++++++--- ssl.c | 9 +++++++++ www/changelog.xml | 1 + 9 files changed, 78 insertions(+), 6 deletions(-) diff --git a/dtls.c b/dtls.c index 9a879e48..127e6258 100644 --- a/dtls.c +++ b/dtls.c @@ -1020,6 +1020,38 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout) struct pkt *send_pkt = this; int ret; + /* If TOS optname is set, we want to copy the TOS/TCLASS header + to the outer UDP packet */ + if (vpninfo->dtls_tos_optname) { + int valid=1; + int tos; + + switch(this->data[0] >> 4) { + case 4: + tos = this->data[1]; + break; + case 6: + tos = (load_be16(this->data) >> 4) & 0xff; + break; + default: + vpn_progress(vpninfo, PRG_ERR, + _("Unknown packet (len %d) received: %02x %02x %02x %02x...\n"), + this->len, this->data[0], this->data[1], this->data[2], this->data[3]); + valid = 0; + } + + /* set the actual value */ + if (valid && tos != vpninfo->dtls_tos_current) { + vpn_progress(vpninfo, PRG_DEBUG, _("TOS this: %d, TOS last: %d\n"), + tos, vpninfo->dtls_tos_current); + if (setsockopt(vpninfo->dtls_fd, vpninfo->dtls_tos_proto, + vpninfo->dtls_tos_optname, &tos, sizeof(tos))) + vpn_perror(vpninfo, _("UDP setsockopt")); + else + vpninfo->dtls_tos_current = tos; + } + } + /* One byte of header */ this->cstp.hdr[7] = AC_PKT_DATA; diff --git a/libopenconnect.map.in b/libopenconnect.map.in index 5ff20f4f..44eea34a 100644 --- a/libopenconnect.map.in +++ b/libopenconnect.map.in @@ -87,6 +87,11 @@ OPENCONNECT_5_3 { openconnect_set_reconnected_handler; } OPENCONNECT_5_2; +OPENCONNECT_5_4 { + global: + openconnect_set_pass_tos; +} OPENCONNECT_5_3; + OPENCONNECT_PRIVATE { global: @SYMVER_TIME@ @SYMVER_GETLINE@ @SYMVER_JAVA@ @SYMVER_ASPRINTF@ @SYMVER_VASPRINTF@ @SYMVER_WIN32_STRERROR@ openconnect_fopen_utf8; diff --git a/library.c b/library.c index b258bbbe..3742f083 100644 --- a/library.c +++ b/library.c @@ -68,6 +68,8 @@ struct openconnect_info *openconnect_vpninfo_new(const char *useragent, init_pkt_queue(&vpninfo->incoming_queue); init_pkt_queue(&vpninfo->outgoing_queue); init_pkt_queue(&vpninfo->oncp_control_queue); + vpninfo->dtls_tos_current = 0; + vpninfo->dtls_pass_tos = 0; vpninfo->ssl_fd = vpninfo->dtls_fd = -1; vpninfo->cmd_fd = vpninfo->cmd_fd_write = -1; vpninfo->tncc_fd = -1; @@ -153,8 +155,12 @@ int openconnect_set_protocol(struct openconnect_info *vpninfo, const char *proto return -EINVAL; } -void openconnect_set_loglevel(struct openconnect_info *vpninfo, - int level) +void openconnect_set_pass_tos(struct openconnect_info *vpninfo, int enable) +{ + vpninfo->dtls_pass_tos = enable; +} + +void openconnect_set_loglevel(struct openconnect_info *vpninfo, int level) { vpninfo->verbose = level; } @@ -551,6 +557,7 @@ void openconnect_reset_ssl(struct openconnect_info *vpninfo) free(vpninfo->peer_addr); vpninfo->peer_addr = NULL; + vpninfo->dtls_tos_optname = 0; free(vpninfo->ip_info.gateway_addr); vpninfo->ip_info.gateway_addr = NULL; diff --git a/main.c b/main.c index b3de6411..8a3a0460 100644 --- a/main.c +++ b/main.c @@ -188,6 +188,7 @@ enum { OPT_HTTP_AUTH, OPT_LOCAL_HOSTNAME, OPT_PROTOCOL, + OPT_PASSTOS, }; #ifdef __sun__ @@ -227,6 +228,7 @@ static const struct option long_options[] = { OPTION("base-mtu", 1, OPT_BASEMTU), OPTION("script", 1, 's'), OPTION("timestamp", 0, OPT_TIMESTAMP), + OPTION("passtos", 0, OPT_PASSTOS), OPTION("key-password", 1, 'p'), OPTION("proxy", 1, 'P'), OPTION("proxy-auth", 1, OPT_PROXY_AUTH), @@ -761,6 +763,7 @@ static void usage(void) printf(" -l, --syslog %s\n", _("Use syslog for progress messages")); #endif printf(" --timestamp %s\n", _("Prepend timestamp to progress messages")); + printf(" --passtos %s\n", _("copy TOS / TCLASS when using DTLS")); #ifndef _WIN32 printf(" -U, --setuid=USER %s\n", _("Drop privileges after connecting")); printf(" --csd-user=USER %s\n", _("Drop privileges during CSD execution")); @@ -1372,6 +1375,9 @@ int main(int argc, char **argv) xstrdup("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")); } break; + case OPT_PASSTOS: + openconnect_set_pass_tos(vpninfo, 1); + break; case OPT_TIMESTAMP: timestamp = 1; break; diff --git a/openconnect-internal.h b/openconnect-internal.h index 2295f7ab..5f8b5157 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -586,6 +586,10 @@ struct openconnect_info { int ssl_fd; int dtls_fd; + int dtls_tos_current; + int dtls_pass_tos; + int dtls_tos_proto, dtls_tos_optname; + int cmd_fd; int cmd_fd_write; int got_cancel_cmd; @@ -674,7 +678,7 @@ struct openconnect_info { if ((_v)->verbose >= (lvl)) \ (_v)->progress((_v)->cbdata, lvl, __VA_ARGS__); \ } while(0) -#define vpn_perror(vpninfo, msg) vpn_progress((vpninfo), PRG_ERR, "%s: %s\n", (msg), strerror(errno)); +#define vpn_perror(vpninfo, msg) vpn_progress((vpninfo), PRG_ERR, "%s: %s\n", (msg), strerror(errno)) /****************************************************************************/ /* Oh Solaris how we hate thee! */ diff --git a/openconnect.8.in b/openconnect.8.in index 475501ae..1968bd07 100644 --- a/openconnect.8.in +++ b/openconnect.8.in @@ -21,6 +21,7 @@ openconnect \- Connect to Cisco AnyConnect VPN .OP \-i,\-\-interface ifname .OP \-l,\-\-syslog .OP \-\-timestamp +.OP \-\-passtos .OP \-U,\-\-setuid user .OP \-\-csd\-user user .OP \-m,\-\-mtu mtu @@ -194,6 +195,9 @@ Use syslog for progress messages .B \-\-timestamp Prepend a timestamp to each progress message .TP +.B \-\-passtos +Copy TOS / TCLASS of payload packet into DTLS packets. +.TP .B \-U,\-\-setuid=USER Drop privileges after connecting, to become user .I USER diff --git a/openconnect.h b/openconnect.h index 35900fb9..c6217657 100644 --- a/openconnect.h +++ b/openconnect.h @@ -33,9 +33,12 @@ extern "C" { #endif #define OPENCONNECT_API_VERSION_MAJOR 5 -#define OPENCONNECT_API_VERSION_MINOR 3 +#define OPENCONNECT_API_VERSION_MINOR 4 /* + * API version 5.4: + * - Add openconnect_set_pass_tos() + * * API version 5.3 (v7.07; 2016-07-11): * - Add openconnect_set_localname(). * - Add openconnect_override_getaddrinfo(). @@ -612,8 +615,9 @@ typedef void (*openconnect_protect_socket_vfn) (void *privdata, int fd); void openconnect_set_protect_socket_handler(struct openconnect_info *vpninfo, openconnect_protect_socket_vfn protect_socket); -void openconnect_set_loglevel(struct openconnect_info *vpninfo, - int level); +void openconnect_set_loglevel(struct openconnect_info *vpninfo, int level); + +void openconnect_set_pass_tos(struct openconnect_info *vpninfo, int enable); /* Callback for obtaining traffic stats via OC_CMD_STATS. */ diff --git a/ssl.c b/ssl.c index 7384a8ec..ce4af6a1 100644 --- a/ssl.c +++ b/ssl.c @@ -908,9 +908,13 @@ int udp_sockaddr(struct openconnect_info *vpninfo, int port) if (vpninfo->peer_addr->sa_family == AF_INET) { struct sockaddr_in *sin = (void *)vpninfo->dtls_addr; sin->sin_port = htons(port); + vpninfo->dtls_tos_proto = IPPROTO_IP; + vpninfo->dtls_tos_optname = IP_TOS; } else if (vpninfo->peer_addr->sa_family == AF_INET6) { struct sockaddr_in6 *sin = (void *)vpninfo->dtls_addr; sin->sin6_port = htons(port); + vpninfo->dtls_tos_proto = IPPROTO_IPV6; + vpninfo->dtls_tos_optname = IPV6_TCLASS; } else { vpn_progress(vpninfo, PRG_ERR, _("Unknown protocol family %d. Cannot create UDP server address\n"), @@ -918,6 +922,11 @@ int udp_sockaddr(struct openconnect_info *vpninfo, int port) return -EINVAL; } + /* in case DTLS TOS copy is disabled, reset the optname value */ + /* so that the copy won't be applied in dtls.c / dtls_mainloop() */ + if (!vpninfo->dtls_pass_tos) + vpninfo->dtls_tos_optname = 0; + return 0; } diff --git a/www/changelog.xml b/www/changelog.xml index 35c98f19..eaf9d7cc 100644 --- a/www/changelog.xml +++ b/www/changelog.xml @@ -15,6 +15,7 @@
  • OpenConnect HEAD
      +
    • Add --pass-tos option as in OpenVPN.
    • Support rĂ´le selection form in Juniper VPN.
    • Support DER-format certificates, add certificate format torture tests.
    • For OpenSSL >= 1.0.2, fix certificate validation when only an