all: support route-attribute "onlink" for IPv4

Kernel doesn't support it for IPv6.

This is especially useful, if you combine static routes
with DHCP. In that case, you might want to get the device-route
to the gateway automatically, but add a static-route for it.
This commit is contained in:
Thomas Haller 2017-11-07 19:35:46 +01:00
parent 88a40f960c
commit 0ed49717ab
9 changed files with 140 additions and 48 deletions

View file

@ -2104,6 +2104,7 @@ EXTRA_DIST += \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_LEAP.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wifi_WEP_104_ASCII.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Auto-Negotiate.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Wired_Wake-on-LAN.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Vlan_test-vlan-interface.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-dcb-test.cexpected \

View file

@ -1245,6 +1245,7 @@ static const NMVariantAttributeSpec * const ip_route_attribute_spec[] = {
ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_SRC, G_VARIANT_TYPE_STRING, TRUE, TRUE, 'a'),
ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_FROM, G_VARIANT_TYPE_STRING, FALSE, TRUE, 'p'),
ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_TOS, G_VARIANT_TYPE_BYTE, TRUE, FALSE, 0 ),
ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_ONLINK, G_VARIANT_TYPE_BOOLEAN, TRUE, FALSE, 0 ),
ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ),
ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_CWND, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ),
ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_INITCWND, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ),

View file

@ -146,6 +146,7 @@ gboolean nm_ip_route_attribute_validate (const char *name,
#define NM_IP_ROUTE_ATTRIBUTE_SRC "src"
#define NM_IP_ROUTE_ATTRIBUTE_FROM "from"
#define NM_IP_ROUTE_ATTRIBUTE_TOS "tos"
#define NM_IP_ROUTE_ATTRIBUTE_ONLINK "onlink"
#define NM_IP_ROUTE_ATTRIBUTE_WINDOW "window"
#define NM_IP_ROUTE_ATTRIBUTE_CWND "cwnd"
#define NM_IP_ROUTE_ATTRIBUTE_INITCWND "initcwnd"

View file

@ -817,6 +817,7 @@ _nm_ip_config_merge_route_attributes (int addr_family,
NMIPAddr addr;
NMPlatformIP4Route *r4 = (NMPlatformIP4Route *) r;
NMPlatformIP6Route *r6 = (NMPlatformIP6Route *) r;
gboolean onlink;
nm_assert (s_route);
nm_assert_addr_family (addr_family);
@ -836,8 +837,15 @@ _nm_ip_config_merge_route_attributes (int addr_family,
GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TABLE, table, UINT32, uint32, 0);
r->table_coerced = nm_platform_route_table_coerce (table ?: (route_table ?: RT_TABLE_MAIN));
if (addr_family == AF_INET)
if (addr_family == AF_INET) {
GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_TOS, r4->tos, BYTE, byte, 0);
GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_ONLINK, onlink, BOOLEAN, boolean, FALSE);
} else
onlink = FALSE;
r->r_rtm_flags = 0;
if (onlink)
r->r_rtm_flags = RTNH_F_ONLINK;
GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_WINDOW, r->window, UINT32, uint32, 0);
GET_ATTR (NM_IP_ROUTE_ATTRIBUTE_CWND, r->cwnd, UINT32, uint32, 0);

View file

@ -512,13 +512,13 @@ typedef struct {
bool int_base_16:1;
/* the type, one of PARSE_LINE_TYPE_* */
char type;
/* whether the command line option was found, and @v is
* initialized. */
bool has:1;
/* the type, one of PARSE_LINE_TYPE_* */
char type;
union {
guint8 uint8;
guint32 uint32;
@ -541,6 +541,7 @@ enum {
PARSE_LINE_ATTR_ROUTE_SRC,
PARSE_LINE_ATTR_ROUTE_FROM,
PARSE_LINE_ATTR_ROUTE_TOS,
PARSE_LINE_ATTR_ROUTE_ONLINK,
PARSE_LINE_ATTR_ROUTE_WINDOW,
PARSE_LINE_ATTR_ROUTE_CWND,
PARSE_LINE_ATTR_ROUTE_INITCWND,
@ -562,6 +563,7 @@ enum {
#define PARSE_LINE_TYPE_ADDR 'a'
#define PARSE_LINE_TYPE_ADDR_WITH_PREFIX 'p'
#define PARSE_LINE_TYPE_IFNAME 'i'
#define PARSE_LINE_TYPE_FLAG 'f'
/**
* parse_route_line:
@ -601,42 +603,45 @@ parse_route_line (const char *line,
char buf1[256];
char buf2[256];
ParseLineInfo infos[] = {
[PARSE_LINE_ATTR_ROUTE_TABLE] = { .key = NM_IP_ROUTE_ATTRIBUTE_TABLE,
.type = PARSE_LINE_TYPE_UINT32, },
[PARSE_LINE_ATTR_ROUTE_SRC] = { .key = NM_IP_ROUTE_ATTRIBUTE_SRC,
.type = PARSE_LINE_TYPE_ADDR, },
[PARSE_LINE_ATTR_ROUTE_FROM] = { .key = NM_IP_ROUTE_ATTRIBUTE_FROM,
.type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
.disabled = (addr_family != AF_INET6), },
[PARSE_LINE_ATTR_ROUTE_TOS] = { .key = NM_IP_ROUTE_ATTRIBUTE_TOS,
.type = PARSE_LINE_TYPE_UINT8,
.int_base_16 = TRUE,
.ignore = (addr_family != AF_INET), },
[PARSE_LINE_ATTR_ROUTE_WINDOW] = { .key = NM_IP_ROUTE_ATTRIBUTE_WINDOW,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_CWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_CWND,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_INITCWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITCWND,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_INITRWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITRWND,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_MTU] = { .key = NM_IP_ROUTE_ATTRIBUTE_MTU,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_TABLE] = { .key = NM_IP_ROUTE_ATTRIBUTE_TABLE,
.type = PARSE_LINE_TYPE_UINT32, },
[PARSE_LINE_ATTR_ROUTE_SRC] = { .key = NM_IP_ROUTE_ATTRIBUTE_SRC,
.type = PARSE_LINE_TYPE_ADDR, },
[PARSE_LINE_ATTR_ROUTE_FROM] = { .key = NM_IP_ROUTE_ATTRIBUTE_FROM,
.type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
.disabled = (addr_family != AF_INET6), },
[PARSE_LINE_ATTR_ROUTE_TOS] = { .key = NM_IP_ROUTE_ATTRIBUTE_TOS,
.type = PARSE_LINE_TYPE_UINT8,
.int_base_16 = TRUE,
.ignore = (addr_family != AF_INET), },
[PARSE_LINE_ATTR_ROUTE_ONLINK] = { .key = NM_IP_ROUTE_ATTRIBUTE_ONLINK,
.type = PARSE_LINE_TYPE_FLAG,
.ignore = (addr_family != AF_INET), },
[PARSE_LINE_ATTR_ROUTE_WINDOW] = { .key = NM_IP_ROUTE_ATTRIBUTE_WINDOW,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_CWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_CWND,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_INITCWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITCWND,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_INITRWND] = { .key = NM_IP_ROUTE_ATTRIBUTE_INITRWND,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_MTU] = { .key = NM_IP_ROUTE_ATTRIBUTE_MTU,
.type = PARSE_LINE_TYPE_UINT32_WITH_LOCK, },
[PARSE_LINE_ATTR_ROUTE_TO] = { .key = "to",
.type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
.disabled = (options_route != NULL), },
[PARSE_LINE_ATTR_ROUTE_VIA] = { .key = "via",
.type = PARSE_LINE_TYPE_ADDR,
.disabled = (options_route != NULL), },
[PARSE_LINE_ATTR_ROUTE_METRIC] = { .key = "metric",
.type = PARSE_LINE_TYPE_UINT32,
.disabled = (options_route != NULL), },
[PARSE_LINE_ATTR_ROUTE_TO] = { .key = "to",
.type = PARSE_LINE_TYPE_ADDR_WITH_PREFIX,
.disabled = (options_route != NULL), },
[PARSE_LINE_ATTR_ROUTE_VIA] = { .key = "via",
.type = PARSE_LINE_TYPE_ADDR,
.disabled = (options_route != NULL), },
[PARSE_LINE_ATTR_ROUTE_METRIC] = { .key = "metric",
.type = PARSE_LINE_TYPE_UINT32,
.disabled = (options_route != NULL), },
[PARSE_LINE_ATTR_ROUTE_DEV] = { .key = "dev",
.type = PARSE_LINE_TYPE_IFNAME,
.ignore = TRUE,
.disabled = (options_route != NULL), },
[PARSE_LINE_ATTR_ROUTE_DEV] = { .key = "dev",
.type = PARSE_LINE_TYPE_IFNAME,
.ignore = TRUE,
.disabled = (options_route != NULL), },
};
nm_assert (line);
@ -705,6 +710,9 @@ parse_route_line (const char *line,
case PARSE_LINE_TYPE_IFNAME:
i_words++;
goto parse_line_type_ifname;
case PARSE_LINE_TYPE_FLAG:
i_words++;
goto next;
default:
nm_assert_not_reached ();
}
@ -913,6 +921,15 @@ next:
? nm_sprintf_buf (buf2, "/%u", (unsigned) info->v.addr.plen)
: ""));
break;
case PARSE_LINE_TYPE_FLAG:
/* XXX: the flag (for "onlink") only allows to explictly set "TRUE".
* There is no way to express an explicit "FALSE" setting
* of this attribute, hence, the file format cannot encode
* that configuration. */
nm_ip_route_set_attribute (route,
info->key,
g_variant_new_boolean (TRUE));
break;
default:
nm_assert_not_reached ();
break;

View file

@ -1928,8 +1928,11 @@ get_route_attributes_string (NMIPRoute *route, int family)
g_string_append_printf (str, "%s 0x%02x", names[i], (unsigned) g_variant_get_byte (attr));
} else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_TABLE)) {
g_string_append_printf (str, "%s %u", names[i], (unsigned) g_variant_get_uint32 (attr));
} else if ( nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC)
|| nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_FROM)) {
} else if (nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_ONLINK)) {
if (g_variant_get_boolean (attr))
g_string_append (str, "onlink");
} else if (NM_IN_STRSET (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC,
NM_IP_ROUTE_ATTRIBUTE_FROM)) {
char *arg = nm_streq (names[i], NM_IP_ROUTE_ATTRIBUTE_SRC) ? "src" : "from";
g_string_append_printf (str, "%s %s", arg, g_variant_get_string (attr, NULL));

View file

@ -0,0 +1,20 @@
HWADDR=31:33:33:37:BE:CD
MTU=1492
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=none
IPADDR=1.1.1.3
PREFIX=24
IPADDR1=1.1.1.5
PREFIX1=24
GATEWAY=1.1.1.1
DNS1=4.2.2.1
DNS2=4.2.2.2
DOMAIN="foobar.com lab.foobar.com"
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=no
NAME="Test Write Wired Static Routes"
UUID=${UUID}
ONBOOT=yes

View file

@ -7,3 +7,9 @@ NETMASK1=255.255.255.255
GATEWAY1=192.168.1.7
METRIC1=3
OPTIONS1="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 window 30000 initcwnd lock 13 initrwnd 14"
ADDRESS2=44.55.66.78
NETMASK2=255.255.255.255
GATEWAY2=192.168.1.8
METRIC2=3
OPTIONS2="mtu lock 9000 cwnd 12 src 1.1.1.1 tos 0x28 onlink window 30000 initcwnd lock 13 initrwnd 14"

View file

@ -1318,7 +1318,7 @@ test_read_wired_static_routes (void)
g_assert_cmpstr (nm_setting_ip_config_get_method (s_ip4), ==, NM_SETTING_IP4_CONFIG_METHOD_MANUAL);
/* Routes */
g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 2);
g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 3);
ip4_route = nm_setting_ip_config_get_route (s_ip4, 0);
g_assert (ip4_route);
@ -1343,6 +1343,23 @@ test_read_wired_static_routes (void)
nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE);
nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.1.1.1");
ip4_route = nm_setting_ip_config_get_route (s_ip4, 2);
g_assert (ip4_route);
g_assert_cmpstr (nm_ip_route_get_dest (ip4_route), ==, "44.55.66.78");
g_assert_cmpint (nm_ip_route_get_prefix (ip4_route), ==, 32);
g_assert_cmpstr (nm_ip_route_get_next_hop (ip4_route), ==, "192.168.1.8");
g_assert_cmpint (nm_ip_route_get_metric (ip4_route), ==, 3);
nmtst_assert_route_attribute_byte (ip4_route, NM_IP_ROUTE_ATTRIBUTE_TOS, 0x28);
nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, 30000);
nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_CWND, 12);
nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITCWND, 13);
nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_INITRWND, 14);
nmtst_assert_route_attribute_uint32 (ip4_route, NM_IP_ROUTE_ATTRIBUTE_MTU, 9000);
nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, TRUE);
nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, TRUE);
nmtst_assert_route_attribute_string (ip4_route, NM_IP_ROUTE_ATTRIBUTE_SRC, "1.1.1.1");
nmtst_assert_route_attribute_boolean (ip4_route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, TRUE);
g_object_unref (connection);
}
@ -4748,6 +4765,7 @@ test_write_wired_static_routes (void)
NMIPAddress *addr;
NMIPRoute *route;
GError *error = NULL;
gboolean reread_same = FALSE;
connection = nm_simple_connection_new ();
@ -4792,11 +4810,15 @@ test_write_wired_static_routes (void)
/* Write out routes */
route = nm_ip_route_new (AF_INET, "1.2.3.0", 24, "222.173.190.239", 0, &error);
nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, g_variant_new_uint32 (3455));
nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean (TRUE));
g_assert_no_error (error);
nm_setting_ip_config_add_route (s_ip4, route);
nm_ip_route_unref (route);
route = nm_ip_route_new (AF_INET, "3.2.1.0", 24, "202.254.186.190", 77, &error);
nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_WINDOW, g_variant_new_uint32 (30000));
nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean (FALSE));
g_assert_no_error (error);
nm_setting_ip_config_add_route (s_ip4, route);
nm_ip_route_unref (route);
@ -4818,15 +4840,28 @@ test_write_wired_static_routes (void)
nmtst_assert_connection_verifies (connection);
_writer_new_connection (connection,
TEST_SCRATCH_DIR "/network-scripts/",
&testfile);
reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
routefile = utils_get_route_path (testfile);
_writer_new_connection_reread (connection,
TEST_SCRATCH_DIR "/network-scripts/",
&testfile,
TEST_IFCFG_DIR "/network-scripts/ifcfg-Test_Write_Wired_Static_Routes.cexpected",
&reread,
&reread_same);
/* ifcfg does not support setting onlink=0. It gets lost during write+re-read.
* Assert that it's missing, and patch it to check whether the rest of the
* connection equals. */
g_assert (!reread_same);
nmtst_assert_connection_verifies_without_normalization (reread);
s_ip4 = nm_connection_get_setting_ip4_config (reread);
g_assert (s_ip4);
g_assert_cmpint (nm_setting_ip_config_get_num_routes (s_ip4), ==, 2);
route = nm_setting_ip_config_get_route (s_ip4, 1);
g_assert (route);
g_assert (!nm_ip_route_get_attribute (route, NM_IP_ROUTE_ATTRIBUTE_ONLINK));
nm_ip_route_set_attribute (route, NM_IP_ROUTE_ATTRIBUTE_ONLINK, g_variant_new_boolean (FALSE));
nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
routefile = utils_get_route_path (testfile);
}
static void