Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
9 changes: 7 additions & 2 deletions src/linux/netlinkutil/Route.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@
#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)
family(family), via(nextHop), dev(dev), defaultRoute(defaultRoute), to(to), metric(metric)
{
// 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 family, const std::optional<Address>& nextHop, int dev, bool defaultRoute, const std::optional<Address>& to, int metric);

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)
{
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]",
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);
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]",
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())
{
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
37 changes: 37 additions & 0 deletions test/windows/NetworkTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,43 @@ class NetworkTests
VERIFY_ARE_EQUAL(v6State.DefaultRoute->Device, L"eth0");
}

TEST_METHOD(AddRemoveDefaultOnlinkRoutes)
{
WSL2_TEST_ONLY();

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;
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;
SendDeviceSettingsRequest(L"eth0", defaultRouteV6, ModifyRequestType::Add, GuestEndpointResourceType::Route);

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);

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);
}

TEST_METHOD(SetInterfaceDownAndUp)
{
WSL2_TEST_ONLY();
Expand Down
Loading