Skip to content
Open
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
64 changes: 48 additions & 16 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 @@ -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,14 +226,33 @@ 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)
{
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)
{
Expand All @@ -246,15 +269,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]",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it guaranteed that route.to is always empty?
if so, we should fail the request if route.to.has_value().
this would be silently not doing what the caller expected if route.to.has_value()

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 +287,11 @@ 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");
}

struct Message : RouteMessage
{
utils::AddressAttribute<TAddr> to;
Expand All @@ -272,15 +300,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]",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

route.via might have a value, right?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's guaranteed in all possible paths that calling ModifyLinkLocalRouteImpl can never have a route.via?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if so, we should fail the request if route.via.has_value().
this would be silently not doing what the caller expected if route.via.has_value()

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 +322,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 +336,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