Simplify tun configuration

This commit is contained in:
Ludvig Strigeus 2018-10-23 00:48:20 +02:00
parent 335133fa8f
commit a7776ab7c4
10 changed files with 359 additions and 347 deletions

View file

@ -37,18 +37,6 @@ void *IpToPeerMap::LookupV4(uint32 ip) {
return ipv4_.Lookup(ip); return ipv4_.Lookup(ip);
} }
void *IpToPeerMap::LookupV4DefaultPeer() {
return ipv4_.LookupExact(0, 0);
}
void *IpToPeerMap::LookupV6DefaultPeer() {
for (auto it = ipv6_.begin(); it != ipv6_.end(); ++it) {
if (it->cidr_len == 0)
return it->peer;
}
return NULL;
}
void IpToPeerMap::RemoveV4(uint32 ip, int cidr) { void IpToPeerMap::RemoveV4(uint32 ip, int cidr) {
ipv4_.Delete(ip, cidr); ipv4_.Delete(ip, cidr);
} }

View file

@ -50,9 +50,6 @@ public:
void *LookupV4(uint32 ip); void *LookupV4(uint32 ip);
void *LookupV6(const void *addr); void *LookupV6(const void *addr);
void *LookupV4DefaultPeer();
void *LookupV6DefaultPeer();
void RemoveV4(uint32 ip, int cidr); void RemoveV4(uint32 ip, int cidr);
void RemoveV6(const void *addr, int cidr); void RemoveV6(const void *addr, int cidr);
private: private:

View file

@ -63,43 +63,26 @@ public:
}; };
struct TunConfig { struct TunConfig {
// IP address and netmask of the tun device
in_addr_t ip;
uint8 cidr;
bool block_dns_on_adapters;
// no, yes(firewall), yes(route), yes(both), 255(default) // no, yes(firewall), yes(route), yes(both), 255(default)
uint8 internet_blocking; uint8 internet_blocking;
// Set this to configure a default route for ipv4 bool block_dns_on_adapters;
bool use_ipv4_default_route;
// Set this to configure a default route for ipv6
bool use_ipv6_default_route;
// This holds the address of the vpn endpoint, so those get routed to the old iface.
uint32 default_route_endpoint_v4;
// Set mtu // Set mtu
int mtu; int mtu;
// Set ipv6 address? // The ipv6 and ipv4 addresses
uint8 ipv6_address[16]; std::vector<WgCidrAddr> addresses;
uint8 ipv6_cidr;
// Set this to configure DNS server for ipv4,ipv6 // Set this to configure DNS server
std::vector<IpAddr> ipv4_dns; std::vector<IpAddr> dns;
std::vector<IpAddr> ipv6_dns;
// This holds the address of the vpn endpoint, so those get routed to the old iface.
uint8 default_route_endpoint_v6[16];
// This holds all cidr addresses to add as additional routing entries // This holds all cidr addresses to add as additional routing entries
std::vector<WgCidrAddr> extra_routes; std::vector<WgCidrAddr> included_routes;
// This holds all the ips to exclude // This holds all the ips to exclude
std::vector<WgCidrAddr> excluded_ips; std::vector<WgCidrAddr> excluded_routes;
// This holds the pre/post commands // This holds the pre/post commands
PrePostCommands pre_post_commands; PrePostCommands pre_post_commands;

View file

@ -372,29 +372,6 @@ static uint32 CidrToNetmaskV4(int cidr) {
return cidr == 32 ? 0xffffffff : 0xffffffff << (32 - cidr); return cidr == 32 ? 0xffffffff : 0xffffffff << (32 - cidr);
} }
static uint32 ComputeIpv4DefaultRoute(uint32 ip, uint32 netmask) {
uint32 default_route_v4 = (ip & netmask) | 1;
if (default_route_v4 == ip)
default_route_v4++;
return default_route_v4;
}
static void ComputeIpv6DefaultRoute(const uint8 *ipv6_address, uint8 ipv6_cidr, uint8 *default_route_v6) {
memcpy(default_route_v6, ipv6_address, 16);
// clear the last bits of the ipv6 address to match the cidr.
size_t n = (ipv6_cidr + 7) >> 3;
memset(&default_route_v6[n], 0, 16 - n);
if (n == 0)
return;
// adjust the final byte
default_route_v6[n - 1] &= ~(0xff >> (ipv6_cidr & 7));
// set the very last byte to something
default_route_v6[15] |= 1;
// ensure it doesn't collide
if (memcmp(default_route_v6, ipv6_address, 16) == 0)
default_route_v6[15] ^= 3;
}
void TunsafeBackendBsd::AddRoute(uint32 ip, uint32 cidr, uint32 gw, const char *dev) { void TunsafeBackendBsd::AddRoute(uint32 ip, uint32 cidr, uint32 gw, const char *dev) {
uint32 ip_be, gw_be; uint32 ip_be, gw_be;
WriteBE32(&ip_be, ip); WriteBE32(&ip_be, ip);
@ -411,7 +388,7 @@ static void AddOrRemoveRoute(const RouteInfo &cd, bool remove) {
#if defined(OS_LINUX) #if defined(OS_LINUX)
const char *cmd = remove ? "del" : "add"; const char *cmd = remove ? "del" : "add";
const char *proto = (cd.family == AF_INET) ? NULL : "-6"; const char *proto = (cd.family == AF_INET) ? NULL : "-6";
if (cd.dev.empty()) { if (cd.dev.empty()) {
RunCommand("/sbin/ip %s route %s %s via %s", proto, cmd, buf1, buf2); RunCommand("/sbin/ip %s route %s %s via %s", proto, cmd, buf1, buf2);
} else { } else {
RunCommand("/sbin/ip %s route %s %s dev %s", proto, cmd, buf1, cd.dev.c_str()); RunCommand("/sbin/ip %s route %s %s dev %s", proto, cmd, buf1, cd.dev.c_str());
@ -451,6 +428,7 @@ static bool IsIpv6AddressSet(const void *p) {
// Called to initialize tun // Called to initialize tun
bool TunsafeBackendBsd::Configure(const TunConfig &&config, TunConfigOut *out) override { bool TunsafeBackendBsd::Configure(const TunConfig &&config, TunConfigOut *out) override {
char buf[kSizeOfAddress]; char buf[kSizeOfAddress];
char buf2[kSizeOfAddress];
if (!RunPrePostCommand(config.pre_post_commands.pre_up)) { if (!RunPrePostCommand(config.pre_post_commands.pre_up)) {
RERROR("Pre command failed!"); RERROR("Pre command failed!");
@ -461,72 +439,74 @@ bool TunsafeBackendBsd::Configure(const TunConfig &&config, TunConfigOut *out) o
if (!InitializeTun(devname_)) if (!InitializeTun(devname_))
return false; return false;
uint32 netmask = CidrToNetmaskV4(config.cidr);
uint32 default_route_v4 = ComputeIpv4DefaultRoute(config.ip, netmask);
const WgCidrAddr *ipv4_addr = NULL;
const WgCidrAddr *ipv6_addr = NULL;
for (auto it = config.addresses.begin(); it != config.addresses.end(); ++it) {
if (it->size == 32 && ipv4_addr == NULL)
ipv4_addr = &*it;
else if (it->size == 128 && ipv6_addr == NULL)
ipv6_addr = &*it;
}
if (ipv4_addr == NULL) {
RERROR("The TUN adapter requires an IPv4 address");
return false;
}
uint32 ipv4_netmask = CidrToNetmaskV4(ipv4_addr->cidr);
uint32 ipv4_ip = ReadBE32(ipv4_addr->addr);
addresses_to_remove_ = config.addresses;
#if defined(OS_LINUX) #if defined(OS_LINUX)
if (config.ip) { RunCommand("/sbin/ip address flush dev %s scope global", devname_);
char ip[4]; for(const WgCidrAddr &a : config.addresses)
WriteBE32(ip, config.ip); RunCommand("/sbin/ip address add dev %s %s", devname_, print_ip_prefix(buf, a.size == 32 ? AF_INET : AF_INET6, a.addr, a.cidr));
RunCommand("/sbin/ip address add dev %s %s", devname_, print_ip_prefix(buf, AF_INET, ip, config.cidr));
}
if (config.ipv6_cidr) {
RunCommand("/sbin/ip address add dev %s %s", devname_, print_ip_prefix(buf, AF_INET6, config.ipv6_address, config.ipv6_cidr));
}
RunCommand("/sbin/ip link set dev %s mtu %d up", devname_, config.mtu); RunCommand("/sbin/ip link set dev %s mtu %d up", devname_, config.mtu);
#else // !defined(OS_LINUX) #else
if (config.ip) { for(const WgCidrAddr &a : config.addresses) {
RunCommand("/sbin/ifconfig %s %A mtu %d %A netmask %A up", devname_, config.ip, config.mtu, config.ip, netmask); if (a.size == 32) {
RunCommand("/sbin/ifconfig %s inet %s %s add", devname_, print_ip_prefix(buf, AF_INET, a.addr, a.cidr), print_ip_prefix(buf2, AF_INET, a.addr, -1));
} else {
RunCommand("/sbin/ifconfig %s inet6 %s add", devname_, print_ip_prefix(buf, AF_INET6, a.addr, a.cidr));
}
} }
if (config.ipv6_cidr) { RunCommand("/sbin/ifconfig %s mtu %d up", devname_, config.mtu);
RunCommand("/sbin/ifconfig %s inet6 add %s", devname_, print_ip_prefix(buf, AF_INET6, config.ipv6_address, config.ipv6_cidr)); #endif
}
#endif // !defined(OS_LINUX)
if (config.ip) {
AddRoute(config.ip & netmask, config.cidr, config.ip, devname_);
}
if (config.use_ipv4_default_route) { char default_iface[16];
if (config.default_route_endpoint_v4) { uint32 ipv4_default_gw;
uint32 ipv4_default_gw; bool found_ipv4_route = GetDefaultRoute(default_iface, sizeof(default_iface), &ipv4_default_gw);
char default_iface[16]; for (auto it = config.excluded_routes.begin(); it != config.excluded_routes.end(); ++it) {
if (!GetDefaultRoute(default_iface, sizeof(default_iface), &ipv4_default_gw)) { if (it->size == 32) {
if (!found_ipv4_route) {
RERROR("Unable to determine default interface."); RERROR("Unable to determine default interface.");
return false; return false;
} }
AddRoute(config.default_route_endpoint_v4, 32, ipv4_default_gw, NULL); AddRoute(ReadBE32(it->addr), it->cidr, ipv4_default_gw, NULL);
for (auto it = config.excluded_ips.begin(); it != config.excluded_ips.end(); ++it) { } else if (it->size == 128) {
if (it->size == 32) RERROR("default_route_endpoint_v6 not supported");
AddRoute(ReadBE32(it->addr), it->cidr, ipv4_default_gw, default_iface); return false;
}
}
AddRoute(0x00000000, 1, default_route_v4, devname_);
AddRoute(0x80000000, 1, default_route_v4, devname_);
}
uint8 default_route_v6[16];
if (config.ipv6_cidr) {
static const uint8 matchall_1_route[17] = {0x80, 0, 0, 0};
ComputeIpv6DefaultRoute(config.ipv6_address, config.ipv6_cidr, default_route_v6);
if (config.use_ipv6_default_route) {
if (IsIpv6AddressSet(config.default_route_endpoint_v6)) {
RERROR("default_route_endpoint_v6 not supported");
}
AddRoute(AF_INET6, matchall_1_route + 1, 1, default_route_v6, devname_);
AddRoute(AF_INET6, matchall_1_route + 0, 1, default_route_v6, devname_);
} }
} }
// Add all the extra routes // Add all the extra routes
for (auto it = config.extra_routes.begin(); it != config.extra_routes.end(); ++it) { for (auto it = config.included_routes.begin(); it != config.included_routes.end(); ++it) {
if (it->cidr == 0) {
if (it->size == 32) {
AddRoute(0x00000000, 1, ipv4_ip, devname_);
AddRoute(0x80000000, 1, ipv4_ip, devname_);
} else if (it->size == 128 && ipv6_addr) {
static const uint8 matchall_1_route[17] = {0x80, 0, 0, 0};
AddRoute(AF_INET6, matchall_1_route + 1, 1, ipv6_addr->addr, devname_);
AddRoute(AF_INET6, matchall_1_route + 0, 1, ipv6_addr->addr, devname_);
}
continue;
}
if (it->size == 32) { if (it->size == 32) {
AddRoute(ReadBE32(it->addr), it->cidr, default_route_v4, devname_); AddRoute(ReadBE32(it->addr), it->cidr, ipv4_ip, devname_);
} else if (it->size == 128 && config.ipv6_cidr) { } else if (it->size == 128 && ipv6_addr) {
AddRoute(AF_INET6, it->addr, it->cidr, default_route_v6, devname_); AddRoute(AF_INET6, it->addr, it->cidr, ipv6_addr->addr, devname_);
} }
} }
@ -539,13 +519,30 @@ bool TunsafeBackendBsd::Configure(const TunConfig &&config, TunConfigOut *out) o
} }
void TunsafeBackendBsd::CleanupRoutes() { void TunsafeBackendBsd::CleanupRoutes() {
char buf[kSizeOfAddress];
RunPrePostCommand(pre_down_); RunPrePostCommand(pre_down_);
for(auto it = cleanup_commands_.begin(); it != cleanup_commands_.end(); ++it) { for(auto it = cleanup_commands_.begin(); it != cleanup_commands_.end(); ++it) {
if (!tun_interface_gone_ || strcmp(it->dev.c_str(), devname_) != 0) if (!tun_interface_gone_ || strcmp(it->dev.c_str(), devname_) != 0)
DelRoute(*it); DelRoute(*it);
} }
#if defined(OS_LINUX)
for(const WgCidrAddr &a : addresses_to_remove_)
RunCommand("/sbin/ip address del dev %s %s", devname_, print_ip_prefix(buf, a.size == 32 ? AF_INET : AF_INET6, a.addr, a.cidr));
#else
for(const WgCidrAddr &a : addresses_to_remove_) {
if (a.size == 32) {
RunCommand("/sbin/ifconfig %s inet %s -alias", devname_, print_ip_prefix(buf, AF_INET, a.addr, -1));
} else {
RunCommand("/sbin/ifconfig %s inet6 %s -alias", devname_, print_ip_prefix(buf, AF_INET6, a.addr, -1));
}
}
#endif
cleanup_commands_.clear(); cleanup_commands_.clear();
addresses_to_remove_.clear();
RunPrePostCommand(post_down_); RunPrePostCommand(post_down_);
@ -767,9 +764,13 @@ public:
virtual void OnConnected() override { virtual void OnConnected() override {
if (!is_connected_) { if (!is_connected_) {
uint32 ipv4_ip = ReadBE32(wg_processor_->tun_addr().addr); const WgCidrAddr *ipv4_addr = NULL;
for (const WgCidrAddr &x : wg_processor_->addr()) {
if (x.size == 32) { ipv4_addr = &x; break; }
}
uint32 ipv4_ip = ipv4_addr ? ReadBE32(ipv4_addr->addr) : 0;
char buf[kSizeOfAddress]; char buf[kSizeOfAddress];
RINFO("Connection established. IP %s", print_ip(buf, ipv4_ip)); RINFO("Connection established. IP %s", ipv4_ip ? print_ip(buf, ipv4_ip) : "(none)");
is_connected_ = true; is_connected_ = true;
} }
} }
@ -831,3 +832,4 @@ int main(int argc, char **argv) {
return 0; return 0;
} }

View file

@ -80,6 +80,7 @@ protected:
WireguardProcessor *processor_; WireguardProcessor *processor_;
std::vector<RouteInfo> cleanup_commands_; std::vector<RouteInfo> cleanup_commands_;
std::vector<std::string> pre_down_, post_down_; std::vector<std::string> pre_down_, post_down_;
std::vector<WgCidrAddr> addresses_to_remove_;
sigset_t orig_signal_mask_; sigset_t orig_signal_mask_;
char devname_[16]; char devname_[16];
bool tun_interface_gone_; bool tun_interface_gone_;

View file

@ -310,7 +310,7 @@ struct RouteInfo {
uint8 found_null_routes; uint8 found_null_routes;
}; };
static inline bool IsRouteOriginatingFromNullRoute(MIB_IPFORWARD_ROW2 *row) { static bool IsRouteOriginatingFromNullRoute(MIB_IPFORWARD_ROW2 *row) {
if (!(row->InterfaceLuid.Info.IfType == 24 && row->Protocol == MIB_IPPROTO_NETMGMT && row->DestinationPrefix.PrefixLength == 1)) if (!(row->InterfaceLuid.Info.IfType == 24 && row->Protocol == MIB_IPPROTO_NETMGMT && row->DestinationPrefix.PrefixLength == 1))
return false; return false;
if (row->NextHop.si_family == AF_INET) { if (row->NextHop.si_family == AF_INET) {
@ -322,14 +322,25 @@ static inline bool IsRouteOriginatingFromNullRoute(MIB_IPFORWARD_ROW2 *row) {
return false; return false;
} }
static inline bool IsRouteTheAddressOfTheServer(int family, MIB_IPFORWARD_ROW2 *row, uint8 *old_endpoint_to_delete) { static bool IsDestinationRouteEqualTo(MIB_IPFORWARD_ROW2 *row, const WgCidrAddr *addr) {
if (!(row->Protocol == MIB_IPPROTO_NETMGMT && row->DestinationPrefix.Prefix.si_family == family)) if (addr->size == 32) {
return row->DestinationPrefix.Prefix.si_family == AF_INET &&
row->DestinationPrefix.PrefixLength == addr->cidr &&
memcmp(&row->DestinationPrefix.Prefix.Ipv4.sin_addr, addr->addr, 4) == 0;
} else if (addr->size == 128) {
return row->DestinationPrefix.Prefix.si_family == AF_INET6 &&
row->DestinationPrefix.PrefixLength == addr->cidr &&
memcmp(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr, addr->addr, 16) == 0;
} else {
return false; return false;
if (family == AF_INET) {
return (row->DestinationPrefix.PrefixLength == 32 && memcmp(&row->DestinationPrefix.Prefix.Ipv4.sin_addr, old_endpoint_to_delete, 4) == 0);
} else if (family == AF_INET6) {
return (row->DestinationPrefix.PrefixLength == 128 && memcmp(&row->DestinationPrefix.Prefix.Ipv6.sin6_addr, old_endpoint_to_delete, 16) == 0);
} }
}
static bool IsDestinationRouteEqualToAny(MIB_IPFORWARD_ROW2 *row, const std::vector<WgCidrAddr> &addr) {
for (const WgCidrAddr &x : addr)
if (IsDestinationRouteEqualTo(row, &x))
return true;
return false; return false;
} }
@ -343,17 +354,18 @@ static void DeleteRouteOrPrintErr(MIB_IPFORWARD_ROW2 *row) {
(void*)&row->DestinationPrefix.Prefix.Ipv6.sin6_addr, row->DestinationPrefix.PrefixLength)); (void*)&row->DestinationPrefix.Prefix.Ipv6.sin6_addr, row->DestinationPrefix.PrefixLength));
} }
static bool GetDefaultRouteAndDeleteOldRoutes(int family, const NET_LUID *InterfaceLuid, bool keep_null_routes, uint8 *old_endpoint_to_delete, RouteInfo *ri) { static bool GetDefaultRouteAndDeleteOldRoutes(int family, const NET_LUID *InterfaceLuid, bool keep_null_routes, const std::vector<WgCidrAddr> *old_endpoint_to_delete, RouteInfo *ri) {
MIB_IPFORWARD_TABLE2 *table = NULL; MIB_IPFORWARD_TABLE2 *table = NULL;
assert(family == AF_INET || family == AF_INET6); assert(family == AF_INET || family == AF_INET6);
ri->found_default_adapter = false;
ri->found_null_routes = 0;
if (GetIpForwardTable2(family, &table)) if (GetIpForwardTable2(family, &table))
return false; return false;
DWORD rv = 0; DWORD rv = 0;
DWORD gw_metric = 0xffffffff; DWORD gw_metric = 0xffffffff;
ri->found_default_adapter = false;
ri->found_null_routes = 0;
for (unsigned i = 0; i < table->NumEntries; i++) { for (unsigned i = 0; i < table->NumEntries; i++) {
MIB_IPFORWARD_ROW2 *row = &table->Table[i]; MIB_IPFORWARD_ROW2 *row = &table->Table[i];
if (InterfaceLuid && memcmp(&row->InterfaceLuid, InterfaceLuid, sizeof(NET_LUID)) == 0) { if (InterfaceLuid && memcmp(&row->InterfaceLuid, InterfaceLuid, sizeof(NET_LUID)) == 0) {
@ -378,8 +390,8 @@ static bool GetDefaultRouteAndDeleteOldRoutes(int family, const NET_LUID *Interf
if (old_endpoint_to_delete && ri->found_default_adapter) { if (old_endpoint_to_delete && ri->found_default_adapter) {
for (unsigned i = 0; i < table->NumEntries; i++) { for (unsigned i = 0; i < table->NumEntries; i++) {
MIB_IPFORWARD_ROW2 *row = &table->Table[i]; MIB_IPFORWARD_ROW2 *row = &table->Table[i];
if (memcmp(&row->InterfaceLuid, &ri->default_adapter, sizeof(NET_LUID)) == 0) { if (row->Protocol == MIB_IPPROTO_NETMGMT && memcmp(&row->InterfaceLuid, &ri->default_adapter, sizeof(NET_LUID)) == 0) {
if (IsRouteTheAddressOfTheServer(family, row, old_endpoint_to_delete)) if (IsDestinationRouteEqualToAny(row, *old_endpoint_to_delete))
DeleteRouteOrPrintErr(row); DeleteRouteOrPrintErr(row);
} }
} }
@ -1008,31 +1020,45 @@ static void AssignIpv6Address(const void *new_address, int new_cidr, WgCidrAddr
memcpy(target->addr, new_address, 16); memcpy(target->addr, new_address, 16);
} }
static int IsIpv6AddressInList(const std::vector<WgCidrAddr> &addresses, const void *ipv6_addr, int cidr) {
int i = 0;
for (auto it = addresses.begin(); it != addresses.end(); ++it, i++) {
if (it->size == 128 && it->cidr == cidr && memcmp(it->addr, ipv6_addr, 16) == 0)
return i;
}
return -1;
}
// Set new_cidr to 0 to clear it. // Set new_cidr to 0 to clear it.
static bool SetIPV6AddressOnInterface(NET_LUID *InterfaceLuid, const uint8 new_address[16], int new_cidr, WgCidrAddr *old_address) { static bool SetIPV6AddressOnInterface(NET_LUID *InterfaceLuid, const std::vector<WgCidrAddr> &addresses, std::vector<WgCidrAddr> *old_address) {
NETIO_STATUS Status; NETIO_STATUS Status;
PMIB_UNICASTIPADDRESS_TABLE table = NULL; PMIB_UNICASTIPADDRESS_TABLE table = NULL;
if (old_address) if (old_address)
memset(old_address, 0, sizeof(WgCidrAddr)); old_address->clear();
Status = GetUnicastIpAddressTable(AF_INET6, &table); Status = GetUnicastIpAddressTable(AF_INET6, &table);
if (Status != 0) { if (Status != 0) {
RERROR("GetUnicastAddressTable Failed. Error %d\n", Status); RERROR("GetUnicastAddressTable Failed. Error %d\n", Status);
return false; return false;
} }
uint64 matching_addr = 0;
bool found_row = false;
for (int i = 0; i < (int)table->NumEntries; i++) { for (int i = 0; i < (int)table->NumEntries; i++) {
MIB_UNICASTIPADDRESS_ROW *row = &table->Table[i]; MIB_UNICASTIPADDRESS_ROW *row = &table->Table[i];
if (!memcmp(&row->InterfaceLuid, InterfaceLuid, sizeof(NET_LUID))) { if (!memcmp(&row->InterfaceLuid, InterfaceLuid, sizeof(NET_LUID))) {
if (row->PrefixOrigin == 1 && row->SuffixOrigin == 1) { if (row->PrefixOrigin == 1 && row->SuffixOrigin == 1) {
if (row->OnLinkPrefixLength == new_cidr && !memcmp(&row->Address.Ipv6.sin6_addr, new_address, 16)) { if (old_address) {
found_row = true; WgCidrAddr tmp;
AssignIpv6Address(&row->Address.Ipv6.sin6_addr, row->OnLinkPrefixLength, &tmp);
old_address->push_back(tmp);
}
int idx = IsIpv6AddressInList(addresses, &row->Address.Ipv6.sin6_addr, row->OnLinkPrefixLength);
if (idx >= 0 && idx < 64) {
matching_addr |= (uint64)1 << idx;
RINFO("Using IPv6 address: %s/%d", PrintIPV6((uint8*)&row->Address.Ipv6.sin6_addr), row->OnLinkPrefixLength);
continue; continue;
} }
if (old_address != NULL)
AssignIpv6Address(&row->Address.Ipv6.sin6_addr, row->OnLinkPrefixLength, old_address);
Status = DeleteUnicastIpAddressEntry(row); Status = DeleteUnicastIpAddressEntry(row);
if (Status) if (Status)
RERROR("Error %d deleting IPv6 address: %s/%d", Status, PrintIPV6((uint8*)&row->Address.Ipv6.sin6_addr), row->OnLinkPrefixLength); RERROR("Error %d deleting IPv6 address: %s/%d", Status, PrintIPV6((uint8*)&row->Address.Ipv6.sin6_addr), row->OnLinkPrefixLength);
@ -1043,43 +1069,50 @@ static bool SetIPV6AddressOnInterface(NET_LUID *InterfaceLuid, const uint8 new_a
} }
FreeMibTable(table); FreeMibTable(table);
if (found_row) { // Add all ipv6 addresses that were not already set.
RINFO("Using IPv6 address: %s/%d", PrintIPV6(new_address), new_cidr); bool success = true;
return true; int i = 0;
for (auto it = addresses.begin(); it != addresses.end(); ++it, i++) {
// skip it because of wrong type or already set?
if (it->size != 128 || i < 64 && (matching_addr & ((uint64)1 << i)))
continue;
MIB_UNICASTIPADDRESS_ROW Row;
InitializeUnicastIpAddressEntry(&Row);
Row.OnLinkPrefixLength = it->cidr;
Row.Address.si_family = AF_INET6;
memcpy(&Row.Address.Ipv6.sin6_addr, it->addr, 16);
Row.InterfaceLuid = *InterfaceLuid;
Status = CreateUnicastIpAddressEntry(&Row);
if (Status != 0) {
RERROR("Error %d setting IPv6 address: %s/%d", Status, PrintIPV6(it->addr), it->cidr);
success = false;
} else {
RINFO("Added IPV6 Address: %s/%d", PrintIPV6(it->addr), it->cidr);
}
} }
return success;
if (!IsIpv6AddressSet(new_address))
return true;
if (old_address != NULL)
old_address->size = 128;
MIB_UNICASTIPADDRESS_ROW Row;
InitializeUnicastIpAddressEntry(&Row);
Row.OnLinkPrefixLength = new_cidr;
Row.Address.si_family = AF_INET6;
memcpy(&Row.Address.Ipv6.sin6_addr, new_address, 16);
Row.InterfaceLuid = *InterfaceLuid;
Status = CreateUnicastIpAddressEntry(&Row);
if (Status != 0) {
RERROR("Error %d setting IPv6 address: %s/%d", Status, PrintIPV6(new_address), new_cidr);
return false;
}
RINFO("Set IPV6 Address to: %s/%d", PrintIPV6(new_address), new_cidr);
return true;
} }
static bool SetIPV6DnsOnInterface(NET_LUID *InterfaceLuid, const IpAddr *new_address, size_t new_address_size) { static bool SetIPV6DnsOnInterface(NET_LUID *InterfaceLuid, const IpAddr *new_address, size_t new_address_size) {
char buf[128]; char buf[128];
char ipv6[128]; char ipv6[128];
bool isfirst = true;
NET_IFINDEX InterfaceIndex; NET_IFINDEX InterfaceIndex;
if (ConvertInterfaceLuidToIndex(InterfaceLuid, &InterfaceIndex)) if (ConvertInterfaceLuidToIndex(InterfaceLuid, &InterfaceIndex))
return false; return false;
if (new_address_size) { if (new_address_size) {
for (size_t i = 0; i < new_address_size; i++) { for (size_t i = 0; i < new_address_size; i++) {
if (new_address[i].sin.sin_family != AF_INET6)
continue;
if (!inet_ntop(AF_INET6, (void*)&new_address[i].sin6.sin6_addr, ipv6, sizeof(ipv6))) if (!inet_ntop(AF_INET6, (void*)&new_address[i].sin6.sin6_addr, ipv6, sizeof(ipv6)))
return false; return false;
snprintf(buf, sizeof(buf), "netsh interface ipv6 %s dns name=%d static %s validate=no", (i == 0) ? "set" : "add", InterfaceIndex, ipv6); if (isfirst) {
isfirst = false;
snprintf(buf, sizeof(buf), "netsh interface ipv6 set dnsservers name=%d static %s validate=no", InterfaceIndex, ipv6);
} else {
snprintf(buf, sizeof(buf), "netsh interface ipv6 add dnsservers name=%d %s validate=no", InterfaceIndex, ipv6);
}
if (!RunNetsh(buf)) if (!RunNetsh(buf))
return false; return false;
} }
@ -1127,7 +1160,6 @@ static bool AddMultipleCatchallRoutes(int inet, int bits, const uint8 *target, c
TunWin32Adapter::TunWin32Adapter(DnsBlocker *dns_blocker, const char guid[ADAPTER_GUID_SIZE]) { TunWin32Adapter::TunWin32Adapter(DnsBlocker *dns_blocker, const char guid[ADAPTER_GUID_SIZE]) {
handle_ = NULL; handle_ = NULL;
dns_blocker_ = dns_blocker; dns_blocker_ = dns_blocker;
old_ipv6_address_.size = 0;
old_ipv6_metric_ = kMetricNone; old_ipv6_metric_ = kMetricNone;
old_ipv4_metric_ = kMetricNone; old_ipv4_metric_ = kMetricNone;
has_dns6_setting_ = false; has_dns6_setting_ = false;
@ -1162,6 +1194,22 @@ bool TunWin32Adapter::OpenAdapter(TunsafeBackendWin32 *backend, DWORD open_flags
return (handle_ != NULL); return (handle_ != NULL);
} }
static inline bool CheckFirstNbitsEquals(const byte *a, const byte *b, size_t n) {
return memcmp(a, b, n >> 3) == 0 && ((n & 7) == 0 || !((a[n >> 3] ^ b[n >> 3]) & (0xff << (8 - (n & 7)))));
}
static bool IsWgCidrAddrSubsetOf(const WgCidrAddr &inner, const WgCidrAddr &outer) {
return inner.size == outer.size && inner.cidr >= outer.cidr &&
CheckFirstNbitsEquals(inner.addr, outer.addr, outer.cidr);
}
static bool IsWgCidrAddrSubsetOfAny(const WgCidrAddr &inner, const std::vector<WgCidrAddr> &addr) {
for (auto &a : addr)
if (IsWgCidrAddrSubsetOf(inner, a))
return true;
return false;
}
bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, TunInterface::TunConfigOut *out) { bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, TunInterface::TunConfigOut *out) {
DWORD len, err; DWORD len, err;
@ -1175,14 +1223,28 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
pre_down_ = std::move(config.pre_post_commands.pre_down); pre_down_ = std::move(config.pre_post_commands.pre_down);
post_down_ = std::move(config.pre_post_commands.post_down); post_down_ = std::move(config.pre_post_commands.post_down);
uint32 netmask = CidrToNetmaskV4(config.cidr); const WgCidrAddr *ipv4_addr = NULL;
const WgCidrAddr *ipv6_addr = NULL;
for (auto it = config.addresses.begin(); it != config.addresses.end(); ++it) {
if (it->size == 32 && ipv4_addr == NULL)
ipv4_addr = &*it;
else if (it->size == 128 && ipv6_addr == NULL)
ipv6_addr = &*it;
}
if (ipv4_addr == NULL) {
RERROR("The TUN adapter on Windows requires an IPv4 address");
return false;
}
uint32 ipv4_netmask = CidrToNetmaskV4(ipv4_addr->cidr);
uint32 ipv4_ip = ReadBE32(ipv4_addr->addr);
// Set TAP-Windows TUN subnet mode // Set TAP-Windows TUN subnet mode
if (1) { if (1) {
uint32 v[3]; uint32 v[3];
v[0] = htonl(config.ip); v[0] = htonl(ipv4_ip);
v[1] = htonl(config.ip & netmask); v[1] = htonl(ipv4_ip & ipv4_netmask);
v[2] = htonl(netmask); v[2] = htonl(ipv4_netmask);
if (!DeviceIoControl(handle_, TAP_IOCTL_CONFIG_TUN, v, sizeof(v), v, sizeof(v), &len, NULL)) { if (!DeviceIoControl(handle_, TAP_IOCTL_CONFIG_TUN, v, sizeof(v), v, sizeof(v), &len, NULL)) {
RERROR("DeviceIoControl(TAP_IOCTL_CONFIG_TUN) failed"); RERROR("DeviceIoControl(TAP_IOCTL_CONFIG_TUN) failed");
return false; return false;
@ -1192,9 +1254,9 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
// Set DHCP IP/netmask // Set DHCP IP/netmask
{ {
uint32 v[4]; uint32 v[4];
v[0] = htonl(config.ip); v[0] = htonl(ipv4_ip);
v[1] = htonl(netmask); v[1] = htonl(ipv4_netmask);
v[2] = htonl((config.ip | ~netmask) - 1); // x.x.x.254 v[2] = htonl((ipv4_ip | ~ipv4_netmask) - 1); // x.x.x.254
v[3] = 31536000; // One year v[3] = 31536000; // One year
if (!DeviceIoControl(handle_, TAP_IOCTL_CONFIG_DHCP_MASQ, v, sizeof(v), v, sizeof(v), &len, NULL)) { if (!DeviceIoControl(handle_, TAP_IOCTL_CONFIG_DHCP_MASQ, v, sizeof(v), v, sizeof(v), &len, NULL)) {
RERROR("DeviceIoControl(TAP_IOCTL_CONFIG_DHCP_MASQ) failed"); RERROR("DeviceIoControl(TAP_IOCTL_CONFIG_DHCP_MASQ) failed");
@ -1202,21 +1264,28 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
} }
} }
// Set DHCP config string // Extract and set IPv4 DNS servers through DHCP
if (config.ipv4_dns.size()) { {
enum { kMaxDnsServers = 4 }; enum { kMaxDnsServers = 4 };
uint8 dhcp_options[2 + kMaxDnsServers * 4]; // max 4 dns servers uint8 dhcp_options[2 + kMaxDnsServers * 4]; // max 4 dns servers
size_t num_dns = std::min<size_t>(config.ipv4_dns.size(), kMaxDnsServers); uint32 num_dns = 0;
dhcp_options[0] = 6; for (auto it = config.dns.begin(); it != config.dns.end(); ++it) {
dhcp_options[1] = (uint8)(num_dns * 4); if (it->sin.sin_family != AF_INET)
for(size_t i = 0; i < num_dns; i++) continue;
memcpy(&dhcp_options[2 + i * 4], &config.ipv4_dns[i].sin.sin_addr, num_dns * 4); memcpy(&dhcp_options[2 + num_dns * 4], &it->sin.sin_addr, 4);
DWORD dhcp_options_size = (DWORD)(num_dns * 4 + 2); if (++num_dns == kMaxDnsServers)
byte output[10]; break;
if (!DeviceIoControl(handle_, TAP_IOCTL_CONFIG_DHCP_SET_OPT, }
if (num_dns != 0) {
dhcp_options[0] = 6;
dhcp_options[1] = (uint8)(num_dns * 4);
DWORD dhcp_options_size = (DWORD)(num_dns * 4 + 2);
byte output[10];
if (!DeviceIoControl(handle_, TAP_IOCTL_CONFIG_DHCP_SET_OPT,
(void*)dhcp_options, dhcp_options_size, output, sizeof(output), &len, NULL)) { (void*)dhcp_options, dhcp_options_size, output, sizeof(output), &len, NULL)) {
RERROR("DeviceIoControl(TAP_IOCTL_CONFIG_DHCP_SET_OPT) failed"); RERROR("DeviceIoControl(TAP_IOCTL_CONFIG_DHCP_SET_OPT) failed");
return false; return false;
}
} }
} }
@ -1246,7 +1315,7 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
err = SetMtuOnNetworkAdapter(&interface_luid_, AF_INET, config.mtu); err = SetMtuOnNetworkAdapter(&interface_luid_, AF_INET, config.mtu);
if (err) if (err)
RERROR("SetMtuOnNetworkAdapter IPv4 failed: %d", err); RERROR("SetMtuOnNetworkAdapter IPv4 failed: %d", err);
if (config.ipv6_cidr) { if (ipv6_addr) {
err = SetMtuOnNetworkAdapter(&interface_luid_, AF_INET6, config.mtu); err = SetMtuOnNetworkAdapter(&interface_luid_, AF_INET6, config.mtu);
if (err) if (err)
RERROR("SetMtuOnNetworkAdapter IPv6 failed: %d", err); RERROR("SetMtuOnNetworkAdapter IPv6 failed: %d", err);
@ -1254,18 +1323,22 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
} }
has_dns6_setting_ = false; has_dns6_setting_ = false;
if (config.ipv6_cidr) { if (ipv6_addr) {
SetIPV6AddressOnInterface(&interface_luid_, config.ipv6_address, config.ipv6_cidr, &old_ipv6_address_); SetIPV6AddressOnInterface(&interface_luid_, config.addresses, &old_ipv6_address_);
if (config.ipv6_dns.size()) { // Check if we have at least one ipv6 setting
has_dns6_setting_ = true; for (auto it = config.dns.begin(); it != config.dns.end(); ++it) {
if (!SetIPV6DnsOnInterface(&interface_luid_, config.ipv6_dns.data(), config.ipv6_dns.size())) { if (it->sin.sin_family == AF_INET6) {
RERROR("SetIPV6DnsOnInterface: failed"); has_dns6_setting_ = true;
break;
} }
} }
if (has_dns6_setting_ && !SetIPV6DnsOnInterface(&interface_luid_, config.dns.data(), config.dns.size())) {
RERROR("SetIPV6DnsOnInterface: failed");
}
} }
if ((config.ipv4_dns.size() || has_dns6_setting_) && config.block_dns_on_adapters) { if (config.dns.size() && config.block_dns_on_adapters) {
RINFO("Blocking standard DNS on all adapters"); RINFO("Blocking standard DNS on all adapters");
dns_blocker_->BlockDnsExceptOnAdapter(interface_luid_, has_dns6_setting_); dns_blocker_->BlockDnsExceptOnAdapter(interface_luid_, has_dns6_setting_);
@ -1273,7 +1346,7 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
if (err) if (err)
RERROR("SetMetricOnNetworkAdapter IPv4 failed: %d", err); RERROR("SetMetricOnNetworkAdapter IPv4 failed: %d", err);
if (config.ipv6_cidr) { if (ipv6_addr) {
err = SetMetricOnNetworkAdapter(&interface_luid_, AF_INET6, 2, &old_ipv6_metric_); err = SetMetricOnNetworkAdapter(&interface_luid_, AF_INET6, 2, &old_ipv6_metric_);
if (err) if (err)
RERROR("SetMetricOnNetworkAdapter IPv6 failed: %d", err); RERROR("SetMetricOnNetworkAdapter IPv6 failed: %d", err);
@ -1289,25 +1362,17 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
RouteInfo ri, ri6; RouteInfo ri, ri6;
uint32 default_route_endpoint_v4 = ToBE32(config.default_route_endpoint_v4);
// Delete any current /1 default routes and read some stuff from the routing table. // Delete any current /1 default routes and read some stuff from the routing table.
if (!GetDefaultRouteAndDeleteOldRoutes(AF_INET, &interface_luid_, block_all_traffic_route, config.use_ipv4_default_route ? (uint8*)&default_route_endpoint_v4 : NULL, &ri)) { if (!GetDefaultRouteAndDeleteOldRoutes(AF_INET, &interface_luid_, block_all_traffic_route, &config.excluded_routes, &ri)) {
RERROR("Unable to read old default gateway and delete old default routes."); RERROR("Unable to read old default gateway and delete old default routes.");
return false; return false;
} }
if (config.ipv6_cidr) { // Delete any current /1 default routes and read some stuff from the routing table.
// Delete any current /1 default routes and read some stuff from the routing table. if (!GetDefaultRouteAndDeleteOldRoutes(AF_INET6, &interface_luid_, block_all_traffic_route, &config.excluded_routes, &ri6)) {
if (!GetDefaultRouteAndDeleteOldRoutes(AF_INET6, &interface_luid_, block_all_traffic_route, config.use_ipv6_default_route ? (uint8*)config.default_route_endpoint_v6 : NULL, &ri6)) { RERROR("Unable to read old default gateway and delete old default routes for IPv6.");
RERROR("Unable to read old default gateway and delete old default routes for IPv6.");
return false;
}
} }
uint32 default_route_v4 = ComputeIpv4DefaultRoute(config.ip, netmask);
uint8 default_route_v6[16];
if (block_all_traffic_route) { if (block_all_traffic_route) {
RINFO("Blocking all regular Internet traffic using routing rules"); RINFO("Blocking all regular Internet traffic using routing rules");
NET_LUID localhost_luid; NET_LUID localhost_luid;
@ -1317,17 +1382,17 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
g_killswitch_curr |= kBlockInternet_Route; g_killswitch_curr |= kBlockInternet_Route;
uint32 dst[4] = {0}; uint32 dst[4] = {0};
if (!AddMultipleCatchallRoutes(AF_INET, 1, (uint8*)&dst, localhost_luid, NULL)) { if (!AddMultipleCatchallRoutes(AF_INET, 1, (uint8*)&dst, localhost_luid, NULL)) {
RERROR("Unable to add routes for route based blocking."); RERROR("Unable to add routes for route based blocking.");
DeactivateKillSwitch(0); DeactivateKillSwitch(0);
return false; return false;
} }
if (config.ipv6_cidr) {
if (!AddMultipleCatchallRoutes(AF_INET6, 1, (uint8*)&dst, localhost_luid, NULL)) { if (!AddMultipleCatchallRoutes(AF_INET6, 1, (uint8*)&dst, localhost_luid, NULL)) {
RERROR("Unable to add IPv6 routes for route based blocking."); RERROR("Unable to add IPv6 routes for route based blocking.");
DeactivateKillSwitch(0); DeactivateKillSwitch(0);
return false; return false;
}
} }
} }
} }
@ -1335,8 +1400,7 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
if (ibs & kBlockInternet_Firewall) { if (ibs & kBlockInternet_Firewall) {
RINFO("Blocking all regular Internet traffic using firewall rules"); RINFO("Blocking all regular Internet traffic using firewall rules");
g_killswitch_curr |= kBlockInternet_Firewall; g_killswitch_curr |= kBlockInternet_Firewall;
if (!AddKillSwitchFirewall(interface_luid_, true, (ibs & kBlockInternet_AllowLocalNetworks) != 0)) {
if (!AddKillSwitchFirewall(interface_luid_, config.ipv6_cidr != 0, (ibs & kBlockInternet_AllowLocalNetworks) != 0)) {
RERROR("Unable to activate firewall based kill switch"); RERROR("Unable to activate firewall based kill switch");
DeactivateKillSwitch(0); DeactivateKillSwitch(0);
return false; return false;
@ -1345,63 +1409,54 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
DeactivateKillSwitch(ibs); DeactivateKillSwitch(ibs);
// Configure default route? uint8 default_route_v4[4];
if (config.use_ipv4_default_route) { uint8 default_route_v6[16];
// Add a bypass route to the original gateway?
if (config.default_route_endpoint_v4 != 0) { WriteBE32(default_route_v4, ComputeIpv4DefaultRoute(ipv4_ip, ipv4_netmask));
if (!ri.found_default_adapter) { if (ipv6_addr)
RERROR("Unable to read old ipv4 default gateway"); ComputeIpv6DefaultRoute(ipv6_addr->addr, ipv6_addr->cidr, default_route_v6);
return false;
} // Add all the routes that should go through the VPN
if (!AddRoute(AF_INET, &default_route_endpoint_v4, 32, ri.default_gw, &ri.default_adapter, &routes_to_undo_)) { for (auto it = config.included_routes.begin(); it != config.included_routes.end(); ++it) {
RERROR("Unable to add ipv4 gateway bypass route."); if (it->cidr == 0) {
return false; // /0 gets changed to two /1 routes, to avoid overwriting the system's default route
if (it->size == 32) {
if (!AddMultipleCatchallRoutes(AF_INET, block_all_traffic_route ? 2 : 1, default_route_v4, interface_luid_, &routes_to_undo_))
RERROR("Unable to add new default ipv4 route.");
} else if (it->size == 128 && ipv6_addr) {
if (!AddMultipleCatchallRoutes(AF_INET6, block_all_traffic_route ? 2 : 1, default_route_v6, interface_luid_, &routes_to_undo_))
RERROR("Unable to add new default ipv6 route.");
} }
continue;
} }
// Either add 4 routes or 2 routes, depending on if we use route blocking. // Avoid adding a route if it's a subset of the address
uint32 be = ToBE32(default_route_v4); if (IsWgCidrAddrSubsetOfAny(*it, config.addresses))
if (!AddMultipleCatchallRoutes(AF_INET, block_all_traffic_route ? 2 : 1, (uint8*)&be, interface_luid_, &routes_to_undo_)) continue;
RERROR("Unable to add new default ipv4 route.");
}
if (config.ipv6_cidr) {
ComputeIpv6DefaultRoute(config.ipv6_address, config.ipv6_cidr, default_route_v6);
// Configure default route?
if (config.use_ipv6_default_route) {
if (IsIpv6AddressSet(config.default_route_endpoint_v6)) {
if (!ri6.found_default_adapter) {
RERROR("Unable to read old ipv6 default gateway");
return false;
}
if (!AddRoute(AF_INET6, config.default_route_endpoint_v6, 128, ri.default_gw, &ri6.default_adapter, &routes_to_undo_)) {
RERROR("Unable to add ipv6 gateway bypass route.");
return false;
}
}
if (!AddMultipleCatchallRoutes(AF_INET6, block_all_traffic_route ? 2 : 1, default_route_v6, interface_luid_, &routes_to_undo_))
RERROR("Unable to add new default ipv6 route.");
}
}
// Add all the extra routes
for (auto it = config.extra_routes.begin(); it != config.extra_routes.end(); ++it) {
if (it->size == 32) { if (it->size == 32) {
uint32 be = ToBE32(default_route_v4); AddRoute(AF_INET, it->addr, it->cidr, default_route_v4, &interface_luid_, &routes_to_undo_);
AddRoute(AF_INET, it->addr, it->cidr, &be, &interface_luid_, &routes_to_undo_); } else if (it->size == 128 && ipv6_addr) {
} else if (it->size == 128 && config.ipv6_cidr) {
AddRoute(AF_INET6, it->addr, it->cidr, default_route_v6, &interface_luid_, &routes_to_undo_); AddRoute(AF_INET6, it->addr, it->cidr, default_route_v6, &interface_luid_, &routes_to_undo_);
} }
} }
// Add all the routes that should bypass vpn // Add all the routes that should bypass vpn
for (auto it = config.excluded_ips.begin(); it != config.excluded_ips.end(); ++it) { int warned = 0;
for (auto it = config.excluded_routes.begin(); it != config.excluded_routes.end(); ++it) {
if (it->size == 32) { if (it->size == 32) {
if (ri.found_default_adapter) if (ri.found_default_adapter) {
AddRoute(AF_INET, it->addr, it->cidr, ri.default_gw, &ri.default_adapter, &routes_to_undo_); AddRoute(AF_INET, it->addr, it->cidr, ri.default_gw, &ri.default_adapter, &routes_to_undo_);
} else if (it->size == 128 && config.ipv6_cidr) { } else if (!(warned & 1)) {
if (ri6.found_default_adapter) warned |= 1;
RERROR("Unable to read old ipv4 default gateway");
}
} else if (it->size == 128) {
if (ri6.found_default_adapter) {
AddRoute(AF_INET6, it->addr, it->cidr, ri6.default_gw, &ri6.default_adapter, &routes_to_undo_); AddRoute(AF_INET6, it->addr, it->cidr, ri6.default_gw, &ri6.default_adapter, &routes_to_undo_);
} else if (!(warned & 2)) {
warned |= 2;
RERROR("Unable to read old ipv6 default gateway");
}
} }
} }
@ -1414,7 +1469,7 @@ bool TunWin32Adapter::ConfigureAdapter(const TunInterface::TunConfig &&config, T
RERROR("FlushIpNetTable failed: 0x%X", err); RERROR("FlushIpNetTable failed: 0x%X", err);
return false; return false;
} }
if (config.ipv6_cidr) { if (ipv6_addr != NULL) {
if ((err = FlushIpNetTable2(AF_INET6, InterfaceIndex)) != NO_ERROR) { if ((err = FlushIpNetTable2(AF_INET6, InterfaceIndex)) != NO_ERROR) {
RERROR("FlushIpNetTable failed: 0x%X", err); RERROR("FlushIpNetTable failed: 0x%X", err);
return false; return false;
@ -1439,8 +1494,8 @@ void TunWin32Adapter::CloseAdapter(bool is_restart) {
TunAdaptersInUse::GetInstance()->Release(backend_); TunAdaptersInUse::GetInstance()->Release(backend_);
} }
if (old_ipv6_address_.size != 0) if (old_ipv6_address_.size())
SetIPV6AddressOnInterface(&interface_luid_, old_ipv6_address_.addr, old_ipv6_address_.cidr, NULL); SetIPV6AddressOnInterface(&interface_luid_, old_ipv6_address_, NULL);
if (old_ipv4_metric_ != kMetricNone) if (old_ipv4_metric_ != kMetricNone)
SetMetricOnNetworkAdapter(&interface_luid_, AF_INET, old_ipv4_metric_, NULL); SetMetricOnNetworkAdapter(&interface_luid_, AF_INET, old_ipv4_metric_, NULL);
if (old_ipv6_metric_ != kMetricNone) if (old_ipv6_metric_ != kMetricNone)
@ -1449,7 +1504,7 @@ void TunWin32Adapter::CloseAdapter(bool is_restart) {
SetIPV6DnsOnInterface(&interface_luid_, NULL, 0); SetIPV6DnsOnInterface(&interface_luid_, NULL, 0);
old_ipv4_metric_ = old_ipv6_metric_ = -1; old_ipv4_metric_ = old_ipv6_metric_ = -1;
old_ipv6_address_.size = 0; old_ipv6_address_.clear();
has_dns6_setting_ = false; has_dns6_setting_ = false;
for (auto it = routes_to_undo_.begin(); it != routes_to_undo_.end(); ++it) for (auto it = routes_to_undo_.begin(); it != routes_to_undo_.end(); ++it)
@ -2245,10 +2300,17 @@ void TunsafeBackendWin32::SendConfigurationProtocolPacket(uint32 identifier, con
void TunsafeBackendWin32::OnConnected() { void TunsafeBackendWin32::OnConnected() {
if (status_ != TunsafeBackend::kStatusConnected) { if (status_ != TunsafeBackend::kStatusConnected) {
ipv4_ip_ = ReadBE32(wg_processor_->tun_addr().addr); const WgCidrAddr *ipv4_addr = NULL;
for (const WgCidrAddr &x : wg_processor_->addr()) {
if (x.size == 32) {
ipv4_addr = &x;
break;
}
}
ipv4_ip_ = ipv4_addr ? ReadBE32(ipv4_addr->addr) : 0;
if (status_ != TunsafeBackend::kStatusReconnecting) { if (status_ != TunsafeBackend::kStatusReconnecting) {
char buf[kSizeOfAddress]; char buf[kSizeOfAddress];
RINFO("Connection established. IP %s", print_ip_prefix(buf, AF_INET, wg_processor_->tun_addr().addr, -1)); RINFO("Connection established. IP %s", ipv4_addr ? print_ip_prefix(buf, AF_INET, ipv4_addr->addr, -1) : "(none)");
} }
SetStatus(TunsafeBackend::kStatusConnected); SetStatus(TunsafeBackend::kStatusConnected);
} }

View file

@ -118,7 +118,7 @@ private:
int old_ipv4_metric_, old_ipv6_metric_; int old_ipv4_metric_, old_ipv6_metric_;
WgCidrAddr old_ipv6_address_; std::vector<WgCidrAddr> old_ipv6_address_;
NET_LUID interface_luid_; NET_LUID interface_luid_;

View file

@ -23,8 +23,6 @@ enum {
}; };
WireguardProcessor::WireguardProcessor(UdpInterface *udp, TunInterface *tun, ProcessorDelegate *procdel) { WireguardProcessor::WireguardProcessor(UdpInterface *udp, TunInterface *tun, ProcessorDelegate *procdel) {
tun_addr_.size = 0;
tun6_addr_.size = 0;
udp_ = udp; udp_ = udp;
tun_ = tun; tun_ = tun;
procdel_ = procdel; procdel_ = procdel;
@ -54,21 +52,16 @@ void WireguardProcessor::SetListenPort(int listen_port) {
} }
void WireguardProcessor::AddDnsServer(const IpAddr &sin) { void WireguardProcessor::AddDnsServer(const IpAddr &sin) {
std::vector<IpAddr> *target = (sin.sin.sin_family == AF_INET6) ? &dns6_addr_ : &dns_addr_; dns_addr_.push_back(sin);
target->push_back(sin);
} }
bool WireguardProcessor::SetTunAddress(const WgCidrAddr &addr) { bool WireguardProcessor::SetTunAddress(const WgCidrAddr &addr) {
WgCidrAddr *target = (addr.size == 128) ? &tun6_addr_ : &tun_addr_; addresses_.push_back(addr);
if (target->size != 0)
return false;
*target = addr;
return true; return true;
} }
void WireguardProcessor::ClearTunAddress() { void WireguardProcessor::ClearTunAddress() {
tun_addr_.size = 0; addresses_.clear();
tun6_addr_.size = 0;
} }
void WireguardProcessor::AddExcludedIp(const WgCidrAddr &cidr_addr) { void WireguardProcessor::AddExcludedIp(const WgCidrAddr &cidr_addr) {
@ -118,19 +111,32 @@ void WireguardProcessor::SetupCompressionHeader(WgPacketCompressionVer01 *c) {
c->ttl = 64; c->ttl = 64;
#endif // defined(OS_WIN) #endif // defined(OS_WIN)
WriteLE16(&c->version, EXT_PACKET_COMPRESSION_VER); WriteLE16(&c->version, EXT_PACKET_COMPRESSION_VER);
memcpy(c->ipv4_addr, &tun_addr_.addr, 4);
if (tun6_addr_.size == 128) for (auto it = addresses_.begin(); it != addresses_.end(); ++it) {
memcpy(c->ipv6_addr, &tun6_addr_.addr, 16); if (it->size == 32) {
c->flags = ((tun_addr_.cidr >> 3) & 3); memcpy(c->ipv4_addr, it->addr, 4);
c->flags = ((it->cidr >> 3) & 3);
break;
}
}
for (auto it = addresses_.begin(); it != addresses_.end(); ++it) {
if (it->size == 128) {
memcpy(c->ipv6_addr, it->addr, 16);
break;
}
}
} }
static inline bool CheckFirstNbitsEquals(const byte *a, const byte *b, size_t n) { static WgCidrAddr WgCidrAddrFromIpAddr(const IpAddr &addr) {
return memcmp(a, b, n >> 3) == 0 && ((n & 7) == 0 || !((a[n >> 3] ^ b[n >> 3]) & (0xff << (8 - (n & 7))))); WgCidrAddr r = {0};
} if (addr.sin.sin_family == AF_INET) {
r.size = r.cidr = 32;
static bool IsWgCidrAddrSubsetOf(const WgCidrAddr &inner, const WgCidrAddr &outer) { memcpy(r.addr, &addr.sin.sin_addr, 4);
return inner.size == outer.size && inner.cidr >= outer.cidr && } else if (addr.sin.sin_family == AF_INET6) {
CheckFirstNbitsEquals(inner.addr, outer.addr, outer.cidr); r.size = r.cidr = 128;
memcpy(r.addr, &addr.sin6.sin6_addr, 16);
}
return r;
} }
bool WireguardProcessor::Start() { bool WireguardProcessor::Start() {
@ -146,74 +152,52 @@ bool WireguardProcessor::ConfigureTun() {
assert(dev_.IsMainThread()); assert(dev_.IsMainThread());
TunInterface::TunConfig config = {0}; TunInterface::TunConfig config = {0};
if (tun_addr_.size == 32) { uint32 ipv4_broadcast_addr = 0xffffffff;
if (tun_addr_.cidr >= 31) {
RERROR("TAP is not compatible CIDR /31 or /32. Changing to /24");
tun_addr_.cidr = 24;
}
config.ip = ReadBE32(tun_addr_.addr);
config.cidr = tun_addr_.cidr;
} else {
RERROR("No IPv4 address configured");
}
config.mtu = mtu_; for (auto it = addresses_.begin(); it != addresses_.end(); ++it) {
config.pre_post_commands = pre_post_; if (it->size == 32) {
config.excluded_ips = excluded_ips_; if (it->cidr >= 31) {
RINFO("TAP is not compatible CIDR /31 or /32. Changing to /24");
it->cidr = 24;
}
uint32 netmask = tun_addr_.cidr == 32 ? 0xffffffff : 0xffffffff << (32 - tun_addr_.cidr); // Packets to this IP will not be sent out.
if (ipv4_broadcast_addr == 0xffffffff) {
uint32 ipv4_broadcast_addr = (netmask == 0xffffffff) ? 0xffffffff : config.ip | ~netmask; uint32 netmask = it->cidr == 32 ? 0xffffffff : 0xffffffff << (32 - it->cidr);
ipv4_broadcast_addr = (netmask == 0xffffffff) ? 0xffffffff : ReadBE32(it->addr) | ~netmask;
if (tun6_addr_.size == 128) { }
if (tun6_addr_.cidr > 126) { } else if (it->size == 128) {
RERROR("IPv6 /127 or /128 not supported. Changing to 120"); if (it->cidr > 126) {
tun6_addr_.cidr = 120; RERROR("IPv6 /127 or /128 not supported. Changing to 120");
} it->cidr = 120;
config.ipv6_cidr = tun6_addr_.cidr;
memcpy(&config.ipv6_address, tun6_addr_.addr, 16);
}
if (add_routes_mode_) {
WgPeer *peer = (WgPeer *)dev_.ip_to_peer_map().LookupV4DefaultPeer();
if (peer != NULL && peer->endpoint_.sin.sin_family != 0) {
config.default_route_endpoint_v4 = (peer->endpoint_.sin.sin_family == AF_INET) ? ReadBE32(&peer->endpoint_.sin.sin_addr) : 0;
// Set the default route to something
config.use_ipv4_default_route = true;
peer->allow_endpoint_change_ = false;
}
// Also configure ipv6 gw?
if (config.ipv6_cidr != 0) {
peer = (WgPeer*)dev_.ip_to_peer_map().LookupV6DefaultPeer();
if (peer != NULL && peer->endpoint_.sin.sin_family != 0) {
if (peer->endpoint_.sin.sin_family == AF_INET6)
memcpy(&config.default_route_endpoint_v6, &peer->endpoint_.sin6.sin6_addr, 16);
config.use_ipv6_default_route = true;
peer->allow_endpoint_change_ = false;
} }
} }
config.addresses.push_back(*it);
}
config.mtu = mtu_;
config.pre_post_commands = pre_post_;
config.excluded_routes = excluded_ips_;
if (add_routes_mode_) {
// For each peer, add the extra routes to the extra routes table // For each peer, add the extra routes to the extra routes table
for (WgPeer *peer = dev_.first_peer(); peer; peer = peer->next_peer_) { for (WgPeer *peer = dev_.first_peer(); peer; peer = peer->next_peer_) {
for (auto it = peer->allowed_ips_.begin(); it != peer->allowed_ips_.end(); ++it) { for (auto it = peer->allowed_ips_.begin(); it != peer->allowed_ips_.end(); ++it) {
// Don't add an entry if it's identical to my address or it's a default route config.included_routes.push_back(*it);
if (IsWgCidrAddrSubsetOf(*it, tun_addr_) || IsWgCidrAddrSubsetOf(*it, tun6_addr_) || it->cidr == 0) // If peer has the ::/0 or 0.0.0.0/0 address, disallow endpoint change.
continue; if (it->cidr == 0)
// Don't add an entry if we have no ipv6 address configured peer->allow_endpoint_change_ = false;
if (config.ipv6_cidr == 0 && it->size != 32)
continue;
config.extra_routes.push_back(*it);
} }
// Add the peer's endpoint to the route exclusion list.
WgCidrAddr endpoint_addr = WgCidrAddrFromIpAddr(peer->endpoint_);
if (endpoint_addr.size != 0)
config.excluded_routes.push_back(endpoint_addr);
} }
} }
config.block_dns_on_adapters = dns_blocking_ && ((config.use_ipv4_default_route && dns_addr_.size()) || config.block_dns_on_adapters = dns_blocking_;
(config.use_ipv6_default_route && dns6_addr_.size()));
config.internet_blocking = internet_blocking_; config.internet_blocking = internet_blocking_;
config.dns = dns_addr_;
config.ipv4_dns = dns_addr_;
config.ipv6_dns = dns6_addr_;
TunInterface::TunConfigOut config_out; TunInterface::TunConfigOut config_out;
if (!tun_->Configure(std::move(config), &config_out)) if (!tun_->Configure(std::move(config), &config_out))

View file

@ -99,8 +99,7 @@ public:
WgDevice &dev() { return dev_; } WgDevice &dev() { return dev_; }
TunInterface::PrePostCommands &prepost() { return pre_post_; } TunInterface::PrePostCommands &prepost() { return pre_post_; }
const WgCidrAddr &tun_addr() { return tun_addr_; } const std::vector<WgCidrAddr> &addr() { return addresses_; }
void RunAllMainThreadScheduled(); void RunAllMainThreadScheduled();
private: private:
void DoWriteUdpPacket(Packet *packet); void DoWriteUdpPacket(Packet *packet);
@ -138,12 +137,10 @@ private:
WgDevice dev_; WgDevice dev_;
WgCidrAddr tun_addr_;
WgCidrAddr tun6_addr_;
WgProcessorStats stats_; WgProcessorStats stats_;
std::vector<IpAddr> dns_addr_, dns6_addr_; std::vector<WgCidrAddr> addresses_;
std::vector<IpAddr> dns_addr_;
TunInterface::PrePostCommands pre_post_; TunInterface::PrePostCommands pre_post_;

View file

@ -379,10 +379,8 @@ void WgConfig::HandleConfigurationProtocolGet(WireguardProcessor *proc, std::str
CmsgAppendHex(result, "private_key", proc->dev_.s_priv_, sizeof(proc->dev_.s_priv_)); CmsgAppendHex(result, "private_key", proc->dev_.s_priv_, sizeof(proc->dev_.s_priv_));
if (proc->listen_port_) if (proc->listen_port_)
CmsgAppendFmt(result, "listen_port=%d", proc->listen_port_); CmsgAppendFmt(result, "listen_port=%d", proc->listen_port_);
if (proc->tun_addr_.size == 32) for(const WgCidrAddr &x : proc->addresses_)
CmsgAppendFmt(result, "address=%s", PrintWgCidrAddr(proc->tun_addr_, buf)); CmsgAppendFmt(result, "address=%s", PrintWgCidrAddr(x, buf));
if (proc->tun6_addr_.size == 128)
CmsgAppendFmt(result, "address=%s", PrintWgCidrAddr(proc->tun6_addr_, buf));
for (WgPeer *peer = proc->dev_.peers_; peer; peer = peer->next_peer_) { for (WgPeer *peer = proc->dev_.peers_; peer; peer = peer->next_peer_) {
WG_SCOPED_LOCK(peer->lock_); WG_SCOPED_LOCK(peer->lock_);