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.
This commit is contained in:
Beniamino Galvani 2017-02-15 15:06:24 +01:00
parent 93b3a478bb
commit 54e58eb96b
5 changed files with 313 additions and 0 deletions

View file

@ -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)

View file

@ -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))

View file

@ -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,

View file

@ -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 ();
}

View file

@ -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;