ifcfg-rh: add support for routing rules as "ROUTING_RULE_#" keys

initscripts support rule-* and rule6-* files for that.

Up until now, we ignored these files for the most part, except if
a user configured such files, the profile could not contain any static
routes (or specify a route-table setting). This also worked together
with the dispatcher script "examples/dispatcher/10-ifcfg-rh-routes.sh".

We cannot now start taking over that file format for rules. It might
break existing setups, because we can never fully understand all rules as
they are understood by iproute2. Also, if a user has a rule/rule6 file and
uses NetworkManager successfully today, then clearly there is a script
in place to make that work. We must not break that when adding rules
support.

Hence, store routing rules as numbered "ROUTING_RULE_#" and
"ROUTING_RULE6_#" keys.

Note that we use different keys for IPv4 and IPv6. The main reason is
that the string format is mostly compatible with iproute2. That means,
you can take the value and pass it to `ip rule add`.
However, `ip rule add` only accepts IPv4 rules. For IPv6 rules, the user
needs to call `ip -6 rule add`. If we would use the same key for IPv4
and IPv6, then it would be hard to write a script to do this.
Also, nm_ip_routing_rule_from_string() does take the address family as
hint in this case. This makes

  ROUTING_RULE_1="pref 1"
  ROUTING_RULE6_1="pref 1"

automatically determine that address families. Otherwise, such
abbreviated forms would be not valid.
This commit is contained in:
Thomas Haller 2019-03-22 20:13:15 +01:00
parent 6e6d1e070c
commit 4d46804437
7 changed files with 346 additions and 12 deletions

View file

@ -2729,6 +2729,7 @@ EXTRA_DIST += \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Bridge_Component.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Permissions.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Proxy_Basic.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Infiniband_Port.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Team_Port.cexpected \
src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_VLAN_reorder_hdr.cexpected \

View file

@ -2083,7 +2083,7 @@ make_ip6_setting (shvarFile *ifcfg,
g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_HOSTNAME, v, NULL);
g_object_set (s_ip6, NM_SETTING_IP_CONFIG_DHCP_SEND_HOSTNAME,
svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL);
svGetValueBoolean (ifcfg, "DHCPV6_SEND_HOSTNAME", TRUE), NULL);
/* Read static IP addresses.
* Read them even for AUTO and DHCP methods - in this case the addresses are
@ -4316,6 +4316,84 @@ parse_ethtool_option (const char *value,
}
}
static GPtrArray *
read_routing_rules_parse (shvarFile *ifcfg,
gboolean routes_read)
{
gs_unref_ptrarray GPtrArray *arr = NULL;
gs_free const char **keys = NULL;
guint i, len;
keys = svGetKeysSorted (ifcfg, SV_KEY_TYPE_ROUTING_RULE4 | SV_KEY_TYPE_ROUTING_RULE6, &len);
if (len == 0)
return NULL;
if (!routes_read) {
PARSE_WARNING ("'rule-' or 'rule6-' files are present; Policy routing rules (ROUTING_RULE*) settings are ignored");
return NULL;
}
arr = g_ptr_array_new_full (len, (GDestroyNotify) nm_ip_routing_rule_unref);
for (i = 0; i < len; i++) {
const char *key = keys[i];
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL;
gs_free_error GError *local = NULL;
gs_free char *value_to_free = NULL;
const char *value;
gboolean key_is_ipv4;
key_is_ipv4 = (key[NM_STRLEN ("ROUTING_RULE")] == '_');
nm_assert ( key_is_ipv4 == NM_STR_HAS_PREFIX (key, "ROUTING_RULE_"));
nm_assert (!key_is_ipv4 == NM_STR_HAS_PREFIX (key, "ROUTING_RULE6_"));
value = svGetValueStr (ifcfg, key, &value_to_free);
if (!value)
continue;
rule = nm_ip_routing_rule_from_string (value,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| (key_is_ipv4
? NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET
: NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6),
NULL,
&local);
if (!rule) {
PARSE_WARNING ("invalid routing rule %s=\"%s\": %s", key, value, local->message);
continue;
}
g_ptr_array_add (arr, g_steal_pointer (&rule));
}
if (arr->len == 0)
return NULL;
return g_steal_pointer (&arr);
}
static void
read_routing_rules (shvarFile *ifcfg,
gboolean routes_read,
NMSettingIPConfig *s_ip4,
NMSettingIPConfig *s_ip6)
{
gs_unref_ptrarray GPtrArray *routing_rules = NULL;
guint i;
routing_rules = read_routing_rules_parse (ifcfg, routes_read);
if (!routing_rules)
return;
for (i = 0; i < routing_rules->len; i++) {
NMIPRoutingRule *rule = routing_rules->pdata[i];
nm_setting_ip_config_add_routing_rule ( (nm_ip_routing_rule_get_addr_family (rule) == AF_INET)
? s_ip4
: s_ip6,
rule);
}
}
static void
parse_ethtool_options (shvarFile *ifcfg, NMConnection *connection)
{
@ -5817,8 +5895,7 @@ connection_from_file_full (const char *filename,
error);
if (!s_ip6)
return NULL;
else
nm_connection_add_setting (connection, s_ip6);
nm_connection_add_setting (connection, s_ip6);
s_ip4 = make_ip4_setting (main_ifcfg,
network_ifcfg,
@ -5827,12 +5904,15 @@ connection_from_file_full (const char *filename,
error);
if (!s_ip4)
return NULL;
else {
read_aliases (NM_SETTING_IP_CONFIG (s_ip4),
!has_ip4_defroute && !nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (s_ip4)),
filename);
nm_connection_add_setting (connection, s_ip4);
}
read_aliases (NM_SETTING_IP_CONFIG (s_ip4),
!has_ip4_defroute && !nm_setting_ip_config_get_gateway (NM_SETTING_IP_CONFIG (s_ip4)),
filename);
nm_connection_add_setting (connection, s_ip4);
read_routing_rules (main_ifcfg,
!has_complex_routes_v4 && !has_complex_routes_v6,
NM_SETTING_IP_CONFIG (s_ip4),
NM_SETTING_IP_CONFIG (s_ip6));
s_sriov = make_sriov_setting (main_ifcfg);
if (s_sriov)

View file

@ -2954,6 +2954,52 @@ write_ip6_setting (NMConnection *connection,
return TRUE;
}
static void
write_ip_routing_rules (NMConnection *connection,
shvarFile *ifcfg,
gboolean route_ignore)
{
gsize idx;
int is_ipv4;
svUnsetAll (ifcfg, SV_KEY_TYPE_ROUTING_RULE4 | SV_KEY_TYPE_ROUTING_RULE6);
if (route_ignore)
return;
idx = 0;
for (is_ipv4 = 1; is_ipv4 >= 0; is_ipv4--) {
const int addr_family = is_ipv4 ? AF_INET : AF_INET6;
NMSettingIPConfig *s_ip;
guint i, num;
s_ip = nm_connection_get_setting_ip_config (connection, addr_family);
if (!s_ip)
continue;
num = nm_setting_ip_config_get_num_routing_rules (s_ip);
for (i = 0; i < num; i++) {
NMIPRoutingRule *rule = nm_setting_ip_config_get_routing_rule (s_ip, i);
gs_free const char *s = NULL;
char key[64];
s = nm_ip_routing_rule_to_string (rule,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE,
NULL,
NULL);
if (!s)
continue;
if (is_ipv4)
numbered_tag (key, "ROUTING_RULE_", ++idx);
else
numbered_tag (key, "ROUTING_RULE6_", ++idx);
svSetValueStr (ifcfg, key, s);
}
}
}
static char *
escape_id (const char *id)
{
@ -3176,6 +3222,15 @@ do_write_construct (NMConnection *connection,
has_complex_routes_v4 ? "" : "6");
return FALSE;
}
if ( ( s_ip4
&& nm_setting_ip_config_get_num_routing_rules (s_ip4) > 0)
|| ( s_ip6
&& nm_setting_ip_config_get_num_routing_rules (s_ip6) > 0)) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_FAILED,
"Cannot configure routing rules on a connection that has an associated 'rule%s-' file",
has_complex_routes_v4 ? "" : "6");
return FALSE;
}
route_ignore = TRUE;
} else
route_ignore = FALSE;
@ -3193,6 +3248,10 @@ do_write_construct (NMConnection *connection,
error))
return FALSE;
write_ip_routing_rules (connection,
ifcfg,
route_ignore);
write_connection_setting (s_con, ifcfg);
NM_SET_OUT (out_ifcfg, g_steal_pointer (&ifcfg));

View file

@ -879,10 +879,25 @@ _is_all_digits (const char *str)
#define IS_NUMBERED_TAG(key, tab_name) \
({ \
const char *_key = (key); \
const char *_key2 = (key); \
\
( (strncmp (_key, tab_name, NM_STRLEN (tab_name)) == 0) \
&& _is_all_digits (&_key[NM_STRLEN (tab_name)])); \
( (strncmp (_key2, tab_name, NM_STRLEN (tab_name)) == 0) \
&& _is_all_digits (&_key2[NM_STRLEN (tab_name)])); \
})
#define IS_NUMBERED_TAG_PARSE(key, tab_name, out_idx) \
({ \
const char *_key = (key); \
gint64 _idx; \
gboolean _good = FALSE; \
gint64 *_out_idx = (out_idx); \
\
if ( IS_NUMBERED_TAG (_key, ""tab_name"") \
&& (_idx = _nm_utils_ascii_str_to_int64 (&_key[NM_STRLEN (tab_name)], 10, 0, G_MAXINT64, -1)) != -1) { \
NM_SET_OUT (_out_idx, _idx); \
_good = TRUE; \
} \
_good; \
})
static gboolean
@ -919,10 +934,30 @@ _svKeyMatchesType (const char *key, SvKeyType match_key_type)
if (IS_NUMBERED_TAG (key, "SRIOV_VF"))
return TRUE;
}
if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE4)) {
if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE_", NULL))
return TRUE;
}
if (NM_FLAGS_HAS (match_key_type, SV_KEY_TYPE_ROUTING_RULE6)) {
if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE6_", NULL))
return TRUE;
}
return FALSE;
}
gint64
svNumberedParseKey (const char *key)
{
gint64 idx;
if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE_", &idx))
return idx;
if (IS_NUMBERED_TAG_PARSE (key, "ROUTING_RULE6_", &idx))
return idx;
return -1;
}
GHashTable *
svGetKeys (shvarFile *s, SvKeyType match_key_type)
{
@ -947,6 +982,42 @@ svGetKeys (shvarFile *s, SvKeyType match_key_type)
return keys;
}
static int
_get_keys_sorted_cmp (gconstpointer a,
gconstpointer b,
gpointer user_data)
{
const char *k_a = *((const char *const*) a);
const char *k_b = *((const char *const*) b);
gint64 n_a;
gint64 n_b;
n_a = svNumberedParseKey (k_a);
n_b = svNumberedParseKey (k_b);
NM_CMP_DIRECT (n_a, n_b);
NM_CMP_RETURN (strcmp (k_a, k_b));
nm_assert_not_reached ();
return 0;
}
const char **
svGetKeysSorted (shvarFile *s,
SvKeyType match_key_type,
guint *out_len)
{
gs_unref_hashtable GHashTable *keys_hash = NULL;
keys_hash = svGetKeys (s, match_key_type);
if (!keys_hash) {
NM_SET_OUT (out_len, 0);
return NULL;
}
return (const char **) nm_utils_hash_keys_to_array (keys_hash,
_get_keys_sorted_cmp,
NULL,
out_len);
}
/*****************************************************************************/
const char *

View file

@ -40,6 +40,8 @@ typedef enum {
SV_KEY_TYPE_TC = (1LL << 3),
SV_KEY_TYPE_USER = (1LL << 4),
SV_KEY_TYPE_SRIOV_VF = (1LL << 5),
SV_KEY_TYPE_ROUTING_RULE4 = (1LL << 6),
SV_KEY_TYPE_ROUTING_RULE6 = (1LL << 7),
} SvKeyType;
const char *svFileGetName (const shvarFile *s);
@ -67,8 +69,14 @@ char *svGetValueStr_cp (shvarFile *s, const char *key);
int svParseBoolean (const char *value, int def);
gint64 svNumberedParseKey (const char *key);
GHashTable *svGetKeys (shvarFile *s, SvKeyType match_key_type);
const char **svGetKeysSorted (shvarFile *s,
SvKeyType match_key_type,
guint *out_len);
/* return TRUE if <key> resolves to any truth value (e.g. "yes", "y", "true")
* return FALSE if <key> resolves to any non-truth value (e.g. "no", "n", "false")
* return <def> otherwise

View file

@ -0,0 +1,19 @@
TYPE=Ethernet
PROXY_METHOD=none
BROWSER_ONLY=no
BOOTPROTO=dhcp
DEFROUTE=yes
IPV4_FAILURE_FATAL=no
IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_FAILURE_FATAL=no
IPV6_ADDR_GEN_MODE=stable-privacy
ROUTING_RULE_1="priority 10 from 0.0.0.0/0 table 1"
ROUTING_RULE_2="priority 10 to 192.167.8.0/24 table 2"
ROUTING_RULE6_3="priority 10 from ::/0 table 10"
ROUTING_RULE6_4="priority 10 to 1:2:3::5/24 table 22"
ROUTING_RULE6_5="priority 10 to 1:3:3::5 table 55"
NAME="Test Write Routing Rules"
UUID=${UUID}
ONBOOT=yes

View file

@ -4465,6 +4465,101 @@ test_write_wired_dhcp (void)
nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
}
static NMIPRoutingRule *
_ip_routing_rule_new (int addr_family,
const char *str)
{
NMIPRoutingRuleAsStringFlags flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_NONE;
gs_free_error GError *local = NULL;
NMIPRoutingRule *rule;
if (addr_family != AF_UNSPEC) {
if (addr_family == AF_INET)
flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET;
else {
g_assert (addr_family == AF_INET6);
flags = NM_IP_ROUTING_RULE_AS_STRING_FLAGS_AF_INET6;
}
}
rule = nm_ip_routing_rule_from_string (str,
NM_IP_ROUTING_RULE_AS_STRING_FLAGS_VALIDATE
| flags,
NULL,
nmtst_get_rand_bool () ? &local : NULL);
nmtst_assert_success (rule, local);
if (addr_family != AF_UNSPEC)
g_assert_cmpint (nm_ip_routing_rule_get_addr_family (rule), ==, addr_family);
return rule;
}
static void
_ip_routing_rule_add_to_setting (NMSettingIPConfig *s_ip,
const char *str)
{
nm_auto_unref_ip_routing_rule NMIPRoutingRule *rule = NULL;
rule = _ip_routing_rule_new (nm_setting_ip_config_get_addr_family (s_ip), str);
nm_setting_ip_config_add_routing_rule (s_ip, rule);
}
static void
test_write_routing_rules (void)
{
nmtst_auto_unlinkfile char *testfile = NULL;
gs_unref_object NMConnection *connection = NULL;
gs_unref_object NMConnection *reread = NULL;
NMSettingConnection *s_con;
NMSettingWired *s_wired;
NMSettingIPConfig *s_ip4;
NMSettingIPConfig *s_ip6;
connection = nm_simple_connection_new ();
s_con = (NMSettingConnection *) nm_setting_connection_new ();
nm_connection_add_setting (connection, NM_SETTING (s_con));
g_object_set (s_con,
NM_SETTING_CONNECTION_ID, "Test Write Routing Rules",
NM_SETTING_CONNECTION_UUID, nm_utils_uuid_generate_a (),
NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
NM_SETTING_CONNECTION_TYPE, NM_SETTING_WIRED_SETTING_NAME,
NULL);
s_wired = (NMSettingWired *) nm_setting_wired_new ();
nm_connection_add_setting (connection, NM_SETTING (s_wired));
s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
nm_connection_add_setting (connection, NM_SETTING (s_ip4));
g_object_set (s_ip4,
NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_AUTO,
NULL);
s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
nm_connection_add_setting (connection, NM_SETTING (s_ip6));
g_object_set (s_ip6,
NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
NULL);
_ip_routing_rule_add_to_setting (s_ip4, "pref 10 from 0.0.0.0/0 table 1");
_ip_routing_rule_add_to_setting (s_ip4, "priority 10 to 192.167.8.0/24 table 2");
_ip_routing_rule_add_to_setting (s_ip6, "pref 10 from ::/0 table 10");
_ip_routing_rule_add_to_setting (s_ip6, "pref 10 from ::/0 to 1:2:3::5/24 table 22");
_ip_routing_rule_add_to_setting (s_ip6, "pref 10 from ::/0 to 1:3:3::5/128 table 55");
nmtst_assert_connection_verifies (connection);
_writer_new_connec_exp (connection,
TEST_SCRATCH_DIR,
TEST_IFCFG_DIR"/ifcfg-Test_Write_Routing_Rules.cexpected",
&testfile);
reread = _connection_from_file (testfile, NULL, TYPE_ETHERNET, NULL);
nmtst_assert_connection_equals (connection, TRUE, reread, FALSE);
}
static void
test_write_wired_match (void)
{
@ -10200,6 +10295,7 @@ int main (int argc, char **argv)
g_test_add_func (TPATH "wired/write-dhcp-plus-ip", test_write_wired_dhcp_plus_ip);
g_test_add_func (TPATH "wired/write/dhcp-8021x-peap-mschapv2", test_write_wired_dhcp_8021x_peap_mschapv2);
g_test_add_func (TPATH "wired/write/match", test_write_wired_match);
g_test_add_func (TPATH "wired/write/routing-rules", test_write_routing_rules);
#define _add_test_write_wired_8021x_tls(testpath, scheme, flags) \
nmtst_add_test_func (testpath, test_write_wired_8021x_tls, GINT_TO_POINTER (scheme), GINT_TO_POINTER (flags))