diff --git a/Makefile.am b/Makefile.am index a8353656da..de3d25e308 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 \ diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c index de45c084ef..f9722b8f37 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-reader.c @@ -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) diff --git a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c index f6b8f8c21a..a57e6c3a60 100644 --- a/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c +++ b/src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c @@ -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)); diff --git a/src/settings/plugins/ifcfg-rh/shvar.c b/src/settings/plugins/ifcfg-rh/shvar.c index f3d58e26c7..02aba1c57f 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.c +++ b/src/settings/plugins/ifcfg-rh/shvar.c @@ -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 * diff --git a/src/settings/plugins/ifcfg-rh/shvar.h b/src/settings/plugins/ifcfg-rh/shvar.h index 622bb474b1..b38a855760 100644 --- a/src/settings/plugins/ifcfg-rh/shvar.h +++ b/src/settings/plugins/ifcfg-rh/shvar.h @@ -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 resolves to any truth value (e.g. "yes", "y", "true") * return FALSE if resolves to any non-truth value (e.g. "no", "n", "false") * return otherwise diff --git a/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected new file mode 100644 index 0000000000..0c2fa035cf --- /dev/null +++ b/src/settings/plugins/ifcfg-rh/tests/network-scripts/ifcfg-Test_Write_Routing_Rules.cexpected @@ -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 diff --git a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c index 99ffae403b..09e2c0e4b5 100644 --- a/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c +++ b/src/settings/plugins/ifcfg-rh/tests/test-ifcfg-rh.c @@ -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))