diff --git a/src/linux/netlinkutil/RoutingTable.cpp b/src/linux/netlinkutil/RoutingTable.cpp index 9ec761c0c..62941b0db 100644 --- a/src/linux/netlinkutil/RoutingTable.cpp +++ b/src/linux/netlinkutil/RoutingTable.cpp @@ -111,6 +111,10 @@ void RoutingTable::ModifyRouteImpl(const Route& route, Operation action) { ModifyLoopbackRouteImpl(route, operation, flags); } + else if (route.defaultRoute && route.IsOnlink()) + { + ModifyDefaultLinkLocalRouteImpl(route, operation, flags); + } else if (route.defaultRoute) { ModifyDefaultRouteImpl(route, operation, flags); @@ -222,7 +226,7 @@ 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(message.to, route.to.value(), RTA_DST); @@ -230,6 +234,25 @@ void RoutingTable::ModifyLoopbackRouteImpl(const Route& route, int operation, in }); } +template +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(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 void RoutingTable::ModifyDefaultRouteImpl(const Route& route, int operation, int flags) { @@ -253,8 +276,8 @@ void RoutingTable::ModifyDefaultRouteImpl(const Route& route, int operation, int SendMessage(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(message.via, route.via.value(), RTA_GATEWAY); utils::InitializeIntegerAttribute(message.metric, route.metric, RTA_PRIORITY); @@ -264,6 +287,11 @@ void RoutingTable::ModifyDefaultRouteImpl(const Route& route, int operation, int template 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 to; @@ -279,7 +307,7 @@ void RoutingTable::ModifyLinkLocalRouteImpl(const Route& route, int operation, i SendMessage(route, operation, flags, [&](Message& message) { GNS_LOG_INFO( - "InitializeAddressAttribute RTA_DST ({}) RTA_GATEWAY ([not set]), RTA_PRIORITY ({})", + "Netlink message configuration: RTA_DST ({}) RTA_GATEWAY ([not set]), RTA_PRIORITY ({})", route.to.has_value() ? route.to.value().Addr().c_str() : "[empty]", route.metric); utils::InitializeAddressAttribute(message.to, route.to.value(), RTA_DST); @@ -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 { @@ -311,7 +343,7 @@ void RoutingTable::ModifyOfflinkRouteImpl(const Route& route, int operation, int SendMessage(route, operation, flags, [&](Message& message) { GNS_LOG_INFO( - "InitializeAddressAttribute RTA_DST ({}) RTA_GATEWAY ({}), RTA_PRIORITY ({})", + "Netlink message configuration: 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]", route.metric); diff --git a/src/linux/netlinkutil/RoutingTable.h b/src/linux/netlinkutil/RoutingTable.h index a5e0d05e4..0f9bfb02c 100644 --- a/src/linux/netlinkutil/RoutingTable.h +++ b/src/linux/netlinkutil/RoutingTable.h @@ -51,6 +51,9 @@ class RoutingTable template void ModifyDefaultRouteImpl(const Route& route, int operation, int flags); + template + void ModifyDefaultLinkLocalRouteImpl(const Route& route, int operation, int flags); + template void ModifyLinkLocalRouteImpl(const Route& route, int operation, int flags); diff --git a/src/windows/service/exe/WslMirroredNetworking.cpp b/src/windows/service/exe/WslMirroredNetworking.cpp index d0f1f8b1a..28b8d3137 100644 --- a/src/windows/service/exe/WslMirroredNetworking.cpp +++ b/src/windows/service/exe/WslMirroredNetworking.cpp @@ -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); } } diff --git a/test/windows/NetworkTests.cpp b/test/windows/NetworkTests.cpp index 0cdf8d470..6d0925f98 100644 --- a/test/windows/NetworkTests.cpp +++ b/test/windows/NetworkTests.cpp @@ -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();