1
1
Fork 0
mirror of https://github.com/oxen-io/lokinet synced 2023-12-14 06:53:00 +01:00
lokinet/llarp/vpn/win32.cpp
Jason Rhinelander 9ddf7413af
Windows DNS fixes
- windivert was being set up *before* DNS is set up, so the DNS port was
  nullopt and thus we couldn't properly identify upstream DNS traffic.
- close() doesn't close a socket on Windows, so the socket-bind-close
  approach to get a free UDP port wasn't actually closing, and thus
  unbound upstream constrained to the given port were completely
  failing.
- The unbound thread was accessing the same shared_ptr instance as the
  outer code, which isn't thread-safe; changed it to copy a weak_ptr
  into the lambda instead.
- Exclude upstream DNS traffic in the filter rather than capturing and
  reinjecting it.
2022-09-19 20:26:37 -03:00

160 lines
4.7 KiB
C++

#include "vpn/win32.hpp"
#include <llarp/win32/windivert.hpp>
#include <llarp/win32/wintun.hpp>
#include <fmt/core.h>
namespace llarp::win32
{
namespace
{
template <typename T>
std::string
ip_to_string(T ip)
{
return var::visit([](auto&& ip) { return ip.ToString(); }, ip);
}
} // namespace
void
VPNPlatform::Route(std::string ip, std::string gw, std::string cmd)
{
llarp::win32::Exec(
"route.exe", fmt::format("{} {} MASK 255.255.255.255 {} METRIC {}", cmd, ip, gw, m_Metric));
}
void
VPNPlatform::DefaultRouteViaInterface(NetworkInterface& vpn, std::string cmd)
{
// route hole for loopback bacause god is dead on windows
llarp::win32::Exec(
"route.exe", fmt::format("{} 127.0.0.0 MASK 255.0.0.0 0.0.0.0 METRIC {}", cmd, m_Metric));
// set up ipv4 routes
auto lower = RouteViaInterface(vpn, "0.0.0.0", "128.0.0.0", cmd);
auto upper = RouteViaInterface(vpn, "128.0.0.0", "128.0.0.0", cmd);
}
OneShotExec
VPNPlatform::RouteViaInterface(
NetworkInterface& vpn, std::string addr, std::string mask, std::string cmd)
{
const auto& info = vpn.Info();
auto index = info.index;
if (index == 0)
{
if (auto maybe_idx = net::Platform::Default_ptr()->GetInterfaceIndex(info[0]))
index = *maybe_idx;
}
auto ifaddr = ip_to_string(info[0]);
// this changes the last 1 to a 0 so that it routes over the interface
// this is required because windows is idiotic af
ifaddr.back()--;
if (index)
{
return OneShotExec{
"route.exe",
fmt::format(
"{} {} MASK {} {} IF {} METRIC {}", cmd, addr, mask, ifaddr, info.index, m_Metric)};
}
else
{
return OneShotExec{
"route.exe",
fmt::format("{} {} MASK {} {} METRIC {}", cmd, addr, mask, ifaddr, m_Metric)};
}
}
void
VPNPlatform::AddRoute(net::ipaddr_t ip, net::ipaddr_t gateway)
{
Route(ip_to_string(ip), ip_to_string(gateway), "ADD");
}
void
VPNPlatform::DelRoute(net::ipaddr_t ip, net::ipaddr_t gateway)
{
Route(ip_to_string(ip), ip_to_string(gateway), "DELETE");
}
void
VPNPlatform::AddRouteViaInterface(NetworkInterface& vpn, IPRange range)
{
RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "ADD");
}
void
VPNPlatform::DelRouteViaInterface(NetworkInterface& vpn, IPRange range)
{
RouteViaInterface(vpn, range.BaseAddressString(), range.NetmaskString(), "DELETE");
}
std::vector<net::ipaddr_t>
VPNPlatform::GetGatewaysNotOnInterface(NetworkInterface& vpn)
{
std::vector<net::ipaddr_t> gateways;
auto idx = vpn.Info().index;
using UInt_t = decltype(idx);
for (const auto& iface : Net().AllNetworkInterfaces())
{
if (static_cast<UInt_t>(iface.index) == idx)
continue;
if (iface.gateway)
gateways.emplace_back(*iface.gateway);
}
return gateways;
}
void
VPNPlatform::AddDefaultRouteViaInterface(NetworkInterface& vpn)
{
// kill ipv6
llarp::win32::Exec(
"WindowsPowerShell\\v1.0\\powershell.exe",
"-Command (Disable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)");
DefaultRouteViaInterface(vpn, "ADD");
llarp::win32::Exec("ipconfig.exe", "/flushdns");
}
void
VPNPlatform::DelDefaultRouteViaInterface(NetworkInterface& vpn)
{
// restore ipv6
llarp::win32::Exec(
"WindowsPowerShell\\v1.0\\powershell.exe",
"-Command (Enable-NetAdapterBinding -Name \"* \" -ComponentID ms_tcpip6)");
DefaultRouteViaInterface(vpn, "DELETE");
llarp::win32::Exec("netsh.exe", "winsock reset");
llarp::win32::Exec("ipconfig.exe", "/flushdns");
}
std::shared_ptr<NetworkInterface>
VPNPlatform::ObtainInterface(InterfaceInfo info, AbstractRouter* router)
{
return wintun::make_interface(std::move(info), router);
}
std::shared_ptr<I_Packet_IO>
VPNPlatform::create_packet_io(
unsigned int ifindex, const std::optional<SockAddr>& dns_upstream_src)
{
// we only want do this on all interfaes with windivert
if (ifindex)
throw std::invalid_argument{
"cannot create packet io on explicitly specified interface, not currently supported on "
"windows (yet)"};
uint16_t upstream_src_port = dns_upstream_src ? dns_upstream_src->getPort() : 0;
std::string udp_filter = upstream_src_port != 0
? fmt::format("( udp.DstPort == 53 and udp.SrcPort != {} )", upstream_src_port)
: "udp.DstPort == 53";
auto filter = "outbound and ( " + udp_filter + " or tcp.DstPort == 53 )";
return WinDivert::make_interceptor(filter, [router = _ctx->router] { router->TriggerPump(); });
}
} // namespace llarp::win32