all: allow configuring default-routes as manual, static routes

Up until now, a default-route (with prefix length zero) could not
be configured directly. The user could only set ipv4.gateway,
ipv4.never-default, ipv4.route-metric and ipv4.route-table to influence
the setting of the default-route (respectively for IPv6).

That is a problematic limitation. For one, whether a route has prefix
length zero or non-zero does not make a fundamental difference. Also,
it makes it impossible to configure all the routing attributes that one can
configure otherwise for static routes. For example, the default-route could
not be configured as "onlink", could not have a special MTU, nor could it be
placed in a dedicated routing table.

Fix that by lifting the restriction. Note that "ipv4.never-default" does
not apply to /0 manual routes. Likewise, the previous manners of
configuring default-routes ("ipv4.gateway") don't conflict with manual
default-routes.

Server-side this all the pieces are already in place to accept a default-route
as static routes. This was done by earlier commits like 5c299454b4
('core: rework tracking of gateway/default-route in ip-config').

A long time ago, NMIPRoute would assert that the prefix length is
positive. That was relaxed by commit a2e93f2de4 ('libnm: allow zero
prefix length for NMIPRoute'), already before 1.0.0. Using libnm from
before 1.0.0 would result in assertion failures.

Note that the default-route-metric-penalty based on connectivity
checking applies to all /0 routes, even these static routes. Be they
added due to DHCP, "ipv4.gateway", "ipv4.routes" or "wireguard.peer-routes".
I wonder whether doing that unconditionally is desirable, and maybe
there should be a way to opt-out/opt-in for the entire profile or even
per-routes.

https://bugzilla.redhat.com/show_bug.cgi?id=1714438
This commit is contained in:
Thomas Haller
2019-08-03 08:15:50 +02:00
parent 539db43619
commit c167e0140b
7 changed files with 33 additions and 40 deletions

View File

@@ -367,7 +367,7 @@ _parse_ip_route (int family,
}
prefix = MAX_PREFIX;
if (plen) {
if ((prefix = _nm_utils_ascii_str_to_int64 (plen, 10, 1, MAX_PREFIX, -1)) == -1) {
if ((prefix = _nm_utils_ascii_str_to_int64 (plen, 10, 0, MAX_PREFIX, -1)) == -1) {
g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_INVALID_ARGUMENT,
_("invalid prefix '%s'; <1-%d> allowed"),
plen, MAX_PREFIX);

View File

@@ -248,15 +248,16 @@ static gpointer
build_route (KeyfileReaderInfo *info,
const char *property_name,
int family,
const char *dest_str, guint32 plen,
const char *gateway_str, const char *metric_str)
const char *dest_str,
guint32 plen,
const char *gateway_str,
const char *metric_str)
{
NMIPRoute *route;
guint32 u32;
gint64 metric = -1;
GError *error = NULL;
g_return_val_if_fail (plen, NULL);
g_return_val_if_fail (dest_str, NULL);
/* Next hop */
@@ -294,7 +295,10 @@ build_route (KeyfileReaderInfo *info,
metric = u32;
}
route = nm_ip_route_new (family, dest_str, plen, gateway_str,
route = nm_ip_route_new (family,
dest_str,
plen,
gateway_str,
metric,
&error);
if (!route) {
@@ -517,8 +521,7 @@ read_one_ip_address_or_route (KeyfileReaderInfo *info,
/* parse plen, fallback to defaults */
if (plen_str) {
if ( !get_one_int (info, property_name, plen_str, ipv6 ? 128 : 32, &plen)
|| (route && plen == 0)) {
if (!get_one_int (info, property_name, plen_str, ipv6 ? 128 : 32, &plen)) {
plen = DEFAULT_PREFIX (route, ipv6);
if ( info->error
|| !handle_warn (info, property_name, NM_KEYFILE_WARN_SEVERITY_WARN,
@@ -536,12 +539,19 @@ read_one_ip_address_or_route (KeyfileReaderInfo *info,
/* build the appropriate data structure for NetworkManager settings */
if (route) {
result = build_route (info, property_name,
result = build_route (info,
property_name,
ipv6 ? AF_INET6 : AF_INET,
address_str, plen, gateway_str, metric_str);
address_str,
plen,
gateway_str,
metric_str);
} else {
result = build_address (info, ipv6 ? AF_INET6 : AF_INET,
address_str, plen, property_name);
result = build_address (info,
ipv6 ? AF_INET6 : AF_INET,
address_str,
plen,
property_name);
if (!result)
return NULL;
if (gateway_str)

View File

@@ -1335,7 +1335,7 @@ nm_ip_route_attribute_validate (const char *name,
if (str) {
addr = nm_strndup_a (200, addr, str - addr, &addr_free);
str++;
if (_nm_utils_ascii_str_to_int64 (str, 10, 1, family == AF_INET ? 32 : 128, -1) < 0) {
if (_nm_utils_ascii_str_to_int64 (str, 10, 0, family == AF_INET ? 32 : 128, -1) < 0) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_FAILED,
@@ -5005,15 +5005,6 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_IP_CONFIG_ROUTES);
return FALSE;
}
if (nm_ip_route_get_prefix (route) == 0) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%d. route cannot be a default route"),
(int) (i + 1));
g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_IP_CONFIG_ROUTES);
return FALSE;
}
}
if (priv->routing_rules) {

View File

@@ -991,8 +991,6 @@ nm_ip4_config_merge_setting (NMIP4Config *self,
route.plen = nm_ip_route_get_prefix (s_route);
nm_assert (route.plen <= 32);
if (route.plen == 0)
continue;
nm_ip_route_get_next_hop_binary (s_route, &route.gateway);
if (nm_ip_route_get_metric (s_route) == -1)

View File

@@ -672,8 +672,6 @@ nm_ip6_config_merge_setting (NMIP6Config *self,
route.plen = nm_ip_route_get_prefix (s_route);
nm_assert (route.plen <= 128);
if (route.plen == 0)
continue;
nm_ip_route_get_next_hop_binary (s_route, &route.gateway);
if (nm_ip_route_get_metric (s_route) == -1)

View File

@@ -1116,15 +1116,6 @@ next:
? info_to->v.addr.plen
: (addr_family == AF_INET ? 32 : 128);
if ( ( (addr_family == AF_INET && !info_to->v.addr.addr.addr4)
|| (addr_family == AF_INET6 && IN6_IS_ADDR_UNSPECIFIED (&info_to->v.addr.addr.addr6)))
&& prefix == 0) {
/* we ignore default routes by returning -ERANGE. */
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Ignore manual default route");
return -ERANGE;
}
route = nm_ip_route_new_binary (addr_family,
&info_to->v.addr.addr,
prefix,
@@ -1244,7 +1235,7 @@ read_one_ip4_route (shvarFile *ifcfg,
return FALSE;
if (has_key) {
prefix = nm_utils_ip4_netmask_to_prefix (netmask);
if (prefix == 0 || netmask != _nm_utils_ip4_prefix_to_netmask (prefix)) {
if (netmask != _nm_utils_ip4_prefix_to_netmask (prefix)) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid IP4 netmask '%s' \"%s\"", netmask_tag, nm_utils_inet4_ntop (netmask, inet_buf));
return FALSE;

View File

@@ -1520,10 +1520,8 @@ test_read_wired_ipv6_manual (void)
NMIPAddress *ip6_addr;
NMIPRoute *ip6_route;
NMTST_EXPECT_NM_WARN ("*ignoring manual default route*");
connection = _connection_from_file (TEST_IFCFG_DIR"/ifcfg-test-wired-ipv6-manual",
NULL, TYPE_ETHERNET, &unmanaged);
g_test_assert_expected_messages ();
g_assert (!unmanaged);
/* ===== CONNECTION SETTING ===== */
@@ -1581,7 +1579,7 @@ test_read_wired_ipv6_manual (void)
g_assert_cmpint (nm_ip_address_get_prefix (ip6_addr), ==, 96);
/* Routes */
g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 3);
g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip6), ==, 4);
/* Route #1 */
ip6_route = nm_setting_ip_config_get_route (s_ip6, 0);
g_assert (ip6_route);
@@ -1592,12 +1590,19 @@ test_read_wired_ipv6_manual (void)
/* Route #2 */
ip6_route = nm_setting_ip_config_get_route (s_ip6, 1);
g_assert (ip6_route);
g_assert_cmpstr (nm_ip_route_get_dest (ip6_route), ==, "::");
g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 0);
g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, "dead::beaf");
g_assert_cmpint (nm_ip_route_get_metric (ip6_route), ==, -1);
/* Route #3 */
ip6_route = nm_setting_ip_config_get_route (s_ip6, 2);
g_assert (ip6_route);
g_assert_cmpstr (nm_ip_route_get_dest (ip6_route), ==, "abbe::cafe");
g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64);
g_assert_cmpstr (nm_ip_route_get_next_hop (ip6_route), ==, NULL);
g_assert_cmpint (nm_ip_route_get_metric (ip6_route), ==, 777);
/* Route #3 */
ip6_route = nm_setting_ip_config_get_route (s_ip6, 2);
/* Route #4 */
ip6_route = nm_setting_ip_config_get_route (s_ip6, 3);
g_assert (ip6_route);
g_assert_cmpstr (nm_ip_route_get_dest (ip6_route), ==, "aaaa::cccc");
g_assert_cmpint (nm_ip_route_get_prefix (ip6_route), ==, 64);