merge: branch 'bg/warn-unreachable-gateways'

Warn about directly unreachable gateways

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2360
This commit is contained in:
Beniamino Galvani 2026-02-18 13:51:23 +00:00
commit 7d0427890a
9 changed files with 604 additions and 1 deletions

8
NEWS
View file

@ -12,6 +12,14 @@ USE AT YOUR OWN RISK. NOT RECOMMENDED FOR PRODUCTION USE!
suffixes when appropriate. This affects, for example, the URL and filename
of the release tarball and the version reported by nmcli and the daemon.
As an exception, the C API will continue to use the 90+ scheme for RC versions.
* Connection profiles with manual IP addressing and with gateways that are not
directly reachable will generate a warning on activation and when they are
added/modified via nmcli and nmtui. NetworkManager currently adds on-link
routes for them automatically, but this will change in the future. To fix the
warning, users should add addresses or routes whose subnets cover these
gateways. A gateway (either the default gateway or the next-hop of a route) is
considered directly reachable if it falls within the subnet of a direct route
(a route without a next hop) or of a prefix route from a static address.
* Restrict the connectivity check to use the DNS servers defined on the
same link. If the link has no DNS servers, the connectivity check will
use any servers available in the system.

View file

@ -3684,6 +3684,7 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co
{
NML3ConfigData *l3cd;
int ifindex;
gs_free char *gw_warning = NULL;
nm_assert(NM_IS_DEVICE(self));
nm_assert(!connection || NM_IS_CONNECTION(connection));
@ -3704,6 +3705,10 @@ nm_device_create_l3_config_data_from_connection(NMDevice *self, NMConnection *co
nm_l3_config_data_set_ip6_privacy(l3cd, _prop_get_ipv6_ip6_privacy(self, connection));
nm_l3_config_data_set_mptcp_flags(l3cd, _prop_get_connection_mptcp_flags(self, connection));
gw_warning = nm_connection_get_unreachable_gateways_warning(connection, FALSE);
if (gw_warning)
_LOGW(LOGD_IP, "%s", gw_warning);
return l3cd;
}

View file

@ -1149,3 +1149,202 @@ nm_setting_ovs_other_config_check_val(const char *val, GError **error)
return TRUE;
}
/*****************************************************************************/
typedef struct {
NMIPAddr dest;
guint prefix;
} DirectRoute;
static void
_setting_ip_config_collect_unreachable_gateways(NMSettingIPConfig *s_ip, GHashTable **result)
{
const int addr_family = nm_setting_ip_config_get_addr_family(s_ip);
guint num_routes;
guint num_addrs;
guint n_direct_routes = 0;
NMIPAddr gw_bin = NM_IP_ADDR_INIT;
guint i;
guint j;
const char *gateway;
gs_free DirectRoute *direct_routes = NULL;
num_routes = nm_setting_ip_config_get_num_routes(s_ip);
num_addrs = nm_setting_ip_config_get_num_addresses(s_ip);
direct_routes = g_new0(DirectRoute, num_routes + num_addrs);
/* Collect direct routes (routes without a gateway) from the setting. */
for (i = 0; i < num_routes; i++) {
NMIPRoute *route = nm_setting_ip_config_get_route(s_ip, i);
if (nm_ip_route_get_next_hop(route))
continue;
nm_ip_route_get_dest_binary(route, &direct_routes[n_direct_routes].dest);
direct_routes[n_direct_routes].prefix = nm_ip_route_get_prefix(route);
n_direct_routes++;
}
/* Add prefix routes (device routes) for each static address. */
for (i = 0; i < num_addrs; i++) {
NMIPAddress *addr = nm_setting_ip_config_get_address(s_ip, i);
nm_ip_address_get_address_binary(addr, &direct_routes[n_direct_routes].dest);
direct_routes[n_direct_routes].prefix = nm_ip_address_get_prefix(addr);
n_direct_routes++;
}
/* Check the setting's default gateway. */
gateway = nm_setting_ip_config_get_gateway(s_ip);
if (gateway && nm_inet_parse_bin(addr_family, gateway, NULL, &gw_bin)) {
gboolean reachable = FALSE;
if (addr_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&gw_bin.addr6))
reachable = TRUE;
if (!reachable) {
for (j = 0; j < n_direct_routes; j++) {
if (nm_ip_addr_same_prefix(addr_family,
&gw_bin,
&direct_routes[j].dest,
direct_routes[j].prefix)) {
reachable = TRUE;
break;
}
}
}
if (!reachable) {
if (!*result)
*result = g_hash_table_new(nm_str_hash, g_str_equal);
g_hash_table_add(*result, (gpointer) gateway);
}
}
/* Check gateways of each route in the setting. */
for (i = 0; i < num_routes; i++) {
NMIPRoute *route = nm_setting_ip_config_get_route(s_ip, i);
NMIPAddr next_hop = NM_IP_ADDR_INIT;
gboolean reachable = FALSE;
GVariant *attribute;
if (!nm_ip_route_get_next_hop_binary(route, &next_hop))
continue;
if (addr_family == AF_INET6 && IN6_IS_ADDR_LINKLOCAL(&next_hop.addr6))
continue;
attribute = nm_ip_route_get_attribute(route, NM_IP_ROUTE_ATTRIBUTE_ONLINK);
if (attribute && g_variant_is_of_type(attribute, G_VARIANT_TYPE("b"))
&& g_variant_get_boolean(attribute)) {
/* the gateway of a onlink route is reachable */
continue;
}
for (j = 0; j < n_direct_routes; j++) {
if (nm_ip_addr_same_prefix(addr_family,
&next_hop,
&direct_routes[j].dest,
direct_routes[j].prefix)) {
reachable = TRUE;
break;
}
}
if (!reachable) {
if (!*result)
*result = g_hash_table_new(nm_str_hash, g_str_equal);
g_hash_table_add(*result, (gpointer) nm_ip_route_get_next_hop(route));
}
}
}
/**
* nm_connection_get_unreachable_gateways:
* @connection: the #NMConnection
*
* Checks whether there are gateways (either the default gateway or gateways
* of routes) that are not directly reachable in the IPv4 and IPv6 settings
* of the connection. A gateway is considered directly reachable if it falls
* within the subnet of a direct route (a route without a next hop) or of a
* prefix route from a static address.
*
* Returns: a %NULL-terminated array of gateway strings not directly reachable,
* or %NULL if all gateways are reachable. The individual strings are owned
* by the setting. Free the returned array with g_free().
*/
const char **
nm_connection_get_unreachable_gateways(NMConnection *connection)
{
gs_unref_hashtable GHashTable *result = NULL;
NMSettingIPConfig *s_ip;
guint len;
const char **strv;
if (!connection)
return NULL;
s_ip = nm_connection_get_setting_ip4_config(connection);
if (s_ip
&& nm_streq0(nm_setting_ip_config_get_method(s_ip), NM_SETTING_IP4_CONFIG_METHOD_MANUAL)) {
_setting_ip_config_collect_unreachable_gateways(s_ip, &result);
}
s_ip = nm_connection_get_setting_ip6_config(connection);
if (s_ip
&& nm_streq0(nm_setting_ip_config_get_method(s_ip), NM_SETTING_IP6_CONFIG_METHOD_MANUAL)) {
_setting_ip_config_collect_unreachable_gateways(s_ip, &result);
}
if (result) {
strv = (const char **) g_hash_table_get_keys_as_array(result, &len);
nm_strv_sort(strv, len);
return strv;
}
return NULL;
}
/**
* nm_connection_get_unreachable_gateways_warning:
* @connection: the #NMConnection
* @translate: whether to translate the message (use %TRUE for user-facing
* tools like nmcli, %FALSE for daemon logs)
*
* Checks whether there are unreachable gateways in the connection and returns
* a formatted warning message if so.
*
* Returns: a warning message string, or %NULL if all gateways are reachable.
* Free with g_free().
*/
char *
nm_connection_get_unreachable_gateways_warning(NMConnection *connection, gboolean translate)
{
gs_free const char **gateways = NULL;
gs_free char *gw_list = NULL;
gateways = nm_connection_get_unreachable_gateways(connection);
if (!gateways)
return NULL;
gw_list = g_strjoinv(", ", (char **) gateways);
/* Keep the following two strings in sync. */
if (translate) {
return g_strdup_printf(
_("the following gateways are not directly reachable from any configured address or "
"route: %s. NetworkManager currently adds on-link routes for them automatically, "
"but this will change in the future. Consider adding addresses or routes whose "
"subnets cover these gateways"),
gw_list);
}
return g_strdup_printf(
"the following gateways are not directly reachable from any configured address or "
"route: %s. NetworkManager currently adds on-link routes for them automatically, "
"but this will change in the future. Consider adding addresses or routes whose "
"subnets cover these gateways",
gw_list);
}

View file

@ -344,6 +344,12 @@ gboolean nm_setting_ovs_other_config_check_val(const char *val, GError **error);
/*****************************************************************************/
const char **nm_connection_get_unreachable_gateways(NMConnection *connection);
char *nm_connection_get_unreachable_gateways_warning(NMConnection *connection, gboolean translate);
/*****************************************************************************/
/* Wi-Fi frequencies range for each band */
#define _NM_WIFI_FREQ_MIN_2GHZ 2412
#define _NM_WIFI_FREQ_MAX_2GHZ 2484

View file

@ -11613,6 +11613,328 @@ test_dhcp_iaid_hexstr(void)
/*****************************************************************************/
static void
test_unreachable_gateways(void)
{
NMConnection *conn;
NMSettingIPConfig *s_ip4;
NMSettingIPConfig *s_ip6;
gs_free const char **result = NULL;
/* IPv4 gateway reachable via address prefix route */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
NM_SETTING_IP_CONFIG_GATEWAY,
"192.168.1.1",
NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(!result);
g_object_unref(conn);
}
/* IPv4 gateway NOT reachable */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
NM_SETTING_IP_CONFIG_GATEWAY,
"10.0.0.1",
NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nmtst_setting_ip_config_add_address(s_ip4, "172.16.1.1", 16);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(result);
g_assert_cmpint(g_strv_length((char **) result), ==, 1);
g_assert_cmpstr(result[0], ==, "10.0.0.1");
nm_clear_g_free(&result);
g_object_unref(conn);
}
/* IPv4 gateway NOT reachable. It's ignored because of method "auto" */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP4_CONFIG_METHOD_AUTO,
NM_SETTING_IP_CONFIG_GATEWAY,
"10.0.0.1",
NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nmtst_setting_ip_config_add_address(s_ip4, "172.16.1.1", 16);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(!result);
g_object_unref(conn);
}
/* Route gateway reachable via address prefix route */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nmtst_setting_ip_config_add_route(s_ip4, "10.0.0.0", 8, "192.168.1.254", 100);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(!result);
g_object_unref(conn);
}
/* Route gateway NOT reachable, check sorting */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nmtst_setting_ip_config_add_route(s_ip4, "10.0.0.0", 16, "172.16.0.2", 100);
nmtst_setting_ip_config_add_route(s_ip4, "10.1.0.0", 16, "172.16.0.4", 100);
nmtst_setting_ip_config_add_route(s_ip4, "10.2.0.0", 16, "172.16.0.3", 100);
nmtst_setting_ip_config_add_route(s_ip4, "10.3.0.0", 16, "172.16.0.1", 100);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(result);
g_assert_cmpint(g_strv_length((char **) result), ==, 4);
g_assert_cmpstr(result[0], ==, "172.16.0.1");
g_assert_cmpstr(result[1], ==, "172.16.0.2");
g_assert_cmpstr(result[2], ==, "172.16.0.3");
g_assert_cmpstr(result[3], ==, "172.16.0.4");
nm_clear_g_free(&result);
g_object_unref(conn);
}
/* Route gateway reachable via a direct route in the setting */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nmtst_setting_ip_config_add_route(s_ip4, "172.16.0.0", 16, NULL, 100);
nmtst_setting_ip_config_add_route(s_ip4, "10.0.0.0", 8, "172.16.0.1", 100);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(!result);
g_object_unref(conn);
}
/* No gateways */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(!result);
g_object_unref(conn);
}
/* Both default gateway and route gateway unreachable */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
NM_SETTING_IP_CONFIG_GATEWAY,
"10.0.0.1",
NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nmtst_setting_ip_config_add_route(s_ip4, "10.0.0.0", 8, "172.16.0.1", 100);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(result);
g_assert_cmpint(g_strv_length((char **) result), ==, 2);
g_assert_cmpstr(result[0], ==, "10.0.0.1");
g_assert_cmpstr(result[1], ==, "172.16.0.1");
nm_clear_g_free(&result);
g_object_unref(conn);
}
/* Test deduplication */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
NM_SETTING_IP_CONFIG_GATEWAY,
"192.168.1.1",
NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nmtst_setting_ip_config_add_route(s_ip4, "10.0.0.0", 16, "172.16.0.1", 100);
nmtst_setting_ip_config_add_route(s_ip4, "10.1.0.0", 16, "172.16.0.1", 100);
nmtst_setting_ip_config_add_route(s_ip4, "10.2.0.0", 16, "172.16.0.1", 100);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(result);
g_assert_cmpint(g_strv_length((char **) result), ==, 1);
g_assert_cmpstr(result[0], ==, "172.16.0.1");
nm_clear_g_free(&result);
g_object_unref(conn);
}
/* IPv6 gateway reachable via address prefix route */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new();
g_object_set(s_ip6,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
NM_SETTING_IP_CONFIG_GATEWAY,
"fd01::1",
NULL);
nmtst_setting_ip_config_add_address(s_ip6, "fd01::10", 64);
nm_connection_add_setting(conn, NM_SETTING(s_ip6));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(!result);
g_object_unref(conn);
}
/* IPv6 gateway NOT reachable */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new();
g_object_set(s_ip6,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
NM_SETTING_IP_CONFIG_GATEWAY,
"fd02::1",
NULL);
nmtst_setting_ip_config_add_address(s_ip6, "fd01::10", 64);
nm_connection_add_setting(conn, NM_SETTING(s_ip6));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(result);
g_assert_cmpint(g_strv_length((char **) result), ==, 1);
g_assert_cmpstr(result[0], ==, "fd02::1");
nm_clear_g_free(&result);
g_object_unref(conn);
}
/* Multiple addresses, gateway reachable via second address */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
NM_SETTING_IP_CONFIG_GATEWAY,
"10.0.0.1",
NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nmtst_setting_ip_config_add_address(s_ip4, "10.0.0.5", 24);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(!result);
g_object_unref(conn);
}
/* Unreachable gateways in both IPv4 and IPv6 */
{
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
NM_SETTING_IP_CONFIG_GATEWAY,
"10.0.0.1",
NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new();
g_object_set(s_ip6,
NM_SETTING_IP_CONFIG_METHOD,
NM_SETTING_IP6_CONFIG_METHOD_MANUAL,
NM_SETTING_IP_CONFIG_GATEWAY,
"fd02::1",
NULL);
nmtst_setting_ip_config_add_address(s_ip6, "fd01::10", 64);
nm_connection_add_setting(conn, NM_SETTING(s_ip6));
result = nm_connection_get_unreachable_gateways(conn);
g_assert(result);
g_assert_cmpint(g_strv_length((char **) result), ==, 2);
g_assert_cmpstr(result[0], ==, "10.0.0.1");
g_assert_cmpstr(result[1], ==, "fd02::1");
nm_clear_g_free(&result);
g_object_unref(conn);
}
/* Onlink and IPv6-link-local routes */
{
NMIPRoute *route;
conn =
nmtst_create_minimal_connection("test-ugw", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new();
g_object_set(s_ip4, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL, NULL);
nmtst_setting_ip_config_add_address(s_ip4, "192.168.1.5", 24);
route = nm_ip_route_new(AF_INET, "10.0.0.1", 8, "192.168.20.1", 100, NULL);
g_assert(route);
nm_ip_route_set_attribute(route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean(TRUE));
g_assert(nm_setting_ip_config_add_route(s_ip4, route));
nm_ip_route_unref(route);
nm_connection_add_setting(conn, NM_SETTING(s_ip4));
s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new();
g_object_set(s_ip6, NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_MANUAL, NULL);
nmtst_setting_ip_config_add_address(s_ip6, "fd01::10", 64);
nm_connection_add_setting(conn, NM_SETTING(s_ip6));
route = nm_ip_route_new(AF_INET6, "fd02::", 64, "fd03::1111", 100, NULL);
g_assert(route);
nm_ip_route_set_attribute(route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean(TRUE));
g_assert(nm_setting_ip_config_add_route(s_ip6, route));
nm_ip_route_unref(route);
nmtst_setting_ip_config_add_route(s_ip6, "fd04::", 64, "fe80::1111", 100);
result = nm_connection_get_unreachable_gateways(conn);
g_assert(!result);
g_object_unref(conn);
}
}
/*****************************************************************************/
NMTST_DEFINE();
int
@ -11963,6 +12285,7 @@ main(int argc, char **argv)
g_test_add_func("/core/general/test_dns_uri_get_legacy", test_dns_uri_parse_plain);
g_test_add_func("/core/general/test_dns_uri_normalize", test_dns_uri_normalize);
g_test_add_func("/core/general/test_dhcp_iaid_hexstr", test_dhcp_iaid_hexstr);
g_test_add_func("/core/general/test_unreachable_gateways", test_unreachable_gateways);
return g_test_run();
}

View file

@ -5645,6 +5645,7 @@ connection_warnings(NmCli *nmc, NMConnection *connection)
guint i, found;
const char *id;
const char *deprecated;
gs_free char *gw_warning = NULL;
deprecated = nmc_connection_check_deprecated(NM_CONNECTION(connection));
if (deprecated)
@ -5673,6 +5674,10 @@ connection_warnings(NmCli *nmc, NMConnection *connection)
nm_connection_get_uuid(NM_CONNECTION(connection)),
found);
}
gw_warning = nm_connection_get_unreachable_gateways_warning(connection, TRUE);
if (gw_warning)
nmc_printerr("Warning: %s.\n", gw_warning);
}
static void

View file

@ -14,6 +14,7 @@
#include "nmt-editor.h"
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#include "nm-utils.h"
#include "nmtui.h"
@ -153,10 +154,16 @@ save_connection_and_exit(NmtNewtButton *button, gpointer user_data)
NmtEditor *editor = user_data;
NmtEditorPrivate *priv = NMT_EDITOR_GET_PRIVATE(editor);
NmtSyncOp op;
GError *error = NULL;
GError *error = NULL;
gs_free char *gw_warning = NULL;
nm_connection_replace_settings_from_connection(priv->orig_connection, priv->edit_connection);
gw_warning = nm_connection_get_unreachable_gateways_warning(priv->orig_connection, TRUE);
if (gw_warning) {
nmt_newt_message_dialog(_("Warning: %s"), gw_warning);
}
nmt_sync_op_init(&op);
if (NM_IS_REMOTE_CONNECTION(priv->orig_connection)) {
nm_remote_connection_commit_changes_async(NM_REMOTE_CONNECTION(priv->orig_connection),

View file

@ -0,0 +1,15 @@
size: 683
location: src/tests/client/test-client.py:test_005()/1
cmd: $NMCLI c add type ethernet ifname eth0 con-name con-xx1 ipv4.method manual ipv4.addresses 192.168.1.1/24 ipv4.gateway 192.168.2.1 ipv4.routes '192.168.4.4 192.168.4.1'
lang: C
returncode: 0
stdout: 80 bytes
>>>
Connection 'con-xx1' (UUID-con-xx1-REPLACED-REPLACED-REPLA) successfully added.
<<<
stderr: 300 bytes
>>>
Warning: the following gateways are not directly reachable from any configured address or route: 192.168.2.1, 192.168.4.1. NetworkManager currently adds on-link routes for them automatically, but this will change in the future. Consider adding addresses or routes whose subnets cover these gateways.
<<<

View file

@ -2222,6 +2222,41 @@ class TestNmcli(unittest.TestCase):
replace_stdout=replace_uuids,
)
@nm_test
def test_005(self):
self.init_001()
replace_uuids = []
replace_uuids.append(
self.ctx.srv.ReplaceTextConUuid(
"con-xx1", "UUID-con-xx1-REPLACED-REPLACED-REPLA"
)
)
# Check the warning about unreachable gateways
self.call_nmcli(
[
"c",
"add",
"type",
"ethernet",
"ifname",
"eth0",
"con-name",
"con-xx1",
"ipv4.method",
"manual",
"ipv4.addresses",
"192.168.1.1/24",
"ipv4.gateway",
"192.168.2.1",
"ipv4.routes",
"192.168.4.4 192.168.4.1",
],
replace_stdout=replace_uuids,
)
@nm_test_no_dbus
def test_offline(self):
# Make sure we're not using D-Bus