Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions src/linux/netlinkutil/Route.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@
#include "Route.h"
#include "Utils.h"

Route::Route(int family, const std::optional<Address>& via, int dev, bool defaultRoute, const std::optional<Address>& to, int metric) :
family(family), via(via), dev(dev), defaultRoute(defaultRoute), to(to), metric(metric)
Route::Route(int routeFamily, const std::optional<Address>& routeNextHop, int routeInterface, bool isRouteDefault, const std::optional<Address>& routeDestination, int routeMetric) :
family(routeFamily), via(routeNextHop), dev(routeInterface), defaultRoute(isRouteDefault), to(routeDestination), metric(routeMetric)
{
// For onlink routes, ensure the via field is empty
if (via.has_value() && ((family == AF_INET && via->Addr() == "0.0.0.0") || (family == AF_INET6 && via->Addr() == "::")))
{
via.reset();
}
}

std::ostream& operator<<(std::ostream& out, const Route& route)
Expand All @@ -30,7 +35,7 @@ std::ostream& operator<<(std::ostream& out, const Route& route)

bool Route::IsOnlink() const
{
return !via.has_value() || (family == AF_INET && via->Addr() == "0.0.0.0") || (family == AF_INET6 && via->Addr() == "::");
return !via.has_value();
}

bool Route::IsMulticast() const
Expand Down
2 changes: 1 addition & 1 deletion src/linux/netlinkutil/Route.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct Route
int metric = 0;
bool isLoopbackRoute = false;

Route(int family, const std::optional<Address>& via, int dev, bool defaultRoute, const std::optional<Address>& to, int metric);
Route(int routeFamily, const std::optional<Address>& routeNextHop, int routeInterface, bool isRouteDefault, const std::optional<Address>& routeDestination, int routeMetric);

bool IsOnlink() const;
bool IsMulticast() const;
Expand Down
85 changes: 67 additions & 18 deletions src/linux/netlinkutil/RoutingTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ void RoutingTable::ModifyRouteImpl(const Route& route, Operation action)
{
ModifyLoopbackRouteImpl<TAddr>(route, operation, flags);
}
else if (route.defaultRoute && route.IsOnlink())
{
ModifyDefaultLinkLocalRouteImpl<TAddr>(route, operation, flags);
}
else if (route.defaultRoute)
{
ModifyDefaultRouteImpl<TAddr>(route, operation, flags);
Expand Down Expand Up @@ -181,7 +185,7 @@ void RoutingTable::ModifyLoopbackRouteImpl(const Route& route, int operation, in
{
if (!route.to.has_value() || !route.via.has_value())
{
throw RuntimeErrorWithSourceLocation(std::format("Loopback route {} missing destination or gateway address", utils::Stringify(route)));
throw RuntimeErrorWithSourceLocation(std::format("Loopback route {} missing destination or next hop", utils::Stringify(route)));
}

struct Message : RouteMessage
Expand All @@ -193,8 +197,8 @@ void RoutingTable::ModifyLoopbackRouteImpl(const Route& route, int operation, in

GNS_LOG_INFO(
"SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})",
route.to.has_value() ? route.to.value().Addr().c_str() : "[empty]",
route.via.has_value() ? route.via.value().Addr().c_str() : "[empty]",
route.to.value().Addr().c_str(),
route.via.value().Addr().c_str(),
RouteOperationToString(operation),
NetLinkFormatFlagsToString(flags).c_str());

Expand Down Expand Up @@ -222,20 +226,52 @@ void RoutingTable::ModifyLoopbackRouteImpl(const Route& route, int operation, in

message.route.rtm_flags |= RTNH_F_ONLINK;
GNS_LOG_INFO(
"InitializeAddressAttribute RTA_DST ({}) RTA_GATEWAY ({}) RTA_PRIORITY ([not set])",
"Netlink message configuration: RTA_DST ({}) RTA_GATEWAY ({}) RTA_PRIORITY ([not set])",
route.to.value().Addr().c_str(),
route.via.value().Addr().c_str());
utils::InitializeAddressAttribute<TAddr>(message.to, route.to.value(), RTA_DST);
utils::InitializeAddressAttribute<TAddr>(message.via, route.via.value(), RTA_GATEWAY);
});
}

template <typename TAddr>
void RoutingTable::ModifyDefaultLinkLocalRouteImpl(const Route& route, int operation, int flags)
Comment thread
FetoiuCatalin marked this conversation as resolved.
{
if (route.via.has_value())
{
throw RuntimeErrorWithSourceLocation("Default route has unexpected next hop");
}
if (route.to.has_value())
{
throw RuntimeErrorWithSourceLocation("Default route has unexpected destination address");
}

struct Message : RouteMessage
{
utils::IntegerAttribute metric;
} __attribute__((packed));

GNS_LOG_INFO(
"SendMessage Route (default onlink), operation ({}), netLinkflags ({})",
RouteOperationToString(operation),
NetLinkFormatFlagsToString(flags).c_str());

SendMessage<Message>(route, operation, flags, [&](Message& message) {
GNS_LOG_INFO("Netlink message configuration: RTA_DST ([not set]) RTA_GATEWAY ([not set]), RTA_PRIORITY ({})", route.metric);
utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);
});
}

template <typename TAddr>
void RoutingTable::ModifyDefaultRouteImpl(const Route& route, int operation, int flags)
{
if (!route.via.has_value())
{
throw RuntimeErrorWithSourceLocation("Default route is missing its gateway address");
throw RuntimeErrorWithSourceLocation("Default route is missing its next hop");
}
if (route.to.has_value())
{
throw RuntimeErrorWithSourceLocation("Default route has unexpected destination address");
}

struct Message : RouteMessage
Expand All @@ -246,15 +282,15 @@ void RoutingTable::ModifyDefaultRouteImpl(const Route& route, int operation, int

GNS_LOG_INFO(
"SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})",
route.to.has_value() ? route.to.value().Addr().c_str() : "[empty]",
route.via.has_value() ? route.via.value().Addr().c_str() : "[empty]",
"[empty]",
Comment thread
keith-horton marked this conversation as resolved.
route.via.value().Addr().c_str(),
RouteOperationToString(operation),
NetLinkFormatFlagsToString(flags).c_str());

SendMessage<Message>(route, operation, flags, [&](Message& message) {
GNS_LOG_INFO(
"InitializeAddressAttribute RTA_DST ([not set]) RTA_GATEWAY ({}), RTA_PRIORITY ({})",
route.to.has_value() ? route.to.value().Addr().c_str() : "[empty]",
"Netlink message configuration: RTA_DST ([not set]) RTA_GATEWAY ({}), RTA_PRIORITY ({})",
route.via.value().Addr().c_str(),
route.metric);
utils::InitializeAddressAttribute<TAddr>(message.via, route.via.value(), RTA_GATEWAY);
Comment thread
keith-horton marked this conversation as resolved.
utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);
Expand All @@ -264,6 +300,15 @@ void RoutingTable::ModifyDefaultRouteImpl(const Route& route, int operation, int
template <typename TAddr>
void RoutingTable::ModifyLinkLocalRouteImpl(const Route& route, int operation, int flags)
{
if (!route.to.has_value())
{
throw RuntimeErrorWithSourceLocation("Link-local route is missing its destination address");
}
if (route.via.has_value())
{
throw RuntimeErrorWithSourceLocation("Link-local route has unexpected next hop");
}

struct Message : RouteMessage
{
utils::AddressAttribute<TAddr> to;
Expand All @@ -272,15 +317,15 @@ void RoutingTable::ModifyLinkLocalRouteImpl(const Route& route, int operation, i

GNS_LOG_INFO(
"SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})",
route.to.has_value() ? route.to.value().Addr().c_str() : "[empty]",
route.via.has_value() ? route.via.value().Addr().c_str() : "[empty]",
route.to.value().Addr().c_str(),
"[empty]",
Comment thread
keith-horton marked this conversation as resolved.
RouteOperationToString(operation),
NetLinkFormatFlagsToString(flags).c_str());

SendMessage<Message>(route, operation, flags, [&](Message& message) {
GNS_LOG_INFO(
"InitializeAddressAttribute RTA_DST ({}) RTA_GATEWAY ([not set]), RTA_PRIORITY ({})",
route.to.has_value() ? route.to.value().Addr().c_str() : "[empty]",
"Netlink message configuration: RTA_DST ({}) RTA_GATEWAY ([not set]), RTA_PRIORITY ({})",
route.to.value().Addr().c_str(),
route.metric);
utils::InitializeAddressAttribute<TAddr>(message.to, route.to.value(), RTA_DST);
utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY);
Expand All @@ -294,6 +339,10 @@ void RoutingTable::ModifyOfflinkRouteImpl(const Route& route, int operation, int
{
throw RuntimeErrorWithSourceLocation("Offlink route is missing its next hop");
}
if (!route.to.has_value())
Comment thread
FetoiuCatalin marked this conversation as resolved.
{
throw RuntimeErrorWithSourceLocation("Offlink route is missing its destination address");
}

struct Message : RouteMessage
{
Expand All @@ -304,16 +353,16 @@ void RoutingTable::ModifyOfflinkRouteImpl(const Route& route, int operation, int

GNS_LOG_INFO(
"SendMessage Route (to {}, via {}), operation ({}), netLinkflags ({})",
route.to.has_value() ? route.to.value().Addr().c_str() : "[empty]",
route.via.has_value() ? route.via.value().Addr().c_str() : "[empty]",
route.to.value().Addr().c_str(),
route.via.value().Addr().c_str(),
RouteOperationToString(operation),
NetLinkFormatFlagsToString(flags).c_str());

SendMessage<Message>(route, operation, flags, [&](Message& message) {
GNS_LOG_INFO(
"InitializeAddressAttribute RTA_DST ({}) RTA_GATEWAY ({}), RTA_PRIORITY ({})",
route.to.has_value() ? route.to.value().Addr().c_str() : "[empty]",
route.via.has_value() ? route.via.value().Addr().c_str() : "[empty]",
"Netlink message configuration: RTA_DST ({}) RTA_GATEWAY ({}), RTA_PRIORITY ({})",
route.to.value().Addr().c_str(),
route.via.value().Addr().c_str(),
route.metric);
utils::InitializeAddressAttribute<TAddr>(message.to, route.to.value(), RTA_DST);
utils::InitializeAddressAttribute<TAddr>(message.via, route.via.value(), RTA_GATEWAY);
Expand Down
3 changes: 3 additions & 0 deletions src/linux/netlinkutil/RoutingTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ class RoutingTable
template <typename TAddr>
void ModifyDefaultRouteImpl(const Route& route, int operation, int flags);

template <typename TAddr>
void ModifyDefaultLinkLocalRouteImpl(const Route& route, int operation, int flags);

template <typename TAddr>
void ModifyLinkLocalRouteImpl(const Route& route, int operation, int flags);

Expand Down
9 changes: 9 additions & 0 deletions src/windows/service/exe/WslMirroredNetworking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,15 @@ void wsl::core::networking::WslMirroredNetworkManager::ProcessRouteChange()
{
endpointRoute.Metric = UINT16_MAX;
}

// Some Windows interfaces (like VPNs) can have metric 0 and routes over that interface with metric also 0, adding
// up to 0. Linux treats metric 0 as unspecified and will default to a 1024 metric. The highest priority metric in
// Linux is 1 instead so we need to switch the metric from 0 to 1.
if (endpointRoute.Metric == 0)
{
endpointRoute.Metric = 1;
}

endpoint.Network->Routes.insert(endpointRoute);
}
}
Expand Down
3 changes: 1 addition & 2 deletions test/windows/MountTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,10 +438,9 @@ class MountTests
VERIFY_ARE_EQUAL(LxsstuLaunchWsl(L"--unmount " + absolutePath.wstring()), (DWORD)0);
}

TEST_METHOD(AbsolutePathVhdUnmountAfterVMTimeout)
WSL2_TEST_METHOD(AbsolutePathVhdUnmountAfterVMTimeout)
{
SKIP_UNSUPPORTED_ARM64_MOUNT_TEST();
WSL2_TEST_ONLY();

WslKeepAlive keepAlive;

Expand Down
35 changes: 35 additions & 0 deletions test/windows/NetworkTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,41 @@ class NetworkTests
VERIFY_ARE_EQUAL(v6State.DefaultRoute->Device, L"eth0");
}

WSL2_TEST_METHOD(AddRemoveDefaultOnlinkRoutes)
{
wsl::shared::hns::Route defaultRouteV4;
defaultRouteV4.NextHop = L"0.0.0.0";
defaultRouteV4.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_PREFIX;
defaultRouteV4.Family = AF_INET;
defaultRouteV4.Metric = 1;
Comment thread
keith-horton marked this conversation as resolved.
SendDeviceSettingsRequest(L"eth0", defaultRouteV4, ModifyRequestType::Add, GuestEndpointResourceType::Route);

wsl::shared::hns::Route defaultRouteV6;
defaultRouteV6.NextHop = L"::";
defaultRouteV6.DestinationPrefix = LX_INIT_DEFAULT_ROUTE_V6_PREFIX;
defaultRouteV6.Family = AF_INET6;
defaultRouteV6.Metric = 1;
Comment thread
FetoiuCatalin marked this conversation as resolved.
SendDeviceSettingsRequest(L"eth0", defaultRouteV6, ModifyRequestType::Add, GuestEndpointResourceType::Route);
Comment thread
FetoiuCatalin marked this conversation as resolved.

const bool defaultV4RouteExists =
LxsstuLaunchWsl(L"ip -4 route show | grep \"default dev eth0\" | grep -w \"metric 1\"") == (DWORD)0;
const bool defaultV6RouteExists =
LxsstuLaunchWsl(L"ip -6 route show | grep \"default dev eth0\" | grep -w \"metric 1\"") == (DWORD)0;

SendDeviceSettingsRequest(L"eth0", defaultRouteV4, ModifyRequestType::Remove, GuestEndpointResourceType::Route);
SendDeviceSettingsRequest(L"eth0", defaultRouteV6, ModifyRequestType::Remove, GuestEndpointResourceType::Route);
Comment thread
FetoiuCatalin marked this conversation as resolved.

const bool defaultV4RouteRemoved =
LxsstuLaunchWsl(L"ip -4 route show | grep \"default dev eth0\" | grep -w \"metric 1\"") != (DWORD)0;
const bool defaultV6RouteRemoved =
LxsstuLaunchWsl(L"ip -6 route show | grep \"default dev eth0\" | grep -w \"metric 1\"") != (DWORD)0;

VERIFY_IS_TRUE(defaultV4RouteExists);
VERIFY_IS_TRUE(defaultV6RouteExists);
VERIFY_IS_TRUE(defaultV4RouteRemoved);
VERIFY_IS_TRUE(defaultV6RouteRemoved);
}

WSL2_TEST_METHOD(SetInterfaceDownAndUp)
{
// Disconnect interface
Expand Down
Loading