This behavior is supported by AnyConnect (CONNECT with X-CSTP-Address header) and by GlobalProtect (POST /ssl-vpn/getconfig.esp with preferred-ip form field). Currently, this option is only a request. OpenConnect will print an error message, but will not abort, if the server assigns a different IPv4 address. I did not implement the corresponding behavior for IPv6 because I do not have a way to test it right now. Signed-off-by: Daniel Lenski <dlenski at gmail.com> --- auth-globalprotect.c | 2 ++ cstp.c | 23 +++++++++++++++++------ gpst.c | 19 ++++++++++++++----- main.c | 6 ++++++ openconnect.8.in | 6 ++++++ 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/auth-globalprotect.c b/auth-globalprotect.c index 1449d8c..dd3cb0d 100644 --- a/auth-globalprotect.c +++ b/auth-globalprotect.c @@ -295,6 +295,8 @@ static int gpst_login(struct openconnect_info *vpninfo, int portal) buf_append(request_body, "jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:"); append_opt(request_body, "server", vpninfo->hostname); append_opt(request_body, "computer", vpninfo->localname); + if (vpninfo->ip_info.addr) + append_opt(request_body, "preferred-ip", vpninfo->ip_info.addr); if (form->auth_id && form->auth_id[0]!='_') append_opt(request_body, "inputStr", form->auth_id); append_form_opts(vpninfo, form, request_body); diff --git a/cstp.c b/cstp.c index a22c66e..3c35d05 100644 --- a/cstp.c +++ b/cstp.c @@ -262,7 +262,9 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) buf_append(reqbuf, "X-CSTP-MTU: %d\r\n", mtu); buf_append(reqbuf, "X-CSTP-Address-Type: %s\r\n", vpninfo->disable_ipv6 ? "IPv4" : "IPv6,IPv4"); - /* Explicitly request the same IPv4 address on reconnect */ + /* Explicitly request the same IPv4 address on reconnect (or on + * initial connection if specified with the --request-ip option) + */ if (old_addr) buf_append(reqbuf, "X-CSTP-Address: %s\r\n", old_addr); if (!vpninfo->disable_ipv6) @@ -580,11 +582,20 @@ static int start_cstp_connection(struct openconnect_info *vpninfo) mtu); } if (old_addr) { - if (strcmp(old_addr, vpninfo->ip_info.addr)) { - vpn_progress(vpninfo, PRG_ERR, - _("Reconnect gave different Legacy IP address (%s != %s)\n"), - vpninfo->ip_info.addr, old_addr); - return -EINVAL; + /* XXX: if --request-ip option is used, we'll have old_addr!=NULL even on the + first connection attempt, but if old_netmask is also non-NULL then we know + it's a reconnect. */ + if (vpninfo->ip_info.addr==NULL || strcmp(old_addr, vpninfo->ip_info.addr)) { + if (!old_netmask) + vpn_progress(vpninfo, PRG_ERR, + _("Legacy IP address %s was requested, but server provided %s\n"), + old_addr, vpninfo->ip_info.addr); + else { + vpn_progress(vpninfo, PRG_ERR, + _("Reconnect gave different Legacy IP address (%s != %s)\n"), + vpninfo->ip_info.addr, old_addr); + return -EINVAL; + } } } if (old_netmask) { diff --git a/gpst.c b/gpst.c index 9b8b3c6..3665261 100644 --- a/gpst.c +++ b/gpst.c @@ -532,12 +532,21 @@ static int gpst_get_config(struct openconnect_info *vpninfo) goto out; } if (old_addr) { + /* XXX: if --request-ip option is used, we'll have old_addr!=NULL even on the + first connection attempt, but if old_netmask is also non-NULL then we know + it's a reconnect. */ if (strcmp(old_addr, vpninfo->ip_info.addr)) { - vpn_progress(vpninfo, PRG_ERR, - _("Reconnect gave different Legacy IP address (%s != %s)\n"), - vpninfo->ip_info.addr, old_addr); - result = -EINVAL; - goto out; + if (!old_netmask) + vpn_progress(vpninfo, PRG_ERR, + _("Legacy IP address %s was requested, but server provided %s\n"), + old_addr, vpninfo->ip_info.addr); + else { + vpn_progress(vpninfo, PRG_ERR, + _("Reconnect gave different Legacy IP address (%s != %s)\n"), + vpninfo->ip_info.addr, old_addr); + result = -EINVAL; + goto out; + } } } if (old_netmask) { diff --git a/main.c b/main.c index 815c220..b2fb10a 100644 --- a/main.c +++ b/main.c @@ -188,6 +188,7 @@ enum { OPT_LOCAL_HOSTNAME, OPT_PROTOCOL, OPT_PASSTOS, + OPT_REQUEST_IP, }; #ifdef __sun__ @@ -269,6 +270,7 @@ static const struct option long_options[] = { OPTION("dump-http-traffic", 0, OPT_DUMP_HTTP), OPTION("no-system-trust", 0, OPT_NO_SYSTEM_TRUST), OPTION("protocol", 1, OPT_PROTOCOL), + OPTION("request-ip", 1, OPT_REQUEST_IP), #ifdef OPENCONNECT_GNUTLS OPTION("gnutls-debug", 1, OPT_GNUTLS_DEBUG), #endif @@ -860,6 +862,7 @@ static void usage(void) printf(" --resolve=HOST:IP %s\n", _("Use IP when connecting to HOST")); printf(" --os=STRING %s\n", _("OS type (linux,linux-64,win,...) to report")); printf(" --dtls-local-port=PORT %s\n", _("Set local port for DTLS datagrams")); + printf(" --request-ip=IP %s\n", _("Request a specific IPv4 address")); print_supported_protocols_usage(); printf("\n"); @@ -1270,6 +1273,9 @@ int main(int argc, char **argv) case OPT_AUTHGROUP: authgroup = keep_config_arg(); break; + case OPT_REQUEST_IP: + vpninfo->ip_info.addr = keep_config_arg(); + break; case 'C': vpninfo->cookie = dup_config_arg(); break; diff --git a/openconnect.8.in b/openconnect.8.in index 9f46b30..65b26f3 100644 --- a/openconnect.8.in +++ b/openconnect.8.in @@ -66,6 +66,7 @@ openconnect \- Multi-protocol VPN client, for Cisco AnyConnect VPNs and others .OP \-\-useragent string .OP \-\-local-hostname string .OP \-\-os string +.OP \-\-request-ip ip .B [https://]\fIserver\fB[:\fIport\fB][/\fIgroup\fB] .YS @@ -523,6 +524,11 @@ applied to the VPN session. If the gateway requires CSD, it will also cause the corresponding CSD trojan binary to be downloaded, so you may need to use .B \-\-csd\-wrapper if this code is not executable on the local machine. +.TP +.B \-\-request-ip=IP +Request a specific IPv4 address from the gateway. Currently, OpenConnect +will print a warning but will not abort if the gateway provides a different +IPv4 address. .SH SIGNALS In the data phase of the connection, the following signals are handled: .TP -- 2.7.4