Commit 39709fdc authored by Dan Winship's avatar Dan Winship

libnm-core: add NMIPAddress/NMIPRoute attributes, use for labels

Add key-value attributes to NMIPAddress and NMIPRoute, and use them to
store IPv4 address labels. Demote NMSettingIP4Config:address-labels to
a D-Bus-only property, and arrange for :addresses setter to read the
labels out of that property when creating the addresses.
parent 21c8a6b2
......@@ -74,12 +74,6 @@
g_clear_object (&c); \
}
const char *_nm_setting_ip4_config_get_address_label (NMSettingIP4Config *setting,
guint32 i);
gboolean _nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting,
NMIPAddress *address,
const char *label);
/* NM_SETTING_COMPARE_FLAG_INFERRABLE: check whether a device-generated
* connection can be replaced by a already-defined connection. This flag only
* takes into account properties marked with the %NM_SETTING_PARAM_INFERRABLE
......
......@@ -89,6 +89,8 @@ struct NMIPAddress {
char *address, *gateway;
int prefix, family;
GHashTable *attributes;
};
/**
......@@ -202,6 +204,8 @@ nm_ip_address_unref (NMIPAddress *address)
if (address->refcount == 0) {
g_free (address->address);
g_free (address->gateway);
if (address->attributes)
g_hash_table_unref (address->attributes);
g_slice_free (NMIPAddress, address);
}
}
......@@ -211,7 +215,8 @@ nm_ip_address_unref (NMIPAddress *address)
* @address: the #NMIPAddress
* @other: the #NMIPAddress to compare @address to.
*
* Determines if two #NMIPAddress objects contain the same values.
* Determines if two #NMIPAddress objects contain the same address, prefix, and
* gateway (attributes are not compared).
*
* Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
**/
......@@ -251,6 +256,16 @@ nm_ip_address_dup (NMIPAddress *address)
copy = nm_ip_address_new (address->family,
address->address, address->prefix, address->gateway,
NULL);
if (address->attributes) {
GHashTableIter iter;
const char *key;
GVariant *value;
g_hash_table_iter_init (&iter, address->attributes);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value))
nm_ip_address_set_attribute (copy, key, value);
}
return copy;
}
......@@ -427,6 +442,82 @@ nm_ip_address_set_gateway (NMIPAddress *address,
address->gateway = canonicalize_ip (address->family, gateway, TRUE);
}
/**
* nm_ip_address_get_attribute_names:
* @address: the #NMIPAddress
*
* Gets an array of attribute names defined on @address.
*
* Returns: (transfer full): a %NULL-terminated array of attribute names,
**/
char **
nm_ip_address_get_attribute_names (NMIPAddress *address)
{
GHashTableIter iter;
const char *key;
GPtrArray *names;
g_return_val_if_fail (address != NULL, NULL);
names = g_ptr_array_new ();
if (address->attributes) {
g_hash_table_iter_init (&iter, address->attributes);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL))
g_ptr_array_add (names, g_strdup (key));
}
g_ptr_array_add (names, NULL);
return (char **) g_ptr_array_free (names, FALSE);
}
/**
* nm_ip_address_get_attribute:
* @address: the #NMIPAddress
* @name: the name of an address attribute
*
* Gets the value of the attribute with name @name on @address
*
* Returns: (transfer none): the value of the attribute with name @name on
* @address, or %NULL if @address has no such attribute.
**/
GVariant *
nm_ip_address_get_attribute (NMIPAddress *address, const char *name)
{
g_return_val_if_fail (address != NULL, NULL);
g_return_val_if_fail (name != NULL && *name != '\0', NULL);
if (address->attributes)
return g_hash_table_lookup (address->attributes, name);
else
return NULL;
}
/**
* nm_ip_address_set_attribute:
* @address: the #NMIPAddress
* @name: the name of an address attribute
* @value: (transfer none) (allow-none): the value
*
* Sets or clears the named attribute on @address to the given value.
**/
void
nm_ip_address_set_attribute (NMIPAddress *address, const char *name, GVariant *value)
{
g_return_if_fail (address != NULL);
g_return_if_fail (name != NULL && *name != '\0');
if (!address->attributes) {
address->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, (GDestroyNotify) g_variant_unref);
}
if (value)
g_hash_table_insert (address->attributes, g_strdup (name), g_variant_ref_sink (value));
else
g_hash_table_remove (address->attributes, name);
}
G_DEFINE_BOXED_TYPE (NMIPRoute, nm_ip_route, nm_ip_route_dup, nm_ip_route_unref)
......@@ -438,6 +529,8 @@ struct NMIPRoute {
guint prefix;
char *next_hop;
guint32 metric;
GHashTable *attributes;
};
/**
......@@ -559,6 +652,8 @@ nm_ip_route_unref (NMIPRoute *route)
if (route->refcount == 0) {
g_free (route->dest);
g_free (route->next_hop);
if (route->attributes)
g_hash_table_unref (route->attributes);
g_slice_free (NMIPRoute, route);
}
}
......@@ -568,7 +663,8 @@ nm_ip_route_unref (NMIPRoute *route)
* @route: the #NMIPRoute
* @other: the #NMIPRoute to compare @route to.
*
* Determines if two #NMIPRoute objects contain the same values.
* Determines if two #NMIPRoute objects contain the same destination, prefix,
* next hop, and metric. (Attributes are not compared.)
*
* Returns: %TRUE if the objects contain the same values, %FALSE if they do not.
**/
......@@ -609,6 +705,16 @@ nm_ip_route_dup (NMIPRoute *route)
route->dest, route->prefix,
route->next_hop, route->metric,
NULL);
if (route->attributes) {
GHashTableIter iter;
const char *key;
GVariant *value;
g_hash_table_iter_init (&iter, route->attributes);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value))
nm_ip_route_set_attribute (copy, key, value);
}
return copy;
}
......@@ -872,3 +978,79 @@ nm_ip_route_set_metric (NMIPRoute *route,
route->metric = metric;
}
/**
* nm_ip_route_get_attribute_names:
* @route: the #NMIPRoute
*
* Gets an array of attribute names defined on @route.
*
* Returns: (transfer full): a %NULL-terminated array of attribute names
**/
char **
nm_ip_route_get_attribute_names (NMIPRoute *route)
{
GHashTableIter iter;
const char *key;
GPtrArray *names;
g_return_val_if_fail (route != NULL, NULL);
names = g_ptr_array_new ();
if (route->attributes) {
g_hash_table_iter_init (&iter, route->attributes);
while (g_hash_table_iter_next (&iter, (gpointer *) &key, NULL))
g_ptr_array_add (names, g_strdup (key));
}
g_ptr_array_add (names, NULL);
return (char **) g_ptr_array_free (names, FALSE);
}
/**
* nm_ip_route_get_attribute:
* @route: the #NMIPRoute
* @name: the name of an route attribute
*
* Gets the value of the attribute with name @name on @route
*
* Returns: (transfer none): the value of the attribute with name @name on
* @route, or %NULL if @route has no such attribute.
**/
GVariant *
nm_ip_route_get_attribute (NMIPRoute *route, const char *name)
{
g_return_val_if_fail (route != NULL, NULL);
g_return_val_if_fail (name != NULL && *name != '\0', NULL);
if (route->attributes)
return g_hash_table_lookup (route->attributes, name);
else
return NULL;
}
/**
* nm_ip_route_set_attribute:
* @route: the #NMIPRoute
* @name: the name of a route attribute
* @value: (transfer none) (allow-none): the value
*
* Sets the named attribute on @route to the given value.
**/
void
nm_ip_route_set_attribute (NMIPRoute *route, const char *name, GVariant *value)
{
g_return_if_fail (route != NULL);
g_return_if_fail (name != NULL && *name != '\0');
if (!route->attributes) {
route->attributes = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, (GDestroyNotify) g_variant_unref);
}
if (value)
g_hash_table_insert (route->attributes, g_strdup (name), g_variant_ref_sink (value));
else
g_hash_table_remove (route->attributes, name);
}
......@@ -67,6 +67,14 @@ const char *nm_ip_address_get_gateway (NMIPAddress *address);
void nm_ip_address_set_gateway (NMIPAddress *address,
const char *gateway);
char **nm_ip_address_get_attribute_names (NMIPAddress *address);
GVariant *nm_ip_address_get_attribute (NMIPAddress *address,
const char *name);
void nm_ip_address_set_attribute (NMIPAddress *address,
const char *name,
GVariant *value);
typedef struct NMIPRoute NMIPRoute;
GType nm_ip_route_get_type (void);
......@@ -112,6 +120,13 @@ guint32 nm_ip_route_get_metric (NMIPRoute *route);
void nm_ip_route_set_metric (NMIPRoute *route,
guint32 metric);
char **nm_ip_route_get_attribute_names (NMIPRoute *route);
GVariant *nm_ip_route_get_attribute (NMIPRoute *route,
const char *name);
void nm_ip_route_set_attribute (NMIPRoute *route,
const char *name,
GVariant *value);
G_END_DECLS
#endif /* NM_SETTING_IP_CONFIG_H */
......@@ -50,7 +50,6 @@ typedef struct {
GSList *dns; /* list of IP address strings */
GSList *dns_search; /* list of strings */
GSList *addresses; /* array of NMIPAddress */
GSList *address_labels; /* list of strings */
GSList *routes; /* array of NMIPRoute */
gboolean ignore_auto_routes;
gboolean ignore_auto_dns;
......@@ -67,7 +66,6 @@ enum {
PROP_DNS,
PROP_DNS_SEARCH,
PROP_ADDRESSES,
PROP_ADDRESS_LABELS,
PROP_ROUTES,
PROP_IGNORE_AUTO_ROUTES,
PROP_IGNORE_AUTO_DNS,
......@@ -434,19 +432,6 @@ nm_setting_ip4_config_get_address (NMSettingIP4Config *setting, guint32 i)
return (NMIPAddress *) g_slist_nth_data (priv->addresses, i);
}
const char *
_nm_setting_ip4_config_get_address_label (NMSettingIP4Config *setting, guint32 i)
{
NMSettingIP4ConfigPrivate *priv;
g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), NULL);
priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
g_return_val_if_fail (i < g_slist_length (priv->address_labels), NULL);
return (const char *) g_slist_nth_data (priv->address_labels, i);
}
/**
* nm_setting_ip4_config_add_address:
* @setting: the #NMSettingIP4Config
......@@ -461,14 +446,6 @@ _nm_setting_ip4_config_get_address_label (NMSettingIP4Config *setting, guint32 i
gboolean
nm_setting_ip4_config_add_address (NMSettingIP4Config *setting,
NMIPAddress *address)
{
return _nm_setting_ip4_config_add_address_with_label (setting, address, "");
}
gboolean
_nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting,
NMIPAddress *address,
const char *label)
{
NMSettingIP4ConfigPrivate *priv;
NMIPAddress *copy;
......@@ -476,7 +453,6 @@ _nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting,
g_return_val_if_fail (NM_IS_SETTING_IP4_CONFIG (setting), FALSE);
g_return_val_if_fail (address != NULL, FALSE);
g_return_val_if_fail (label != NULL, FALSE);
priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
for (iter = priv->addresses; iter; iter = g_slist_next (iter)) {
......@@ -486,7 +462,6 @@ _nm_setting_ip4_config_add_address_with_label (NMSettingIP4Config *setting,
copy = nm_ip_address_dup (address);
priv->addresses = g_slist_append (priv->addresses, copy);
priv->address_labels = g_slist_append (priv->address_labels, g_strdup (label));
g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES);
return TRUE;
......@@ -503,19 +478,16 @@ void
nm_setting_ip4_config_remove_address (NMSettingIP4Config *setting, guint32 i)
{
NMSettingIP4ConfigPrivate *priv;
GSList *addr, *label;
GSList *addr;
g_return_if_fail (NM_IS_SETTING_IP4_CONFIG (setting));
priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
addr = g_slist_nth (priv->addresses, i);
label = g_slist_nth (priv->address_labels, i);
g_return_if_fail (addr != NULL && label != NULL);
g_return_if_fail (addr != NULL);
nm_ip_address_unref ((NMIPAddress *) addr->data);
priv->addresses = g_slist_delete_link (priv->addresses, addr);
g_free (label->data);
priv->address_labels = g_slist_delete_link (priv->address_labels, label);
g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES);
}
......@@ -566,8 +538,6 @@ nm_setting_ip4_config_clear_addresses (NMSettingIP4Config *setting)
g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip_address_unref);
priv->addresses = NULL;
g_slist_free_full (priv->address_labels, g_free);
priv->address_labels = NULL;
g_object_notify (G_OBJECT (setting), NM_SETTING_IP4_CONFIG_ADDRESSES);
}
......@@ -843,9 +813,6 @@ verify_label (const char *label)
const char *p;
char *iface;
if (!*label)
return TRUE;
p = strchr (label, ':');
if (!p)
return FALSE;
......@@ -955,31 +922,33 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
}
/* Validate address labels */
for (iter = priv->address_labels, i = 0; iter; iter = g_slist_next (iter), i++) {
const char *label = (const char *) iter->data;
if (!verify_label (label)) {
for (iter = priv->addresses, i = 0; iter; iter = g_slist_next (iter), i++) {
NMIPAddress *addr = (NMIPAddress *) iter->data;
GVariant *label;
label = nm_ip_address_get_attribute (addr, "label");
if (!label)
continue;
if (!g_variant_is_of_type (label, G_VARIANT_TYPE_STRING)) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%d. IPv4 address has 'label' property with invalid type"),
i+1);
g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES);
return FALSE;
}
if (!verify_label (g_variant_get_string (label, NULL))) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("%d. IPv4 address has invalid label '%s'"),
i+1, label);
g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, "address-labels");
i+1, g_variant_get_string (label, NULL));
g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_SETTING_IP4_CONFIG_ADDRESSES);
return FALSE;
}
}
if (g_slist_length (priv->addresses) != g_slist_length (priv->address_labels)) {
g_set_error (error,
NM_CONNECTION_ERROR,
NM_CONNECTION_ERROR_INVALID_PROPERTY,
_("IPv4 address / label count mismatch (%d vs %d)"),
g_slist_length (priv->addresses),
g_slist_length (priv->address_labels));
g_prefix_error (error, "%s.%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME, "address-labels");
return FALSE;
}
/* Validate DNS */
for (iter = priv->dns, i = 0; iter; iter = g_slist_next (iter), i++) {
const char *dns = (const char *) iter->data;
......@@ -1018,7 +987,6 @@ finalize (GObject *object)
g_slist_free_full (priv->dns, g_free);
g_slist_free_full (priv->dns_search, g_free);
g_slist_free_full (priv->addresses, (GDestroyNotify) nm_ip_address_unref);
g_slist_free_full (priv->address_labels, g_free);
g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip_route_unref);
G_OBJECT_CLASS (nm_setting_ip4_config_parent_class)->finalize (object);
......@@ -1038,16 +1006,67 @@ ip4_dns_from_dbus (GVariant *dbus_value,
}
static GVariant *
ip4_addresses_to_dbus (const GValue *prop_value)
ip4_addresses_get (NMSetting *setting,
const char *property)
{
return nm_utils_ip4_addresses_to_variant (g_value_get_boxed (prop_value));
GPtrArray *addrs;
GVariant *ret;
g_object_get (setting, property, &addrs, NULL);
ret = nm_utils_ip4_addresses_to_variant (addrs);
g_ptr_array_unref (addrs);
return ret;
}
static void
ip4_addresses_from_dbus (GVariant *dbus_value,
GValue *prop_value)
ip4_addresses_set (NMSetting *setting,
GVariant *connection_dict,
const char *property,
GVariant *value)
{
g_value_take_boxed (prop_value, nm_utils_ip4_addresses_from_variant (dbus_value));
GPtrArray *addrs;
GVariant *s_ip4;
char **labels;
int i;
addrs = nm_utils_ip4_addresses_from_variant (value);
s_ip4 = g_variant_lookup_value (connection_dict, NM_SETTING_IP4_CONFIG_SETTING_NAME, NM_VARIANT_TYPE_SETTING);
if (g_variant_lookup (s_ip4, "address-labels", "^as", &labels)) {
for (i = 0; i < addrs->len && labels[i]; i++)
if (*labels[i])
nm_ip_address_set_attribute (addrs->pdata[i], "label", g_variant_new_string (labels[i]));
g_strfreev (labels);
}
g_variant_unref (s_ip4);
g_object_set (setting, property, addrs, NULL);
g_ptr_array_unref (addrs);
}
static GVariant *
ip4_address_labels_get (NMSetting *setting,
NMConnection *connection,
const char *property)
{
NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
GPtrArray *labels;
GSList *iter;
GVariant *ret;
labels = g_ptr_array_new ();
for (iter = priv->addresses; iter; iter = iter->next) {
NMIPAddress *addr = iter->data;
GVariant *label = nm_ip_address_get_attribute (addr, "label");
g_ptr_array_add (labels, (char *) (label ? g_variant_get_string (label, NULL) : ""));
}
ret = g_variant_new_strv ((const char * const *) labels->pdata, labels->len);
g_ptr_array_unref (labels);
return ret;
}
static GVariant *
......@@ -1069,7 +1088,6 @@ set_property (GObject *object, guint prop_id,
{
NMSettingIP4Config *setting = NM_SETTING_IP4_CONFIG (object);
NMSettingIP4ConfigPrivate *priv = NM_SETTING_IP4_CONFIG_GET_PRIVATE (setting);
GSList *iter;
switch (prop_id) {
case PROP_METHOD:
......@@ -1089,16 +1107,6 @@ set_property (GObject *object, guint prop_id,
priv->addresses = _nm_utils_copy_array_to_slist (g_value_get_boxed (value),
(NMUtilsCopyFunc) nm_ip_address_dup);
if (g_slist_length (priv->addresses) != g_slist_length (priv->address_labels)) {
g_slist_free_full (priv->address_labels, g_free);
priv->address_labels = NULL;
for (iter = priv->addresses; iter; iter = iter->next)
priv->address_labels = g_slist_prepend (priv->address_labels, g_strdup (""));
}
break;
case PROP_ADDRESS_LABELS:
g_slist_free_full (priv->address_labels, g_free);
priv->address_labels = _nm_utils_strv_to_slist (g_value_get_boxed (value));
break;
case PROP_ROUTES:
g_slist_free_full (priv->routes, (GDestroyNotify) nm_ip_route_unref);
......@@ -1154,9 +1162,6 @@ get_property (GObject *object, guint prop_id,
case PROP_ADDRESSES:
g_value_take_boxed (value, _nm_utils_copy_slist_to_array (priv->addresses, (NMUtilsCopyFunc) nm_ip_address_dup, (GDestroyNotify) nm_ip_address_unref));
break;
case PROP_ADDRESS_LABELS:
g_value_take_boxed (value, _nm_utils_slist_to_strv (priv->address_labels));
break;
case PROP_ROUTES:
g_value_take_boxed (value, _nm_utils_copy_slist_to_array (priv->routes, (NMUtilsCopyFunc) nm_ip_route_dup, (GDestroyNotify) nm_ip_route_unref));
break;
......@@ -1281,23 +1286,17 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *setting_class)
G_PARAM_READWRITE |
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS));
_nm_setting_class_transform_property (parent_class, NM_SETTING_IP4_CONFIG_ADDRESSES,
G_VARIANT_TYPE ("aau"),
ip4_addresses_to_dbus,
ip4_addresses_from_dbus);
/**
* NMSettingIP4Config:address-labels:
*
* Internal use only.
**/
g_object_class_install_property
(object_class, PROP_ADDRESS_LABELS,
g_param_spec_boxed ("address-labels", "", "",
G_TYPE_STRV,
G_PARAM_READWRITE |
NM_SETTING_PARAM_INFERRABLE |
G_PARAM_STATIC_STRINGS));
_nm_setting_class_override_property (parent_class, NM_SETTING_IP4_CONFIG_ADDRESSES,
G_VARIANT_TYPE ("aau"),
ip4_addresses_get,
ip4_addresses_set,
NULL);
_nm_setting_class_add_dbus_only_property (parent_class,
"address-labels",
G_VARIANT_TYPE_STRING_ARRAY,
ip4_address_labels_get,
NULL);
/**
* NMSettingIP4Config:routes:
......
......@@ -325,9 +325,11 @@ test_setting_ip4_config_labels (void)
{
NMSettingIP4Config *s_ip4;
NMIPAddress *addr;
const char *label;
GVariant *label;
GPtrArray *addrs;
char **labels;
NMConnection *conn;
GVariant *dict, *setting_dict, *value;
GError *error = NULL;
s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
......@@ -344,32 +346,38 @@ test_setting_ip4_config_labels (void)
nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
g_assert_no_error (error);
label = _nm_setting_ip4_config_get_address_label (s_ip4, 0);
g_assert_cmpstr (label, ==, "");
addr = nm_setting_ip4_config_get_address (s_ip4, 0);
label = nm_ip_address_get_attribute (addr, "label");
g_assert (label == NULL);
/* addr 2 */
addr = nm_ip_address_new (AF_INET, "2.2.2.2", 24, NULL, &error);
g_assert_no_error (error);
nm_ip_address_set_attribute (addr, "label", g_variant_new_string ("eth0:1"));
_nm_setting_ip4_config_add_address_with_label (s_ip4, addr, "eth0:1");
nm_setting_ip4_config_add_address (s_ip4, addr);
nm_ip_address_unref (addr);
nm_setting_verify (NM_SETTING (s_ip4), NULL, &error);
g_assert_no_error (error);
label = _nm_setting_ip4_config_get_address_label (s_ip4, 1);
g_assert_cmpstr (label, ==, "eth0:1");
addr = nm_setting_ip4_config_get_address (s_ip4, 1);
label = nm_ip_address_get_attribute (addr, "label");
g_assert (label != NULL);
g_assert_cmpstr (g_variant_get_string (label, NULL), ==, "eth0:1");
/* addr 3 */