mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-09-06 17:14:02 +00:00
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:
parent
6e6d1e070c
commit
4d46804437
|
@ -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 \
|
||||
|
|
|
@ -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,7 +5895,6 @@ connection_from_file_full (const char *filename,
|
|||
error);
|
||||
if (!s_ip6)
|
||||
return NULL;
|
||||
else
|
||||
nm_connection_add_setting (connection, s_ip6);
|
||||
|
||||
s_ip4 = make_ip4_setting (main_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_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)
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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))
|
||||
|
|
Loading…
Reference in a new issue