From 54e58eb96bbfcd26d31ddba2e98ff2c59335a02a Mon Sep 17 00:00:00 2001 From: Beniamino Galvani Date: Wed, 15 Feb 2017 15:06:24 +0100 Subject: [PATCH] libnm-core: define known route attribute names and validation function This adds definition of a set of known route option attributes to libnm-core and helper functions. nm_ip_route_attribute_validate() performs the validation of the attribute type and, in case of a formatted string attribute, of its content. nm_ip_route_get_variant_attribute_spec() returns the attribute format specifier to be passed to nm_utils_parse_variant_attributes(). Since at the moment NMIPRoute is the only user of NMVariantAttributeSpec and the type is opaque to users of the library, the struct is extended to carry some other data useful for validation. --- libnm-core/nm-setting-ip-config.c | 151 ++++++++++++++++++++++++++++++ libnm-core/nm-setting-ip-config.h | 22 +++++ libnm-core/nm-utils-private.h | 3 + libnm-core/tests/test-general.c | 135 ++++++++++++++++++++++++++ libnm/libnm.ver | 2 + 5 files changed, 313 insertions(+) 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;