diff options
Diffstat (limited to 'libnm-core')
-rw-r--r-- | libnm-core/Makefile.am | 7 | ||||
-rw-r--r-- | libnm-core/nm-connection.c | 34 | ||||
-rw-r--r-- | libnm-core/nm-core-internal.h | 4 | ||||
-rw-r--r-- | libnm-core/nm-keyfile-writer.c | 28 | ||||
-rw-r--r-- | libnm-core/nm-setting-ip-config.c | 62 | ||||
-rw-r--r-- | libnm-core/nm-setting-ip-config.h | 4 | ||||
-rw-r--r-- | libnm-core/nm-setting-ip4-config.c | 11 | ||||
-rw-r--r-- | libnm-core/nm-setting-ip6-config.c | 115 | ||||
-rw-r--r-- | libnm-core/nm-setting-ip6-config.h | 4 | ||||
-rw-r--r-- | libnm-core/nm-setting-team-port.c | 46 | ||||
-rw-r--r-- | libnm-core/nm-setting-team.c | 52 | ||||
-rw-r--r-- | libnm-core/nm-utils-private.h | 3 | ||||
-rw-r--r-- | libnm-core/nm-utils.c | 175 | ||||
-rw-r--r-- | libnm-core/nm-version.h | 14 | ||||
-rw-r--r-- | libnm-core/nm-vpn-editor-plugin.c | 40 | ||||
-rw-r--r-- | libnm-core/nm-vpn-editor-plugin.h | 5 | ||||
-rw-r--r-- | libnm-core/nm-vpn-plugin-info.c | 175 | ||||
-rw-r--r-- | libnm-core/nm-vpn-plugin-info.h | 10 | ||||
-rw-r--r-- | libnm-core/tests/test-general.c | 160 |
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 (); |