summaryrefslogtreecommitdiff
path: root/libnm-core
diff options
context:
space:
mode:
Diffstat (limited to 'libnm-core')
-rw-r--r--libnm-core/Makefile.am7
-rw-r--r--libnm-core/nm-connection.c34
-rw-r--r--libnm-core/nm-core-internal.h4
-rw-r--r--libnm-core/nm-keyfile-writer.c28
-rw-r--r--libnm-core/nm-setting-ip-config.c62
-rw-r--r--libnm-core/nm-setting-ip-config.h4
-rw-r--r--libnm-core/nm-setting-ip4-config.c11
-rw-r--r--libnm-core/nm-setting-ip6-config.c115
-rw-r--r--libnm-core/nm-setting-ip6-config.h4
-rw-r--r--libnm-core/nm-setting-team-port.c46
-rw-r--r--libnm-core/nm-setting-team.c52
-rw-r--r--libnm-core/nm-utils-private.h3
-rw-r--r--libnm-core/nm-utils.c175
-rw-r--r--libnm-core/nm-version.h14
-rw-r--r--libnm-core/nm-vpn-editor-plugin.c40
-rw-r--r--libnm-core/nm-vpn-editor-plugin.h5
-rw-r--r--libnm-core/nm-vpn-plugin-info.c175
-rw-r--r--libnm-core/nm-vpn-plugin-info.h10
-rw-r--r--libnm-core/tests/test-general.c160
19 files changed, 901 insertions, 48 deletions
diff --git a/libnm-core/Makefile.am b/libnm-core/Makefile.am
index 3335720a4a..695b6a3a58 100644
--- a/libnm-core/Makefile.am
+++ b/libnm-core/Makefile.am
@@ -12,9 +12,11 @@ AM_CPPFLAGS = \
-DNMCONFDIR=\"$(nmconfdir)\" \
-DNMLIBDIR=\"$(nmlibdir)\" \
-DNMPLUGINDIR=\"$(pkglibdir)\" \
+ -DLIBEXECDIR=\"$(libexecdir)\" \
-DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIB \
$(GLIB_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(JANSSON_CFLAGS)
noinst_LTLIBRARIES = libnm-core.la
@@ -36,7 +38,8 @@ GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM
libnm_core_la_LIBADD = \
$(GLIB_LIBS) \
- $(UUID_LIBS)
+ $(UUID_LIBS) \
+ $(JANSSON_LIBS)
libnm_core_la_LDFLAGS = \
$(CODE_COVERAGE_LDFLAGS)
diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c
index 7a0e1f6bda..a823af9bea 100644
--- a/libnm-core/nm-connection.c
+++ b/libnm-core/nm-connection.c
@@ -23,6 +23,7 @@
#include "nm-default.h"
#include <string.h>
+#include <arpa/inet.h>
#include "nm-connection.h"
#include "nm-connection-private.h"
@@ -724,6 +725,7 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
const char *default_ip6_method = NULL;
NMSettingIPConfig *s_ip4, *s_ip6;
NMSetting *setting;
+ gboolean changed = FALSE;
if (parameters)
default_ip6_method = g_hash_table_lookup (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD);
@@ -756,6 +758,12 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
NM_SETTING_IP_CONFIG_METHOD, default_ip4_method,
NULL);
nm_connection_add_setting (self, setting);
+ } else {
+ if ( nm_setting_ip_config_get_gateway (s_ip4)
+ && nm_setting_ip_config_get_never_default (s_ip4)) {
+ g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, NULL, NULL);
+ changed = TRUE;
+ }
}
if (!s_ip6) {
setting = nm_setting_ip6_config_new ();
@@ -765,8 +773,32 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters)
NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
NULL);
nm_connection_add_setting (self, setting);
+ } else {
+ const char *token;
+
+ token = nm_setting_ip6_config_get_token ((NMSettingIP6Config *) s_ip6);
+ if ( token
+ && nm_setting_ip6_config_get_addr_gen_mode ((NMSettingIP6Config *) s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) {
+ struct in6_addr i6_token;
+ char normalized[NM_UTILS_INET_ADDRSTRLEN];
+
+ if ( inet_pton (AF_INET6, token, &i6_token) == 1
+ && _nm_utils_inet6_is_token (&i6_token)) {
+ nm_utils_inet6_ntop (&i6_token, normalized);
+ if (g_strcmp0 (token, normalized)) {
+ g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, normalized, NULL);
+ changed = TRUE;
+ }
+ }
+ }
+
+ if ( nm_setting_ip_config_get_gateway (s_ip6)
+ && nm_setting_ip_config_get_never_default (s_ip6)) {
+ g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, NULL, NULL);
+ changed = TRUE;
+ }
}
- return !s_ip4 || !s_ip6;
+ return !s_ip4 || !s_ip6 || changed;
}
}
diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h
index 2041a1d3a1..a09ee26c48 100644
--- a/libnm-core/nm-core-internal.h
+++ b/libnm-core/nm-core-internal.h
@@ -296,4 +296,8 @@ typedef enum {
NMBondOptionType
_nm_setting_bond_get_option_type (NMSettingBond *setting, const char *name);
+/***********************************************************/
+
+gboolean _nm_utils_inet6_is_token (const struct in6_addr *in6addr);
+
#endif
diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c
index 0ce7641c1d..f62e97c3e6 100644
--- a/libnm-core/nm-keyfile-writer.c
+++ b/libnm-core/nm-keyfile-writer.c
@@ -233,16 +233,23 @@ route_writer (KeyfileWriterInfo *info,
write_ip_values (info->keyfile, setting_name, array, NULL, TRUE);
}
+static int
+sort_hash_keys (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+ return g_strcmp0 (*((const char **) a), *((const char **) b));
+}
+
static void
write_hash_of_string (GKeyFile *file,
NMSetting *setting,
const char *key,
const GValue *value)
{
- GHashTableIter iter;
- const char *property = NULL, *data = NULL;
+ GHashTable *hash;
const char *group_name = nm_setting_get_name (setting);
gboolean vpn_secrets = FALSE;
+ gs_free const char **keys = NULL;
+ guint i, l;
/* Write VPN secrets out to a different group to keep them separate */
if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) {
@@ -250,10 +257,19 @@ write_hash_of_string (GKeyFile *file,
vpn_secrets = TRUE;
}
- g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value));
- while (g_hash_table_iter_next (&iter, (gpointer *) &property, (gpointer *) &data)) {
+ hash = g_value_get_boxed (value);
+ keys = (const char **) g_hash_table_get_keys_as_array (hash, &l);
+ if (!keys)
+ return;
+
+ g_qsort_with_data (keys, l, sizeof (const char *), sort_hash_keys, NULL);
+
+ for (i = 0; keys[i]; i++) {
+ const char *property, *data;
gboolean write_item = TRUE;
+ property = keys[i];
+
/* Handle VPN secrets specially; they are nested in the property's hash;
* we don't want to write them if the secret is not saved, not required,
* or owned by a user's secret agent.
@@ -266,8 +282,10 @@ write_hash_of_string (GKeyFile *file,
write_item = FALSE;
}
- if (write_item)
+ if (write_item) {
+ data = g_hash_table_lookup (hash, property);
nm_keyfile_plugin_kf_set_string (file, group_name, property, data);
+ }
}
}
diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c
index 3e7f93b065..bdcbc23b08 100644
--- a/libnm-core/nm-setting-ip-config.c
+++ b/libnm-core/nm-setting-ip-config.c
@@ -1120,6 +1120,7 @@ typedef struct {
GPtrArray *dns; /* array of IP address strings */
GPtrArray *dns_search; /* array of domain name strings */
GPtrArray *dns_options;/* array of DNS options */
+ gint dns_priority;
GPtrArray *addresses; /* array of NMIPAddress */
GPtrArray *routes; /* array of NMIPRoute */
gint64 route_metric;
@@ -1140,6 +1141,7 @@ enum {
PROP_DNS,
PROP_DNS_SEARCH,
PROP_DNS_OPTIONS,
+ PROP_DNS_PRIORITY,
PROP_ADDRESSES,
PROP_GATEWAY,
PROP_ROUTES,
@@ -1684,6 +1686,22 @@ nm_setting_ip_config_clear_dns_options (NMSettingIPConfig *setting, gboolean is_
}
/**
+ * nm_setting_ip_config_get_dns_priority:
+ * @setting: the #NMSettingIPConfig
+ *
+ * Returns: the priority of DNS servers
+ *
+ * Since: 1.2.4
+ **/
+gint
+nm_setting_ip_config_get_dns_priority (NMSettingIPConfig *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP_CONFIG (setting), 0);
+
+ return NM_SETTING_IP_CONFIG_GET_PRIVATE (setting)->dns_priority;
+}
+
+/**
* nm_setting_ip_config_get_num_addresses:
* @setting: the #NMSettingIPConfig
*
@@ -2274,6 +2292,16 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
}
}
+ if (priv->gateway && priv->never_default) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("a gateway is incompatible with '%s'"),
+ NM_SETTING_IP_CONFIG_NEVER_DEFAULT);
+ g_prefix_error (error, "%s.%s: ", nm_setting_get_name (setting), NM_SETTING_IP_CONFIG_GATEWAY);
+ return NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+ }
+
return TRUE;
}
@@ -2352,6 +2380,9 @@ set_property (GObject *object, guint prop_id,
}
}
break;
+ case PROP_DNS_PRIORITY:
+ priv->dns_priority = g_value_get_int (value);
+ break;
case PROP_ADDRESSES:
g_ptr_array_unref (priv->addresses);
priv->addresses = _nm_utils_copy_array (g_value_get_boxed (value),
@@ -2424,6 +2455,9 @@ get_property (GObject *object, guint prop_id,
case PROP_DNS_OPTIONS:
g_value_take_boxed (value, priv->dns_options ? _nm_utils_ptrarray_to_strv (priv->dns_options) : NULL);
break;
+ case PROP_DNS_PRIORITY:
+ g_value_set_int (value, priv->dns_priority);
+ break;
case PROP_ADDRESSES:
g_value_take_boxed (value, _nm_utils_copy_array (priv->addresses,
(NMUtilsCopyFunc) nm_ip_address_dup,
@@ -2576,6 +2610,34 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class)
G_PARAM_STATIC_STRINGS));
/**
+ * NMSettingIPConfig:dns-priority:
+ *
+ * DNS priority.
+ *
+ * The relative priority to be used when determining the order of DNS
+ * servers in resolv.conf. A lower value means that servers will be on top
+ * of the file. Zero selects the default value, which is 50 for VPNs and
+ * 100 for other connections. When multiple devices have configurations
+ * with the same priority, the one with an active default route will be
+ * preferred. Note that when using dns=dnsmasq the order is meaningless
+ * since dnsmasq forwards queries to all known servers at the same time.
+ *
+ * Negative values have the special effect of excluding other configurations
+ * with a greater priority value; so in presence of at least a negative
+ * priority, only DNS servers from configurations with the lowest priority
+ * value will be used.
+ *
+ * Since: 1.2.4
+ **/
+ g_object_class_install_property
+ (object_class, PROP_DNS_PRIORITY,
+ g_param_spec_int (NM_SETTING_IP_CONFIG_DNS_PRIORITY, "", "",
+ G_MININT32, G_MAXINT32, 0,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
* NMSettingIPConfig:addresses:
*
* Array of IP addresses.
diff --git a/libnm-core/nm-setting-ip-config.h b/libnm-core/nm-setting-ip-config.h
index 79c7e63921..a41c5466c8 100644
--- a/libnm-core/nm-setting-ip-config.h
+++ b/libnm-core/nm-setting-ip-config.h
@@ -136,6 +136,7 @@ void nm_ip_route_set_attribute (NMIPRoute *route,
#define NM_SETTING_IP_CONFIG_DNS "dns"
#define NM_SETTING_IP_CONFIG_DNS_SEARCH "dns-search"
#define NM_SETTING_IP_CONFIG_DNS_OPTIONS "dns-options"
+#define NM_SETTING_IP_CONFIG_DNS_PRIORITY "dns-priority"
#define NM_SETTING_IP_CONFIG_ADDRESSES "addresses"
#define NM_SETTING_IP_CONFIG_GATEWAY "gateway"
#define NM_SETTING_IP_CONFIG_ROUTES "routes"
@@ -219,6 +220,9 @@ gboolean nm_setting_ip_config_remove_dns_option_by_value (NMSettingIPConfig
const char *dns_option);
void nm_setting_ip_config_clear_dns_options (NMSettingIPConfig *setting, gboolean is_set);
+NM_AVAILABLE_IN_1_2_4
+gint nm_setting_ip_config_get_dns_priority (NMSettingIPConfig *setting);
+
guint nm_setting_ip_config_get_num_addresses (NMSettingIPConfig *setting);
NMIPAddress *nm_setting_ip_config_get_address (NMSettingIPConfig *setting,
int idx);
diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c
index 9b479a083d..8f43def019 100644
--- a/libnm-core/nm-setting-ip4-config.c
+++ b/libnm-core/nm-setting-ip4-config.c
@@ -654,6 +654,17 @@ nm_setting_ip4_config_class_init (NMSettingIP4ConfigClass *ip4_class)
* ---end---
*/
+ /* ---ifcfg-rh---
+ * property: dns-priority
+ * variable: IPV4_DNS_PRIORITY(+)
+ * description: The priority for DNS servers of this connection. Lower values have higher priority.
+ * If zero, the default value will be used (50 for VPNs, 100 for other connections).
+ * A negative value prevents DNS from other connections with greater values to be used.
+ * default: 0
+ * example: IPV4_DNS_PRIORITY=20
+ * ---end---
+ */
+
/**
* NMSettingIP4Config:dhcp-client-id:
*
diff --git a/libnm-core/nm-setting-ip6-config.c b/libnm-core/nm-setting-ip6-config.c
index 1f0b11fc98..17afcae79e 100644
--- a/libnm-core/nm-setting-ip6-config.c
+++ b/libnm-core/nm-setting-ip6-config.c
@@ -24,6 +24,7 @@
#include "nm-setting-ip6-config.h"
#include <string.h>
+#include <arpa/inet.h>
#include "nm-setting-private.h"
#include "nm-core-enum-types.h"
@@ -59,6 +60,7 @@ NM_SETTING_REGISTER_TYPE (NM_TYPE_SETTING_IP6_CONFIG)
typedef struct {
NMSettingIP6ConfigPrivacy ip6_privacy;
NMSettingIP6ConfigAddrGenMode addr_gen_mode;
+ char *token;
} NMSettingIP6ConfigPrivate;
@@ -66,6 +68,7 @@ enum {
PROP_0,
PROP_IP6_PRIVACY,
PROP_ADDR_GEN_MODE,
+ PROP_TOKEN,
LAST_PROP
};
@@ -120,6 +123,25 @@ nm_setting_ip6_config_get_addr_gen_mode (NMSettingIP6Config *setting)
return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->addr_gen_mode;
}
+/**
+ * nm_setting_ip6_config_get_token:
+ * @setting: the #NMSettingIP6Config
+ *
+ * Returns the value contained in the #NMSettingIP6Config:token
+ * property.
+ *
+ * Returns: A string.
+ *
+ * Since: 1.2.4
+ **/
+const char *
+nm_setting_ip6_config_get_token (NMSettingIP6Config *setting)
+{
+ g_return_val_if_fail (NM_IS_SETTING_IP6_CONFIG (setting), NULL);
+
+ return NM_SETTING_IP6_CONFIG_GET_PRIVATE (setting)->token;
+}
+
static gboolean
verify (NMSetting *setting, NMConnection *connection, GError **error)
{
@@ -127,6 +149,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
NMSettingIPConfig *s_ip = NM_SETTING_IP_CONFIG (setting);
NMSettingVerifyResult ret;
const char *method;
+ gboolean token_needs_normalization = FALSE;
ret = NM_SETTING_CLASS (nm_setting_ip6_config_parent_class)->verify (setting, connection, error);
if (ret != NM_SETTING_VERIFY_SUCCESS)
@@ -201,6 +224,44 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
return FALSE;
}
+ if (priv->token) {
+ if (priv->addr_gen_mode == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) {
+ struct in6_addr i6_token;
+ char s_token[NM_UTILS_INET_ADDRSTRLEN];
+
+ if ( inet_pton (AF_INET6, priv->token, &i6_token) != 1
+ || !_nm_utils_inet6_is_token (&i6_token)) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("value is not a valid token"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_TOKEN);
+ return FALSE;
+ }
+
+ if (g_strcmp0 (priv->token, nm_utils_inet6_ntop (&i6_token, s_token)))
+ token_needs_normalization = TRUE;
+ } else {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("only makes sense with EUI64 address generation mode"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_TOKEN);
+ return FALSE;
+ }
+ }
+
+ /* Failures from here on, are NORMALIZABLE_ERROR... */
+
+ if (token_needs_normalization) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ _("token is not in canonical form"));
+ g_prefix_error (error, "%s.%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME, NM_SETTING_IP6_CONFIG_TOKEN);
+ return NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
+ }
+
return TRUE;
}
@@ -388,6 +449,10 @@ set_property (GObject *object, guint prop_id,
case PROP_ADDR_GEN_MODE:
priv->addr_gen_mode = g_value_get_int (value);
break;
+ case PROP_TOKEN:
+ g_free (priv->token);
+ priv->token = g_value_dup_string (value);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -407,6 +472,9 @@ get_property (GObject *object, guint prop_id,
case PROP_ADDR_GEN_MODE:
g_value_set_int (value, priv->addr_gen_mode);
break;
+ case PROP_TOKEN:
+ g_value_set_string (value, priv->token);
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -414,6 +482,17 @@ get_property (GObject *object, guint prop_id,
}
static void
+finalize (GObject *object)
+{
+ NMSettingIP6Config *self = NM_SETTING_IP6_CONFIG (object);
+ NMSettingIP6ConfigPrivate *priv = NM_SETTING_IP6_CONFIG_GET_PRIVATE (self);
+
+ g_free (priv->token);
+
+ G_OBJECT_CLASS (nm_setting_ip6_config_parent_class)->finalize (object);
+}
+
+static void
nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (ip6_class);
@@ -424,6 +503,7 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
/* virtual methods */
object_class->set_property = set_property;
object_class->get_property = get_property;
+ object_class->finalize = finalize;
setting_class->verify = verify;
/* Properties */
@@ -556,6 +636,17 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
* ---end---
*/
+ /* ---ifcfg-rh---
+ * property: dns-priority
+ * variable: IPV6_DNS_PRIORITY(+)
+ * description: The priority for DNS servers of this connection. Lower values have higher priority.
+ * If zero, the default value will be used (50 for VPNs, 100 for other connections).
+ * A negative value prevents DNS from other connections with greater values to be used.
+ * default: 0
+ * example: IPV6_DNS_PRIORITY=20
+ * ---end---
+ */
+
/**
* NMSettingIP6Config:ip6-privacy:
*
@@ -645,6 +736,30 @@ nm_setting_ip6_config_class_init (NMSettingIP6ConfigClass *ip6_class)
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS));
+ /**
+ * NMSettingIP6Config:token:
+ *
+ * Configure the token for draft-chown-6man-tokenised-ipv6-identifiers-02
+ * IPv6 tokenized interface identifiers. Useful with eui64 addr-gen-mode.
+ *
+ * Since: 1.2.4
+ **/
+ /* ---ifcfg-rh---
+ * property: token
+ * variable: IPV6_TOKEN
+ * description: The IPv6 tokenized interface identifier token
+ * example: IPV6_TOKEN=::53
+ * ---end---
+ */
+ g_object_class_install_property
+ (object_class, PROP_TOKEN,
+ g_param_spec_string (NM_SETTING_IP6_CONFIG_TOKEN, "", "",
+ NULL,
+ G_PARAM_READWRITE |
+ NM_SETTING_PARAM_INFERRABLE |
+ G_PARAM_STATIC_STRINGS));
+
+
/* IP6-specific property overrides */
/* ---dbus---
diff --git a/libnm-core/nm-setting-ip6-config.h b/libnm-core/nm-setting-ip6-config.h
index 2966e558cb..f5623b4ae0 100644
--- a/libnm-core/nm-setting-ip6-config.h
+++ b/libnm-core/nm-setting-ip6-config.h
@@ -43,6 +43,8 @@ G_BEGIN_DECLS
#define NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE "addr-gen-mode"
+#define NM_SETTING_IP6_CONFIG_TOKEN "token"
+
/**
* NM_SETTING_IP6_CONFIG_METHOD_IGNORE:
*
@@ -156,6 +158,8 @@ NMSetting *nm_setting_ip6_config_new (void);
NMSettingIP6ConfigPrivacy nm_setting_ip6_config_get_ip6_privacy (NMSettingIP6Config *setting);
NM_AVAILABLE_IN_1_2
NMSettingIP6ConfigAddrGenMode nm_setting_ip6_config_get_addr_gen_mode (NMSettingIP6Config *setting);
+NM_AVAILABLE_IN_1_2_4
+const char *nm_setting_ip6_config_get_token (NMSettingIP6Config *setting);
G_END_DECLS
diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c
index 9671f1d915..8d570c9e1c 100644
--- a/libnm-core/nm-setting-team-port.c
+++ b/libnm-core/nm-setting-team-port.c
@@ -85,6 +85,18 @@ nm_setting_team_port_get_config (NMSettingTeamPort *setting)
static gboolean
verify (NMSetting *setting, NMConnection *connection, GError **error)
{
+ NMSettingTeamPortPrivate *priv = NM_SETTING_TEAM_PORT_GET_PRIVATE (setting);
+
+ if (priv->config) {
+ if (!_nm_utils_check_valid_json (priv->config, error)) {
+ g_prefix_error (error,
+ "%s.%s: ",
+ NM_SETTING_TEAM_PORT_SETTING_NAME,
+ NM_SETTING_TEAM_PORT_CONFIG);
+ return FALSE;
+ }
+ }
+
if (connection) {
NMSettingConnection *s_con;
const char *slave_type;
@@ -116,6 +128,31 @@ verify (NMSetting *setting, NMConnection *connection, GError **error)
return TRUE;
}
+static gboolean
+compare_property (NMSetting *setting,
+ NMSetting *other,
+ const GParamSpec *prop_spec,
+ NMSettingCompareFlags flags)
+{
+ NMSettingClass *parent_class;
+
+ /* If we are trying to match a connection in order to assume it (and thus
+ * @flags contains INFERRABLE), use the "relaxed" matching for team
+ * configuration. Otherwise, for all other purposes (including connection
+ * comparison before an update), resort to the default string comparison.
+ */
+ if ( NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
+ && nm_streq0 (prop_spec->name, NM_SETTING_TEAM_PORT_CONFIG)) {
+ return _nm_utils_team_config_equal (NM_SETTING_TEAM_PORT_GET_PRIVATE (setting)->config,
+ NM_SETTING_TEAM_PORT_GET_PRIVATE (other)->config,
+ TRUE);
+ }
+
+ /* Otherwise chain up to parent to handle generic compare */
+ parent_class = NM_SETTING_CLASS (nm_setting_team_port_parent_class);
+ return parent_class->compare_property (setting, other, prop_spec, flags);
+}
+
static void
nm_setting_team_port_init (NMSettingTeamPort *setting)
{
@@ -173,10 +210,11 @@ nm_setting_team_port_class_init (NMSettingTeamPortClass *setting_class)
g_type_class_add_private (setting_class, sizeof (NMSettingTeamPortPrivate));
/* virtual methods */
- object_class->set_property = set_property;
- object_class->get_property = get_property;
- object_class->finalize = finalize;
- parent_class->verify = verify;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->compare_property = compare_property;
+ parent_class->verify = verify;
/* Properties */
/**
diff --git a/libnm-core/nm-setting-team.c b/libnm-core/nm-setting-team.c
index d47b1e72ca..36cd312b6e 100644
--- a/libnm-core/nm-setting-team.c
+++ b/libnm-core/nm-setting-team.c
@@ -27,6 +27,7 @@
#include "nm-utils.h"
#include "nm-utils-private.h"
#include "nm-connection-private.h"
+#include "nm-utils-private.h"
/**
* SECTION:nm-setting-team
@@ -82,7 +83,47 @@ nm_setting_team_get_config (NMSettingTeam *setting)
static gboolean
verify (NMSetting *setting, NMConnection *connection, GError **error)
{
- return _nm_connection_verify_required_interface_name (connection, error);
+ NMSettingTeamPrivate *priv = NM_SETTING_TEAM_GET_PRIVATE (setting);
+
+ if (!_nm_connection_verify_required_interface_name (connection, error))
+ return FALSE;
+
+ if (priv->config) {
+ if (!_nm_utils_check_valid_json (priv->config, error)) {
+ g_prefix_error (error,
+ "%s.%s: ",
+ NM_SETTING_TEAM_SETTING_NAME,
+ NM_SETTING_TEAM_CONFIG);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+compare_property (NMSetting *setting,
+ NMSetting *other,
+ const GParamSpec *prop_spec,
+ NMSettingCompareFlags flags)
+{
+ NMSettingClass *parent_class;
+
+ /* If we are trying to match a connection in order to assume it (and thus
+ * @flags contains INFERRABLE), use the "relaxed" matching for team
+ * configuration. Otherwise, for all other purposes (including connection
+ * comparison before an update), resort to the default string comparison.
+ */
+ if ( NM_FLAGS_HAS (flags, NM_SETTING_COMPARE_FLAG_INFERRABLE)
+ && nm_streq0 (prop_spec->name, NM_SETTING_TEAM_CONFIG)) {
+ return _nm_utils_team_config_equal (NM_SETTING_TEAM_GET_PRIVATE (setting)->config,
+ NM_SETTING_TEAM_GET_PRIVATE (other)->config,
+ FALSE);
+ }
+
+ /* Otherwise chain up to parent to handle generic compare */
+ parent_class = NM_SETTING_CLASS (nm_setting_team_parent_class);
+ return parent_class->compare_property (setting, other, prop_spec, flags);
}
static void
@@ -142,10 +183,11 @@ nm_setting_team_class_init (NMSettingTeamClass *setting_class)
g_type_class_add_private (setting_class, sizeof (NMSettingTeamPrivate));
/* virtual methods */
- object_class->set_property = set_property;
- object_class->get_property = get_property;
- object_class->finalize = finalize;
- parent_class->verify = verify;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+ object_class->finalize = finalize;
+ parent_class->compare_property = compare_property;
+ parent_class->verify = verify;
/* Properties */
/**
diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h
index 68aaaa1c80..611c467d06 100644
--- a/libnm-core/nm-utils-private.h
+++ b/libnm-core/nm-utils-private.h
@@ -31,6 +31,9 @@
gboolean _nm_utils_string_slist_validate (GSList *list,
const char **valid_values);
+gboolean _nm_utils_check_valid_json (const char *json, GError **error);
+gboolean _nm_utils_team_config_equal (const char *conf1, const char *conf2, gboolean port);
+
/* D-Bus transform funcs */
GVariant * _nm_utils_hwaddr_to_dbus (const GValue *prop_value);
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 1080d9ea20..7a87dc18c4 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -33,6 +33,10 @@
#include <gmodule.h>
#include <sys/stat.h>
+#if WITH_JANSSON
+#include <jansson.h>
+#endif
+
#include "nm-utils-private.h"
#include "nm-setting-private.h"
#include "crypto.h"
@@ -3562,6 +3566,40 @@ nm_utils_ipaddr_valid (int family, const char *ip)
}
/**
+ * nm_utils_iinet6_is_token:
+ * @in6addr: the AF_INET6 address structure
+ *
+ * Checks if only the bottom 64bits of the address are set.
+ *
+ * Return value: %TRUE or %FALSE
+ */
+gboolean
+_nm_utils_inet6_is_token (const struct in6_addr *in6addr)
+{
+ if ( in6addr->s6_addr[0]
+ || in6addr->s6_addr[1]
+ || in6addr->s6_addr[2]
+ || in6addr->s6_addr[3]
+ || in6addr->s6_addr[4]
+ || in6addr->s6_addr[5]
+ || in6addr->s6_addr[6]
+ || in6addr->s6_addr[7])
+ return FALSE;
+
+ if ( in6addr->s6_addr[8]
+ || in6addr->s6_addr[9]
+ || in6addr->s6_addr[10]
+ || in6addr->s6_addr[11]
+ || in6addr->s6_addr[12]
+ || in6addr->s6_addr[13]
+ || in6addr->s6_addr[14]
+ || in6addr->s6_addr[15])
+ return TRUE;
+
+ return FALSE;
+}
+
+/**
* nm_utils_check_virtual_device_compatibility:
* @virtual_type: a virtual connection type
* @other_type: a connection type to test against @virtual_type
@@ -4090,3 +4128,140 @@ const char **nm_utils_enum_get_values (GType type, gint from, gint to)
return (const char **) g_ptr_array_free (array, FALSE);
}
+#if WITH_JANSSON
+gboolean
+_nm_utils_check_valid_json (const char *str, GError **error)
+{
+ json_t *json;
+ json_error_t jerror;
+
+ g_return_val_if_fail (!error || !*error, FALSE);
+
+ if (!str || !str[0]) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "value is NULL or empty");
+ return FALSE;
+ }
+
+ json = json_loads (str, 0, &jerror);
+ if (!json) {
+ g_set_error (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "%s at position %d",
+ jerror.text,
+ jerror.position);
+ return FALSE;
+ }
+
+ json_decref (json);
+ return TRUE;
+}
+
+/* json_object_foreach_safe() is only available since Jansson 2.8,
+ * reimplement it */
+#define _json_object_foreach_safe(object, n, key, value) \
+ for (key = json_object_iter_key (json_object_iter (object)), \
+ n = json_object_iter_next (object, json_object_iter_at (object, key)); \
+ key && (value = json_object_iter_value (json_object_iter_at (object, key))); \
+ key = json_object_iter_key (n), \
+ n = json_object_iter_next (object, json_object_iter_at (object, key)))
+
+gboolean
+_nm_utils_team_config_equal (const char *conf1,
+ const char *conf2,
+ gboolean port_config)
+{
+ json_t *json1 = NULL, *json2 = NULL, *json;
+ gs_free char *dump1 = NULL, *dump2 = NULL;
+ json_t *value, *property;
+ json_error_t jerror;
+ const char *key;
+ gboolean ret;
+ void *tmp;
+ int i;
+
+ if (nm_streq0 (conf1, conf2))
+ return TRUE;
+
+ /* A NULL configuration is equivalent to default value '{}' */
+ json1 = json_loads (conf1 ?: "{}", 0, &jerror);
+ if (json1)
+ json2 = json_loads (conf2 ?: "{}", 0, &jerror);
+
+ if (!json1 || !json2) {
+ ret = FALSE;
+ goto out;
+ }
+
+ /* Some properties are added by teamd when missing from the initial
+ * configuration. Add them with the default value if necessary, depending
+ * on the configuration type.
+ */
+ for (i = 0, json = json1; i < 2; i++, json = json2) {
+ if (port_config) {
+ property = json_object_get (json, "link_watch");
+ if (!property) {
+ property = json_object ();
+ json_object_set_new (property, "name", json_string ("ethtool"));
+ json_object_set_new (json, "link_watch", property);
+ }
+ } else {
+ property = json_object_get (json, "runner");
+ if (!property) {
+ property = json_object ();
+ json_object_set_new (property, "name", json_string ("roundrobin"));
+ json_object_set_new (json, "runner", property);
+ }
+ }
+ }
+
+ /* Only consider a given subset of nodes, others can change depending on
+ * current state */
+ for (i = 0, json = json1; i < 2; i++, json = json2) {
+ _json_object_foreach_safe (json, tmp, key, value) {
+ if (!NM_IN_STRSET (key, "runner", "link_watch"))
+ json_object_del (json, key);
+ }
+ }
+
+ dump1 = json_dumps (json1, JSON_INDENT(0) | JSON_ENSURE_ASCII | JSON_SORT_KEYS);
+ dump2 = json_dumps (json2, JSON_INDENT(0) | JSON_ENSURE_ASCII | JSON_SORT_KEYS);
+
+ ret = nm_streq0 (dump1, dump2);
+out:
+
+ if (json1)
+ json_decref (json1);
+ if (json2)
+ json_decref (json2);
+
+ return ret;
+}
+
+#else /* WITH_JANSSON */
+
+gboolean
+_nm_utils_check_valid_json (const char *str, GError **error)
+{
+ if (!str || !str[0]) {
+ g_set_error_literal (error,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY,
+ "value is NULL or empty");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+_nm_utils_team_config_equal (const char *conf1,
+ const char *conf2,
+ gboolean port_config)
+{
+ return nm_streq0 (conf1, conf2);
+}
+#endif
diff --git a/libnm-core/nm-version.h b/libnm-core/nm-version.h
index d7f112bf93..95131f9e87 100644
--- a/libnm-core/nm-version.h
+++ b/libnm-core/nm-version.h
@@ -90,18 +90,10 @@
# define NM_AVAILABLE_IN_1_2
#endif
-#if NM_VERSION_MIN_REQUIRED >= NM_VERSION_1_4
-# define NM_DEPRECATED_IN_1_4 G_DEPRECATED
-# define NM_DEPRECATED_IN_1_4_FOR(f) G_DEPRECATED_FOR(f)
+#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_2_4
+# define NM_AVAILABLE_IN_1_2_4 G_UNAVAILABLE(1,4)
#else
-# define NM_DEPRECATED_IN_1_4
-# define NM_DEPRECATED_IN_1_4_FOR(f)
-#endif
-
-#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_4
-# define NM_AVAILABLE_IN_1_4 G_UNAVAILABLE(1,4)
-#else
-# define NM_AVAILABLE_IN_1_4
+# define NM_AVAILABLE_IN_1_2_4
#endif
#endif /* NM_VERSION_H */
diff --git a/libnm-core/nm-vpn-editor-plugin.c b/libnm-core/nm-vpn-editor-plugin.c
index 6c78c20983..d0539b045b 100644
--- a/libnm-core/nm-vpn-editor-plugin.c
+++ b/libnm-core/nm-vpn-editor-plugin.c
@@ -25,6 +25,7 @@
#include "nm-vpn-editor-plugin.h"
#include <dlfcn.h>
+#include <gmodule.h>
#include "nm-core-internal.h"
@@ -232,7 +233,8 @@ _nm_vpn_editor_plugin_load (const char *plugin_name,
* If @plugin_name is not an absolute path name, it assumes the file
* is in the plugin directory of NetworkManager. In any case, the call
* will do certain checks on the file before passing it to dlopen.
- * A consequence for that is, that you cannot omit the ".so" suffix.
+ * A consequence for that is, that you cannot omit the ".so" suffix
+ * as you could for nm_vpn_editor_plugin_load().
*
* Returns: (transfer full): a new plugin instance or %NULL on error.
*
@@ -255,6 +257,42 @@ nm_vpn_editor_plugin_load_from_file (const char *plugin_name,
error);
}
+/**
+ * nm_vpn_editor_plugin_load:
+ * @plugin_name: The name of the shared library to load.
+ * This path will be directly passed to dlopen() without
+ * further checks.
+ * @check_service: if not-null, check that the loaded plugin advertises
+ * the given service.
+ * @error: on failure the error reason.
+ *
+ * Load the shared libary @plugin_name and create a new
+ * #NMVpnEditorPlugin instace via the #NMVpnEditorPluginFactory
+ * function.
+ *
+ * This is similar to nm_vpn_editor_plugin_load_from_file(), but
+ * it does no validation of the plugin name, instead passes it directly
+ * to dlopen(). If you have the full path to a plugin file,
+ * nm_vpn_editor_plugin_load_from_file() is preferred.
+ *
+ * Returns: (transfer full): a new plugin instance or %NULL on error.
+ *
+ * Since: 1.2.4
+ */
+NMVpnEditorPlugin *
+nm_vpn_editor_plugin_load (const char *plugin_name,
+ const char *check_service,
+ GError **error)
+{
+ return _nm_vpn_editor_plugin_load (plugin_name,
+ FALSE,
+ check_service,
+ -1,
+ NULL,
+ NULL,
+ error);
+}
+
/*********************************************************************/
/**
diff --git a/libnm-core/nm-vpn-editor-plugin.h b/libnm-core/nm-vpn-editor-plugin.h
index 9ff23a8134..62d09cc758 100644
--- a/libnm-core/nm-vpn-editor-plugin.h
+++ b/libnm-core/nm-vpn-editor-plugin.h
@@ -147,6 +147,11 @@ NMVpnEditorPlugin *nm_vpn_editor_plugin_load_from_file (const char *plugin_name
gpointer user_data,
GError **error);
+NM_AVAILABLE_IN_1_2_4
+NMVpnEditorPlugin *nm_vpn_editor_plugin_load (const char *plugin_name,
+ const char *check_service,
+ GError **error);
+
G_END_DECLS
#endif /* __NM_VPN_EDITOR_PLUGIN_H__ */
diff --git a/libnm-core/nm-vpn-plugin-info.c b/libnm-core/nm-vpn-plugin-info.c
index 2e6275e341..3e63f1bd29 100644
--- a/libnm-core/nm-vpn-plugin-info.c
+++ b/libnm-core/nm-vpn-plugin-info.c
@@ -45,6 +45,7 @@ typedef struct {
char *filename;
char *name;
char *service;
+ char *auth_dialog;
char **aliases;
GKeyFile *keyfile;
@@ -188,6 +189,23 @@ _sort_files (LoadDirInfo *a, LoadDirInfo *b)
nm_vpn_plugin_info_get_filename (b->plugin_info));
}
+#define DEFINE_DEFAULT_DIR_LIST(dir) \
+ const char *dir[] = { \
+ /* We load plugins from NM_VPN_PLUGIN_DIR *and* DEFAULT_DIR*, with
+ * preference to the former.
+ *
+ * load user directory with highest priority. */ \
+ _nm_vpn_plugin_info_get_default_dir_user (), \
+ \
+ /* lib directory has higher priority then etc. The reason is that
+ * etc is deprecated and used by old plugins. We expect newer plugins
+ * to install their file in lib, where they have higher priority.
+ *
+ * Optimally, there are no duplicates anyway, so it doesn't really matter. */ \
+ _nm_vpn_plugin_info_get_default_dir_lib (), \
+ _nm_vpn_plugin_info_get_default_dir_etc (), \
+ }
+
/**
* _nm_vpn_plugin_info_get_default_dir_etc:
*
@@ -253,7 +271,10 @@ _nm_vpn_plugin_info_list_load_dir (const char *dirname,
GSList *res = NULL;
guint i;
- g_return_val_if_fail (dirname && dirname[0], NULL);
+ g_return_val_if_fail (dirname, NULL);
+
+ if (!dirname[0])
+ return NULL;
dir = g_dir_open (dirname, 0, NULL);
if (!dir)
@@ -312,21 +333,7 @@ nm_vpn_plugin_info_list_load ()
gint64 uid;
GSList *list = NULL;
GSList *infos, *info;
- const char *dir[] = {
- /* We load plugins from NM_VPN_PLUGIN_DIR *and* DEFAULT_DIR*, with
- * preference to the former.
- *
- * load user directory with highest priority. */
- _nm_vpn_plugin_info_get_default_dir_user (),
-
- /* lib directory has higher priority then etc. The reason is that
- * etc is deprecated and used by old plugins. We expect newer plugins
- * to install their file in lib, where they have higher priority.
- *
- * Optimally, there are no duplicates anyway, so it doesn't really matter. */
- _nm_vpn_plugin_info_get_default_dir_lib (),
- _nm_vpn_plugin_info_get_default_dir_etc (),
- };
+ DEFINE_DEFAULT_DIR_LIST (dir);
uid = getuid ();
@@ -345,6 +352,66 @@ nm_vpn_plugin_info_list_load ()
return list;
}
+/**
+ * nm_vpn_plugin_info_new_search_file:
+ * @name: (allow-none): the name to search for. Either @name or @service
+ * must be present.
+ * @service: (allow-none): the service to search for. Either @name or
+ * @service must be present.
+ *
+ * This has the same effect as doing a full nm_vpn_plugin_info_list_load()
+ * followed by a search for the first matching VPN plugin info that has the
+ * given @name and/or @service.
+ *
+ * Returns: (transfer full): a newly created instance of plugin info
+ * or %NULL if no matching value was found.
+ *
+ * Since: 1.2.4
+ */
+NMVpnPluginInfo *
+nm_vpn_plugin_info_new_search_file (const char *name, const char *service)
+{
+ int i;
+ gint64 uid;
+ NMVpnPluginInfo *plugin_info = NULL;
+ GSList *infos, *info;
+ DEFINE_DEFAULT_DIR_LIST (dir);
+
+ if (!name && !service)
+ g_return_val_if_reached (NULL);
+
+ uid = getuid ();
+
+ for (i = 0; !plugin_info && i < G_N_ELEMENTS (dir); i++) {
+ if ( !dir[i]
+ || _nm_utils_strv_find_first ((char **) dir, i, dir[i]) >= 0)
+ continue;
+
+ /* We still must load the entire directory while searching for the matching
+ * plugin-info. The reason is that reading the directory has no stable
+ * order and we can only sort them after reading the entire directory --
+ * which _nm_vpn_plugin_info_list_load_dir() does. */
+ infos = _nm_vpn_plugin_info_list_load_dir (dir[i], TRUE, uid, NULL, NULL);
+
+ for (info = infos; info; info = info->next) {
+ NMVpnPluginInfo *p = info->data;
+
+ if (name && !nm_streq (nm_vpn_plugin_info_get_name (p), name))
+ continue;
+ if ( service
+ && !nm_streq (nm_vpn_plugin_info_get_service (p), service)
+ && (_nm_utils_strv_find_first (NM_VPN_PLUGIN_INFO_GET_PRIVATE (p)->aliases,
+ -1, service) < 0))
+ continue;
+ plugin_info = g_object_ref (p);
+ break;
+ }
+
+ g_slist_free_full (infos, g_object_unref);
+ }
+ return plugin_info;
+}
+
/*********************************************************************/
static gboolean
@@ -525,7 +592,7 @@ nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service)
/* First, consider the primary service name. */
for (iter = list; iter; iter = iter->next) {
- if (strcmp (NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data)->service, service) == 0)
+ if (strcmp (nm_vpn_plugin_info_get_service (iter->data), service) == 0)
return iter->data;
}
@@ -576,6 +643,77 @@ nm_vpn_plugin_info_get_name (NMVpnPluginInfo *self)
}
/**
+ * nm_vpn_plugin_info_get_service:
+ * @self: plugin info instance
+ *
+ * Returns: (transfer none): the service. Cannot be %NULL.
+ *
+ * Since: 1.2.4
+ */
+const char *
+nm_vpn_plugin_info_get_service (NMVpnPluginInfo *self)
+{
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+
+ return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->service;
+}
+
+/**
+ * nm_vpn_plugin_info_get_auth_dialog:
+ * @self: plugin info instance
+ *
+ * Returns: the absolute path to the auth-dialog helper or %NULL.
+ *
+ * Since: 1.2.4
+ **/
+const char *
+nm_vpn_plugin_info_get_auth_dialog (NMVpnPluginInfo *self)
+{
+ NMVpnPluginInfoPrivate *priv;
+
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
+
+ priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
+
+ if (G_UNLIKELY (priv->auth_dialog == NULL)) {
+ const char *s;
+
+ s = g_hash_table_lookup (priv->keys, _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "auth-dialog"));
+ if (!s || !s[0])
+ priv->auth_dialog = g_strdup ("");
+ else if (g_path_is_absolute (s))
+ priv->auth_dialog = g_strdup (s);
+ else {
+ /* for relative paths, we take the basename and assume it's in LIBEXECDIR. */
+ gs_free char *prog_basename = g_path_get_basename (s);
+
+ priv->auth_dialog = g_build_filename (LIBEXECDIR, prog_basename, NULL);
+ }
+ }
+
+ return priv->auth_dialog[0] ? priv->auth_dialog : NULL;
+}
+
+/**
+ * nm_vpn_plugin_info_supports_hints:
+ * @self: plugin info instance
+ *
+ * Returns: %TRUE if the supports hints for secret requests, otherwise %FALSE
+ *
+ * Since: 1.2.4
+ */
+gboolean
+nm_vpn_plugin_info_supports_hints (NMVpnPluginInfo *self)
+{
+ const char *s;
+
+ g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), FALSE);
+
+ s = nm_vpn_plugin_info_lookup_property (self, NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "supports-hints");
+ return _nm_utils_ascii_str_to_bool (s, FALSE);
+}
+
+/**
* nm_vpn_plugin_info_get_plugin:
* @self: plugin info instance
*
@@ -753,7 +891,7 @@ nm_vpn_plugin_info_load_editor_plugin (NMVpnPluginInfo *self, GError **error)
priv->editor_plugin_loaded = TRUE;
priv->editor_plugin = nm_vpn_editor_plugin_load_from_file (plugin_filename,
- priv->service,
+ nm_vpn_plugin_info_get_service (self),
getuid (),
NULL,
NULL,
@@ -947,6 +1085,7 @@ finalize (GObject *object)
g_free (priv->name);
g_free (priv->service);
+ g_free (priv->auth_dialog);
g_strfreev (priv->aliases);
g_free (priv->filename);
g_hash_table_unref (priv->keys);
diff --git a/libnm-core/nm-vpn-plugin-info.h b/libnm-core/nm-vpn-plugin-info.h
index c7fef8574a..fce07e6d1b 100644
--- a/libnm-core/nm-vpn-plugin-info.h
+++ b/libnm-core/nm-vpn-plugin-info.h
@@ -73,14 +73,24 @@ NMVpnPluginInfo *nm_vpn_plugin_info_new_with_data (const char *filename,
GKeyFile *keyfile,
GError **error);
+NM_AVAILABLE_IN_1_2_4
+NMVpnPluginInfo *nm_vpn_plugin_info_new_search_file (const char *name,
+ const char *service);
+
NM_AVAILABLE_IN_1_2
const char *nm_vpn_plugin_info_get_name (NMVpnPluginInfo *self);
NM_AVAILABLE_IN_1_2
const char *nm_vpn_plugin_info_get_filename (NMVpnPluginInfo *self);
+NM_AVAILABLE_IN_1_2_4
+const char *nm_vpn_plugin_info_get_service (NMVpnPluginInfo *self);
NM_AVAILABLE_IN_1_2
const char *nm_vpn_plugin_info_get_plugin (NMVpnPluginInfo *self);
NM_AVAILABLE_IN_1_2
const char *nm_vpn_plugin_info_get_program (NMVpnPluginInfo *self);
+NM_AVAILABLE_IN_1_2_4
+const char *nm_vpn_plugin_info_get_auth_dialog (NMVpnPluginInfo *self);
+NM_AVAILABLE_IN_1_2_4
+gboolean nm_vpn_plugin_info_supports_hints (NMVpnPluginInfo *self);
NM_AVAILABLE_IN_1_2
gboolean nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self);
NM_AVAILABLE_IN_1_2
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index dcbcb88021..c80f55893f 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -1968,6 +1968,7 @@ test_connection_diff_a_only (void)
{ NM_SETTING_IP_CONFIG_NEVER_DEFAULT, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_IP_CONFIG_MAY_FAIL, NM_SETTING_DIFF_RESULT_IN_A },
{ NM_SETTING_IP_CONFIG_DAD_TIMEOUT, NM_SETTING_DIFF_RESULT_IN_A },
+ { NM_SETTING_IP_CONFIG_DNS_PRIORITY, NM_SETTING_DIFF_RESULT_IN_A },
{ NULL, NM_SETTING_DIFF_RESULT_UNKNOWN },
} },
};
@@ -3717,6 +3718,57 @@ test_connection_normalize_infiniband_mtu (void)
}
static void
+test_connection_normalize_gateway_never_default (void)
+{
+ gs_unref_object NMConnection *con = NULL;
+ NMSettingIPConfig *s_ip4, *s_ip6;
+ NMIPAddress *addr;
+ gs_free_error GError *error = NULL;
+
+ con = nmtst_create_minimal_connection ("test1", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL);
+ nmtst_assert_connection_verifies_and_normalizable (con);
+
+ s_ip4 = (NMSettingIPConfig *) nm_setting_ip4_config_new ();
+ g_object_set (G_OBJECT (s_ip4),
+ NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP4_CONFIG_METHOD_MANUAL,
+ NULL);
+
+ addr = nm_ip_address_new (AF_INET, "1.1.1.1", 24, &error);
+ g_assert_no_error (error);
+ nm_setting_ip_config_add_address (s_ip4, addr);
+ nm_ip_address_unref (addr);
+
+ g_object_set (s_ip4,
+ NM_SETTING_IP_CONFIG_GATEWAY, "1.1.1.254",
+ NM_SETTING_IP_CONFIG_NEVER_DEFAULT, FALSE,
+ NULL);
+
+ s_ip6 = (NMSettingIPConfig *) nm_setting_ip6_config_new ();
+ g_object_set (s_ip6,
+ NM_SETTING_IP_CONFIG_METHOD, NM_SETTING_IP6_CONFIG_METHOD_AUTO,
+ NULL);
+
+ nm_connection_add_setting (con, (NMSetting *) s_ip4);
+ nm_connection_add_setting (con, (NMSetting *) s_ip6);
+
+ nmtst_assert_connection_verifies_without_normalization (con);
+ g_assert_cmpstr ("1.1.1.254", ==, nm_setting_ip_config_get_gateway (s_ip4));
+
+ /* Now set never-default to TRUE and check that the gateway is
+ * removed during normalization
+ * */
+ g_object_set (s_ip4,
+ NM_SETTING_IP_CONFIG_NEVER_DEFAULT, TRUE,
+ NULL);
+
+ nmtst_assert_connection_verifies_after_normalization (con,
+ NM_CONNECTION_ERROR,
+ NM_CONNECTION_ERROR_INVALID_PROPERTY);
+ nmtst_connection_normalize (con);
+ g_assert_cmpstr (NULL, ==, nm_setting_ip_config_get_gateway (s_ip4));
+}
+
+static void
test_setting_ip4_gateway (void)
{
NMConnection *conn;
@@ -4382,6 +4434,110 @@ test_nm_utils_dns_option_find_idx (void)
/******************************************************************************/
+static void
+_json_config_check_valid (const char *conf, gboolean expected)
+{
+ GError *error = NULL;
+ gboolean res;
+
+ res = _nm_utils_check_valid_json (conf, &error);
+ g_assert_cmpint (res, ==, expected);
+ g_assert (res || error);
+}
+
+static void
+test_nm_utils_check_valid_json (void)
+{
+ _json_config_check_valid (NULL, FALSE);
+ _json_config_check_valid ("", FALSE);
+#if WITH_JANSSON
+ _json_config_check_valid ("{ }", TRUE);
+ _json_config_check_valid ("{ \"a\" : 1 }", TRUE);
+ _json_config_check_valid ("{ \"a\" : }", FALSE);
+#else
+ /* Without JSON library everything except empty string is considered valid */
+ _json_config_check_valid ("{ }", TRUE);
+ _json_config_check_valid ("{'%!-a1", TRUE);
+#endif
+}
+
+static void
+_team_config_equal_check (const char *conf1,
+ const char *conf2,
+ gboolean port_config,
+ gboolean expected)
+{
+ g_assert_cmpint (_nm_utils_team_config_equal (conf1, conf2, port_config), ==, expected);
+}
+
+static void
+test_nm_utils_team_config_equal (void)
+{
+#if WITH_JANSSON
+ _team_config_equal_check ("", "", TRUE, TRUE);
+ _team_config_equal_check ("{}",
+ "{ }",
+ TRUE,
+ TRUE);
+ _team_config_equal_check ("{}",
+ "{",
+ TRUE,
+ FALSE);
+
+ /* team config */
+ _team_config_equal_check ("{ }",
+ "{ \"runner\" : { \"name\" : \"roundrobin\"} }",
+ FALSE,
+ TRUE);
+ _team_config_equal_check ("{ }",
+ "{ \"runner\" : { \"name\" : \"random\"} }",
+ FALSE,
+ FALSE);
+ _team_config_equal_check ("{ \"runner\" : { \"name\" : \"roundrobin\"} }",
+ "{ \"runner\" : { \"name\" : \"random\"} }",
+ FALSE,
+ FALSE);
+ _team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"} }",
+ "{ \"runner\" : { \"name\" : \"random\"} }",
+ FALSE,
+ TRUE);
+ _team_config_equal_check ("{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth0\" : {} } }",
+ "{ \"runner\" : { \"name\" : \"random\"}, \"ports\" : { \"eth1\" : {} } }",
+ FALSE,
+ TRUE);
+
+ /* team port config */
+ _team_config_equal_check ("{ }",
+ "{ \"link_watch\" : { \"name\" : \"ethtool\"} }",
+ TRUE,
+ TRUE);
+ _team_config_equal_check ("{ }",
+ "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
+ TRUE,
+ FALSE);
+ _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"ethtool\"} }",
+ "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
+ TRUE,
+ FALSE);
+ _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
+ "{ \"link_watch\" : { \"name\" : \"arp_ping\"} }",
+ TRUE,
+ TRUE);
+ _team_config_equal_check ("{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth0\" : {} } }",
+ "{ \"link_watch\" : { \"name\" : \"arp_ping\"}, \"ports\" : { \"eth1\" : {} } }",
+ TRUE,
+ TRUE);
+#else
+ /* Without JSON library, strings are compared for equality */
+ _team_config_equal_check ("", "", TRUE, TRUE);
+ _team_config_equal_check ("", " ", TRUE, FALSE);
+ _team_config_equal_check ("{ \"a\": 1 }", "{ \"a\": 1 }", TRUE, TRUE);
+ _team_config_equal_check ("{ \"a\": 1 }", "{ \"a\": 1 }", TRUE, FALSE);
+#endif
+}
+
+/******************************************************************************/
+
enum TEST_IS_POWER_OF_TWP_ENUM_SIGNED {
_DUMMY_1 = -1,
};
@@ -5010,6 +5166,7 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/test_connection_normalize_slave_type_1", test_connection_normalize_slave_type_1);
g_test_add_func ("/core/general/test_connection_normalize_slave_type_2", test_connection_normalize_slave_type_2);
g_test_add_func ("/core/general/test_connection_normalize_infiniband_mtu", test_connection_normalize_infiniband_mtu);
+ g_test_add_func ("/core/general/test_connection_normalize_gateway_never_default", test_connection_normalize_gateway_never_default);
g_test_add_func ("/core/general/test_setting_connection_permissions_helpers", test_setting_connection_permissions_helpers);
g_test_add_func ("/core/general/test_setting_connection_permissions_property", test_setting_connection_permissions_property);
@@ -5066,7 +5223,8 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/_nm_utils_dns_option_validate", test_nm_utils_dns_option_validate);
g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx);
-
+ g_test_add_func ("/core/general/_nm_utils_validate_json", test_nm_utils_check_valid_json);
+ 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);
return g_test_run ();