diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c index 09c5798a25..6a42133c1e 100644 --- a/libnm-core/nm-setting-ip-config.c +++ b/libnm-core/nm-setting-ip-config.c @@ -1182,6 +1182,157 @@ nm_ip_route_set_attribute (NMIPRoute *route, const char *name, GVariant *value) g_hash_table_remove (route->attributes, name); } +#define ATTR_SPEC_PTR(name, type, v4, v6, str_type) \ + &(NMVariantAttributeSpec) { name, type, v4, v6, str_type } + +static const NMVariantAttributeSpec * const ip_route_attribute_spec[] = { + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, G_VARIANT_TYPE_STRING, TRUE, TRUE, 'a'), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_SRC, G_VARIANT_TYPE_STRING, FALSE, TRUE, 'p'), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_TOS, G_VARIANT_TYPE_BYTE, TRUE, TRUE, 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 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_INITRWND, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_MTU, G_VARIANT_TYPE_UINT32, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + ATTR_SPEC_PTR (NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, G_VARIANT_TYPE_BOOLEAN, TRUE, TRUE, 0 ), + NULL, +}; + +/** + * nm_ip_route_get_variant_attribute_spec: + * + * Returns: the specifiers for route attributes + * + * Since: 1.8 + */ +const NMVariantAttributeSpec *const * +nm_ip_route_get_variant_attribute_spec (void) +{ + return ip_route_attribute_spec; +} + +/** + * nm_ip_route_attribute_validate: + * @name: the attribute name + * @value: the attribute value + * @family: IP address family of the route + * @known: (out): on return, whether the attribute name is a known one + * @error: (allow-none): return location for a #GError, or %NULL + * + * Validates a route attribute, i.e. checks that the attribute is a known one + * and the value is of the correct type and well-formed. + * + * Returns: %TRUE if the attribute is valid, %FALSE otherwise + * + * Since: 1.8 + */ +gboolean +nm_ip_route_attribute_validate (const char *name, + GVariant *value, + int family, + gboolean *known, + GError **error) +{ + const NMVariantAttributeSpec *const *iter; + const NMVariantAttributeSpec *spec = NULL; + + g_return_val_if_fail (name, FALSE); + g_return_val_if_fail (value, FALSE); + g_return_val_if_fail (family == AF_INET || family == AF_INET6, FALSE); + g_return_val_if_fail (!error || !*error, FALSE); + + for (iter = ip_route_attribute_spec; *iter; iter++) { + if (nm_streq (name, (*iter)->name)) { + spec = *iter; + break; + } + } + + if (!spec) { + NM_SET_OUT (known, FALSE); + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("unknown attribute")); + return FALSE; + } + + NM_SET_OUT (known, TRUE); + + if (!g_variant_is_of_type (value, spec->type)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid attribute type'%s'"), + g_variant_get_type_string (value)); + return FALSE; + } + + if ( (family == AF_INET && !spec->v4) + || (family == AF_INET6 && !spec->v6)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + family == AF_INET ? + _("attribute is not valid for a IPv4 route") : + _("attribute is not valid for a IPv6 route")); + return FALSE; + } + + if (spec->type == G_VARIANT_TYPE_STRING) { + const char *string = g_variant_get_string (value, NULL); + gs_free char *string_free = NULL; + char *sep; + + switch (spec->str_type) { + case 'a': /* IP address */ + if (!nm_utils_ipaddr_valid (family, string)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + family == AF_INET ? + _("'%s' is not a valid IPv4 address") : + _("'%s' is not a valid IPv6 address"), + string); + return FALSE; + } + break; + case 'p': /* IP address + optional prefix */ + string_free = g_strdup (string); + sep = strchr (string_free, '/'); + if (sep) { + *sep = 0; + if (_nm_utils_ascii_str_to_int64 (sep + 1, 10, 1, family == AF_INET ? 32 : 128, -1) < 0) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + _("invalid prefix %s"), sep + 1); + return FALSE; + } + } + if (!nm_utils_ipaddr_valid (family, string_free)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_FAILED, + family == AF_INET ? + _("'%s' is not a valid IPv4 address") : + _("'%s' is not a valid IPv6 address"), + string_free); + return FALSE; + } + break; + default: + break; + } + } + + return TRUE; +} + /*****************************************************************************/ G_DEFINE_ABSTRACT_TYPE (NMSettingIPConfig, nm_setting_ip_config, NM_TYPE_SETTING) diff --git a/libnm-core/nm-setting-ip-config.h b/libnm-core/nm-setting-ip-config.h index b18d6a9439..a4ebdc3268 100644 --- a/libnm-core/nm-setting-ip-config.h +++ b/libnm-core/nm-setting-ip-config.h @@ -28,6 +28,7 @@ #endif #include "nm-setting.h" +#include "nm-utils.h" G_BEGIN_DECLS @@ -121,7 +122,28 @@ GVariant *nm_ip_route_get_attribute (NMIPRoute *route, void nm_ip_route_set_attribute (NMIPRoute *route, const char *name, GVariant *value); +NM_AVAILABLE_IN_1_8 +const NMVariantAttributeSpec *const *nm_ip_route_get_variant_attribute_spec (void); +NM_AVAILABLE_IN_1_8 +gboolean nm_ip_route_attribute_validate (const char *name, + GVariant *value, + int family, + gboolean *known, + GError **error); +#define NM_IP_ROUTE_ATTRIBUTE_PREF_SRC "pref-src" +#define NM_IP_ROUTE_ATTRIBUTE_SRC "src" +#define NM_IP_ROUTE_ATTRIBUTE_TOS "tos" +#define NM_IP_ROUTE_ATTRIBUTE_WINDOW "window" +#define NM_IP_ROUTE_ATTRIBUTE_CWND "cwnd" +#define NM_IP_ROUTE_ATTRIBUTE_INITCWND "initcwnd" +#define NM_IP_ROUTE_ATTRIBUTE_INITRWND "initrwnd" +#define NM_IP_ROUTE_ATTRIBUTE_MTU "mtu" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_WINDOW "lock-window" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_CWND "lock-cwnd" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_INITCWND "lock-initcwnd" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_INITRWND "lock-initrwnd" +#define NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU "lock-mtu" #define NM_TYPE_SETTING_IP_CONFIG (nm_setting_ip_config_get_type ()) #define NM_SETTING_IP_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SETTING_IP_CONFIG, NMSettingIPConfig)) diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index ee425593d6..03c8790764 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -31,6 +31,9 @@ struct _NMVariantAttributeSpec { char *name; const GVariantType *type; + bool v4:1; + bool v6:1; + char str_type; }; gboolean _nm_utils_string_slist_validate (GSList *list, diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index 073850b4e5..9cdbf2626a 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -642,6 +642,47 @@ test_setting_ip4_config_address_data (void) g_object_unref (conn); } +static void +test_setting_ip_route_attributes (void) +{ + GVariant *variant; + gboolean res, known; + +#define TEST_ATTR(name, type, value, family, exp_res, exp_known) \ + variant = g_variant_new_ ## type (value); \ + res = nm_ip_route_attribute_validate (name, variant, family, &known, NULL); \ + g_assert (res == exp_res); \ + g_assert (known == exp_known); \ + g_variant_unref (variant); + + TEST_ATTR ("foo", uint32, 12, AF_INET, FALSE, FALSE); + + TEST_ATTR ("tos", byte, 127, AF_INET, TRUE, TRUE); + TEST_ATTR ("tos", string, "0x28", AF_INET, FALSE, TRUE); + + TEST_ATTR ("cwnd", uint32, 10, AF_INET, TRUE, TRUE); + TEST_ATTR ("cwnd", string, "11", AF_INET, FALSE, TRUE); + + TEST_ATTR ("lock-mtu", boolean, TRUE, AF_INET, TRUE, TRUE); + TEST_ATTR ("lock-mtu", uint32, 1, AF_INET, FALSE, TRUE); + + TEST_ATTR ("src", string, "fd01::1", AF_INET6, TRUE, TRUE); + TEST_ATTR ("src", string, "fd01::1/64", AF_INET6, TRUE, TRUE); + TEST_ATTR ("src", string, "fd01::1/128", AF_INET6, TRUE, TRUE); + TEST_ATTR ("src", string, "fd01::1/129", AF_INET6, FALSE, TRUE); + TEST_ATTR ("src", string, "fd01::1/a", AF_INET6, FALSE, TRUE); + TEST_ATTR ("src", string, "abc/64", AF_INET6, FALSE, TRUE); + TEST_ATTR ("src", string, "1.2.3.4", AF_INET, FALSE, TRUE); + TEST_ATTR ("src", string, "1.2.3.4", AF_INET6, FALSE, TRUE); + + TEST_ATTR ("pref-src", string, "1.2.3.4", AF_INET, TRUE, TRUE); + TEST_ATTR ("pref-src", string, "1.2.3.4", AF_INET6, FALSE, TRUE); + TEST_ATTR ("pref-src", string, "1.2.3.0/24", AF_INET, FALSE, TRUE); + TEST_ATTR ("pref-src", string, "fd01::12", AF_INET6, TRUE, TRUE); + +#undef TEST_ATTR +} + static void test_setting_gsm_apn_spaces (void) { @@ -5412,6 +5453,97 @@ test_nm_in_strset (void) #undef _ASSERT } +static void +test_route_attributes_parse (void) +{ + GHashTable *ht; + GError *error = NULL; + GVariant *variant; + + ht = nm_utils_parse_variant_attributes ("mtu=1400 pref-src=1.2.3.4 cwnd=14", + ' ', '=', FALSE, + nm_ip_route_get_variant_attribute_spec (), + &error); + g_assert_no_error (error); + g_assert (ht); + g_hash_table_unref (ht); + + ht = nm_utils_parse_variant_attributes ("mtu=1400 pref-src=1.2.3.4 cwnd=14 \\", + ' ', '=', FALSE, + nm_ip_route_get_variant_attribute_spec (), + &error); + g_assert_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_FAILED); + g_assert (!ht); + g_clear_error (&error); + + ht = nm_utils_parse_variant_attributes ("mtu.1400 pref-src.1\\.2\\.3\\.4 ", + ' ', '.', FALSE, + nm_ip_route_get_variant_attribute_spec (), + &error); + g_assert (ht); + g_assert_no_error (error); + variant = g_hash_table_lookup (ht, NM_IP_ROUTE_ATTRIBUTE_MTU); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)); + g_assert_cmpuint (g_variant_get_uint32 (variant), ==, 1400); + + variant = g_hash_table_lookup (ht, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "1.2.3.4"); + g_hash_table_unref (ht); + + ht = nm_utils_parse_variant_attributes ("src:fd01\\:\\:42\\/64/initrwnd:21", + '/', ':', FALSE, + nm_ip_route_get_variant_attribute_spec (), + &error); + g_assert (ht); + g_assert_no_error (error); + variant = g_hash_table_lookup (ht, NM_IP_ROUTE_ATTRIBUTE_INITRWND); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32)); + g_assert_cmpuint (g_variant_get_uint32 (variant), ==, 21); + + variant = g_hash_table_lookup (ht, NM_IP_ROUTE_ATTRIBUTE_SRC); + g_assert (variant); + g_assert (g_variant_is_of_type (variant, G_VARIANT_TYPE_STRING)); + g_assert_cmpstr (g_variant_get_string (variant, NULL), ==, "fd01::42/64"); + g_hash_table_unref (ht); +} + +static void +test_route_attributes_format (void) +{ + gs_unref_hashtable GHashTable *ht = NULL; + char *str; + + ht = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, (GDestroyNotify) g_variant_unref); + + str = nm_utils_format_variant_attributes (NULL, ' ', '='); + g_assert_cmpstr (str, ==, NULL); + + str = nm_utils_format_variant_attributes (ht, ' ', '='); + g_assert_cmpstr (str, ==, NULL); + + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_MTU, g_variant_new_uint32 (5000)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_INITRWND, g_variant_new_uint32 (20)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_LOCK_MTU, g_variant_new_boolean (TRUE)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_PREF_SRC, g_variant_new_string ("aaaa:bbbb::1")); + str = nm_utils_format_variant_attributes (ht, ' ', '='); + g_assert_cmpstr (str, ==, "initrwnd=20 lock-mtu=true mtu=5000 pref-src=aaaa:bbbb::1"); + g_hash_table_remove_all (ht); + g_free (str); + + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_WINDOW, g_variant_new_uint32 (30000)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_INITCWND, g_variant_new_uint32 (21)); + g_hash_table_insert (ht, NM_IP_ROUTE_ATTRIBUTE_SRC, g_variant_new_string ("aaaa:bbbb:cccc:dddd::/64")); + str = nm_utils_format_variant_attributes (ht, '/', ':'); + g_assert_cmpstr (str, ==, "initcwnd:21/src:aaaa\\:bbbb\\:cccc\\:dddd\\:\\:\\/64/window:30000"); + g_hash_table_remove_all (ht); + g_free (str); +} + /*****************************************************************************/ static gboolean @@ -5467,6 +5599,7 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/test_setting_vpn_modify_during_foreach", test_setting_vpn_modify_during_foreach); g_test_add_func ("/core/general/test_setting_ip4_config_labels", test_setting_ip4_config_labels); g_test_add_func ("/core/general/test_setting_ip4_config_address_data", test_setting_ip4_config_address_data); + g_test_add_func ("/core/general/test_setting_ip_route_attributes", test_setting_ip_route_attributes); g_test_add_func ("/core/general/test_setting_gsm_apn_spaces", test_setting_gsm_apn_spaces); g_test_add_func ("/core/general/test_setting_gsm_apn_bad_chars", test_setting_gsm_apn_bad_chars); g_test_add_func ("/core/general/test_setting_gsm_apn_underscore", test_setting_gsm_apn_underscore); @@ -5576,6 +5709,8 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/_nm_utils_team_config_equal", test_nm_utils_team_config_equal); g_test_add_func ("/core/general/test_nm_utils_enum", test_nm_utils_enum); g_test_add_func ("/core/general/nm-set-out", test_nm_set_out); + g_test_add_func ("/core/general/route_attributes/parse", test_route_attributes_parse); + g_test_add_func ("/core/general/route_attributes/format", test_route_attributes_format); return g_test_run (); } diff --git a/libnm/libnm.ver b/libnm/libnm.ver index 3e8087b652..4b3d02fdfa 100644 --- a/libnm/libnm.ver +++ b/libnm/libnm.ver @@ -1148,6 +1148,8 @@ libnm_1_8_0 { global: nm_connection_get_setting_dummy; nm_device_dummy_get_type; + nm_ip_route_get_variant_attribute_spec; + nm_ip_route_attribute_validate; nm_setting_802_1x_auth_flags_get_type; nm_setting_802_1x_get_auth_timeout; nm_setting_802_1x_get_ca_cert_password;