]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Add check_address_conflicts() to tun-win32.c
authorDaniel Lenski <dlenski@gmail.com>
Fri, 2 Apr 2021 05:57:02 +0000 (22:57 -0700)
committerDaniel Lenski <dlenski@gmail.com>
Fri, 10 Sep 2021 18:27:34 +0000 (11:27 -0700)
If other adapters have conflicting IPv4/IPv6 addresses, vpnc-script-win.js
will fail to configure the tunnel addresses correctly.

For now, check_address_conflicts() only checks and fails; it doesn't try to
delete/reclaim the conflicting addresses.

[ Yay, Windows, for persisting a value we don't need or want persisted, and
  not allowing us to override this with 'netsh interface ipvX set address'.
  This appears to be a limitation of the Windows 'netsh' configuration
  utility. ]

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
tun-win32.c

index 0258bad9fc12bdc3b1384a1cd3fb838695397a8e..7525e5ab7543b564e6668e00da1bb36713378c9f 100644 (file)
@@ -22,6 +22,8 @@
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
 #include <winioctl.h>
+/* must precede iphlpapi.h, per https://docs.microsoft.com/en-us/windows/win32/api/iptypes/ns-iptypes-ip_adapter_addresses_lh */
+#include <winsock2.h>
 #include <iphlpapi.h>
 
 #include <errno.h>
@@ -221,6 +223,92 @@ static int get_adapter_index(struct openconnect_info *vpninfo, char *guid)
        return ret;
 }
 
+static int check_address_conflicts(struct openconnect_info *vpninfo)
+{
+       ULONG bufSize = 15000;
+       PIP_ADAPTER_ADDRESSES addresses = NULL;
+       DWORD LastError;
+       int ret = 0;
+
+       struct in_addr our_ipv4_addr;
+       struct in6_addr our_ipv6_addr;
+       if (vpninfo->ip_info.addr && !inet_aton(vpninfo->ip_info.addr, &our_ipv4_addr))
+               return -EINVAL;
+       if (vpninfo->ip_info.addr6 && !inet_pton(AF_INET6, vpninfo->ip_info.addr6, &our_ipv6_addr))
+               return -EINVAL;
+
+       /* XX: repeat twice, because it may tell us we need a bigger buffer */
+       for (int tries=0; tries<2; tries++) {
+               free(addresses);
+               addresses = malloc(bufSize);
+               if (!addresses)
+                       return -ENOMEM;
+
+               /* AF_UNSPEC means both IPv4 and IPv6 */
+               LastError = GetAdaptersAddresses(AF_UNSPEC,
+                                                GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST,
+                                                NULL, addresses, &bufSize);
+               if (LastError != ERROR_BUFFER_OVERFLOW)
+                       break;
+       }
+       if (LastError != ERROR_SUCCESS) {
+               char *errstr = openconnect__win32_strerror(LastError);
+               vpn_progress(vpninfo, PRG_ERR, _("GetAdaptersAddresses() failed: %s\n"), errstr);
+               ret = -EIO;
+               goto out;
+       }
+
+       for (; addresses; addresses=addresses->Next) {
+               /* XX: skip "our own" adapter */
+               if (addresses->IfIndex == vpninfo->tun_idx)
+                       continue;
+
+               for (PIP_ADAPTER_UNICAST_ADDRESS ua = addresses->FirstUnicastAddress; ua; ua=ua->Next) {
+                       struct sockaddr *a = ua->Address.lpSockaddr;
+                       union {
+                               struct sockaddr_in s4;
+                               struct sockaddr_in6 s6;
+                       } *sa = (void *)a;
+                       int needs_reclaim = 0;
+
+                       if (a->sa_family == AF_INET) {
+                               if (vpninfo->ip_info.addr && our_ipv4_addr.s_addr == sa->s4.sin_addr.s_addr)
+                                       needs_reclaim = 1;
+                       } else if (a->sa_family == AF_INET6) {
+                               if (vpninfo->ip_info.addr6 && !memcmp(&our_ipv6_addr, &sa->s6.sin6_addr, sizeof(sa->s6.sin6_addr)))
+                                       needs_reclaim = 1;
+                       }
+
+                       if (needs_reclaim) {
+                               if (addresses->OperStatus == IfOperStatusUp) {
+                                       /* XX: Interface is up. We shouldn't steal its address */
+                                       vpn_progress(vpninfo, PRG_ERR,
+                                                    _("Adapter \"%S\" / %ld is UP and using our IPv%d address. Cannot resolve.\n"),
+                                                    addresses->FriendlyName, addresses->IfIndex, a->sa_family == AF_INET ? 4 : 6);
+                                       ret = -ENOENT;
+                                       goto out;
+                               } else {
+                                       /* XX: Interface is down */
+                                       vpn_progress(vpninfo, PRG_ERR,
+                                                    _("Adapter \"%S\" / %ld is DOWN and using our IPv%d address. We will reclaim the address from it.\n"),
+                                                    addresses->FriendlyName, addresses->IfIndex, a->sa_family == AF_INET ? 4 : 6);
+
+                                       /* FIXME: we SHOULD be able to delete this address, but
+                                        *     DeleteUnicastIpAddressEntry requires a table we don't have right now (maybe make a "fake" row?)
+                                        * and DeleteIPAddress is IPv4 only, and requires a magic number we don't have
+                                        */
+                                       ret = -ENOENT;
+                                       goto out;
+                               }
+                       }
+               }
+       }
+
+ out:
+       free(addresses);
+       return ret;
+}
+
 static intptr_t open_tuntap(struct openconnect_info *vpninfo, char *guid, wchar_t *wname)
 {
        char devname[80];
@@ -400,6 +488,9 @@ intptr_t os_setup_tun(struct openconnect_info *vpninfo)
                ret = OPEN_TUN_HARDFAIL;
        }
 
+       if (check_address_conflicts(vpninfo) < 0)
+               ret = OPEN_TUN_HARDFAIL; /* already complained about it */
+
        return ret;
 }