diff options
Diffstat (limited to 'libnm-core')
49 files changed, 2638 insertions, 283 deletions
diff --git a/libnm-core/Makefile.am b/libnm-core/Makefile.am index 3335720a..695b6a3a 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/Makefile.in b/libnm-core/Makefile.in index bdef6648..ca804e3c 100644 --- a/libnm-core/Makefile.in +++ b/libnm-core/Makefile.in @@ -101,8 +101,9 @@ host_triplet = @host@ @WITH_NSS_TRUE@am__append_6 = $(NSS_LIBS) subdir = libnm-core ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ - $(top_srcdir)/m4/compiler_warnings.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \ + $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_options.m4 \ $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gnome-code-coverage.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ @@ -127,10 +128,10 @@ am__DEPENDENCIES_1 = @WITH_GNUTLS_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) @WITH_NSS_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) libnm_core_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_3) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) am__libnm_core_la_SOURCES_DIST = \ - $(top_builddir)/shared/nm-shared-utils.c \ + $(top_builddir)/shared/nm-utils/nm-shared-utils.c \ $(core_build)/nm-core-enum-types.c $(core)/crypto.c \ $(core)/nm-connection.c $(core)/nm-dbus-utils.c \ $(core)/nm-errors.c $(core)/nm-keyfile-reader.c \ @@ -154,12 +155,12 @@ am__libnm_core_la_SOURCES_DIST = \ $(core)/nm-setting-wireless.c $(core)/nm-setting.c \ $(core)/nm-simple-connection.c $(core)/nm-utils.c \ $(core)/nm-vpn-editor-plugin.c $(core)/nm-vpn-plugin-info.c \ - $(top_builddir)/shared/nm-shared-utils.h $(core)/crypto.h \ - $(core)/nm-connection-private.h $(core)/nm-core-internal.h \ - $(core)/nm-core-types-internal.h $(core)/nm-keyfile-internal.h \ - $(core)/nm-keyfile-utils.h $(core)/nm-property-compare.h \ - $(core)/nm-setting-private.h $(core)/nm-utils-private.h \ - crypto_gnutls.c crypto_nss.c + $(top_builddir)/shared/nm-utils/nm-shared-utils.h \ + $(core)/crypto.h $(core)/nm-connection-private.h \ + $(core)/nm-core-internal.h $(core)/nm-core-types-internal.h \ + $(core)/nm-keyfile-internal.h $(core)/nm-keyfile-utils.h \ + $(core)/nm-property-compare.h $(core)/nm-setting-private.h \ + $(core)/nm-utils-private.h crypto_gnutls.c crypto_nss.c am__objects_1 = nm-shared-utils.lo nm-core-enum-types.lo crypto.lo \ nm-connection.lo nm-dbus-utils.lo nm-errors.lo \ nm-keyfile-reader.lo nm-keyfile-utils.lo nm-keyfile-writer.lo \ @@ -359,6 +360,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@ DHCPCD_PATH = @DHCPCD_PATH@ DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ +DL_LIBS = @DL_LIBS@ DNSMASQ_PATH = @DNSMASQ_PATH@ DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@ DSYMUTIL = @DSYMUTIL@ @@ -415,13 +417,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ IPTABLES_PATH = @IPTABLES_PATH@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@ LIBAUDIT_LIBS = @LIBAUDIT_LIBS@ -LIBDL = @LIBDL@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ @@ -433,6 +436,8 @@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@ +LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@ LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ @@ -493,6 +498,8 @@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ READLINE_LIBS = @READLINE_LIBS@ +SANITIZERS = @SANITIZERS@ +SANITIZER_ENV = @SANITIZER_ENV@ SED = @SED@ SELINUX_CFLAGS = @SELINUX_CFLAGS@ SELINUX_LIBS = @SELINUX_LIBS@ @@ -528,6 +535,7 @@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_RANLIB = @ac_ct_RANLIB@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ @@ -593,10 +601,10 @@ SUBDIRS = . tests AM_CPPFLAGS = -I${top_srcdir}/shared -I${top_builddir}/shared \ -DG_LOG_DOMAIN=\""libnm"\" -DLOCALEDIR=\"$(datadir)/locale\" \ -DNMCONFDIR=\"$(nmconfdir)\" -DNMLIBDIR=\"$(nmlibdir)\" \ - -DNMPLUGINDIR=\"$(pkglibdir)\" \ + -DNMPLUGINDIR=\"$(pkglibdir)\" -DLIBEXECDIR=\"$(libexecdir)\" \ -DNETWORKMANAGER_COMPILATION=NM_NETWORKMANAGER_COMPILATION_LIB \ - $(GLIB_CFLAGS) $(CODE_COVERAGE_CFLAGS) $(am__append_1) \ - $(am__append_4) + $(GLIB_CFLAGS) $(CODE_COVERAGE_CFLAGS) $(JANSSON_CFLAGS) \ + $(am__append_1) $(am__append_4) noinst_LTLIBRARIES = libnm-core.la core = $(top_srcdir)/libnm-core core_build = $(top_builddir)/libnm-core @@ -647,7 +655,7 @@ libnm_core_headers = \ $(core)/nm-vpn-plugin-info.h libnm_core_private_headers = \ - $(top_builddir)/shared/nm-shared-utils.h \ + $(top_builddir)/shared/nm-utils/nm-shared-utils.h \ $(core)/crypto.h \ $(core)/nm-connection-private.h \ $(core)/nm-core-internal.h \ @@ -659,7 +667,7 @@ libnm_core_private_headers = \ $(core)/nm-utils-private.h libnm_core_sources = \ - $(top_builddir)/shared/nm-shared-utils.c \ + $(top_builddir)/shared/nm-utils/nm-shared-utils.c \ $(core_build)/nm-core-enum-types.c \ $(core)/crypto.c \ $(core)/nm-connection.c \ @@ -717,8 +725,8 @@ GLIB_GENERATED = nm-core-enum-types.h nm-core-enum-types.c nm_core_enum_types_sources = $(filter-out %nm-core-enum-types.h,$(libnminclude_HEADERS)) GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM -libnm_core_la_LIBADD = $(GLIB_LIBS) $(UUID_LIBS) $(am__append_3) \ - $(am__append_6) +libnm_core_la_LIBADD = $(GLIB_LIBS) $(UUID_LIBS) $(JANSSON_LIBS) \ + $(am__append_3) $(am__append_6) libnm_core_la_LDFLAGS = \ $(CODE_COVERAGE_LDFLAGS) @@ -850,12 +858,12 @@ distclean-compile: @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< -nm-shared-utils.lo: $(top_builddir)/shared/nm-shared-utils.c -@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-shared-utils.lo -MD -MP -MF $(DEPDIR)/nm-shared-utils.Tpo -c -o nm-shared-utils.lo `test -f '$(top_builddir)/shared/nm-shared-utils.c' || echo '$(srcdir)/'`$(top_builddir)/shared/nm-shared-utils.c +nm-shared-utils.lo: $(top_builddir)/shared/nm-utils/nm-shared-utils.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-shared-utils.lo -MD -MP -MF $(DEPDIR)/nm-shared-utils.Tpo -c -o nm-shared-utils.lo `test -f '$(top_builddir)/shared/nm-utils/nm-shared-utils.c' || echo '$(srcdir)/'`$(top_builddir)/shared/nm-utils/nm-shared-utils.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nm-shared-utils.Tpo $(DEPDIR)/nm-shared-utils.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/shared/nm-shared-utils.c' object='nm-shared-utils.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$(top_builddir)/shared/nm-utils/nm-shared-utils.c' object='nm-shared-utils.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-shared-utils.lo `test -f '$(top_builddir)/shared/nm-shared-utils.c' || echo '$(srcdir)/'`$(top_builddir)/shared/nm-shared-utils.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o nm-shared-utils.lo `test -f '$(top_builddir)/shared/nm-utils/nm-shared-utils.c' || echo '$(srcdir)/'`$(top_builddir)/shared/nm-utils/nm-shared-utils.c nm-core-enum-types.lo: $(core_build)/nm-core-enum-types.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT nm-core-enum-types.lo -MD -MP -MF $(DEPDIR)/nm-core-enum-types.Tpo -c -o nm-core-enum-types.lo `test -f '$(core_build)/nm-core-enum-types.c' || echo '$(srcdir)/'`$(core_build)/nm-core-enum-types.c diff --git a/libnm-core/Makefile.libnm-core b/libnm-core/Makefile.libnm-core index 0038ce23..52c71da5 100644 --- a/libnm-core/Makefile.libnm-core +++ b/libnm-core/Makefile.libnm-core @@ -52,7 +52,7 @@ libnm_core_headers = \ $(core)/nm-vpn-plugin-info.h libnm_core_private_headers = \ - $(top_builddir)/shared/nm-shared-utils.h \ + $(top_builddir)/shared/nm-utils/nm-shared-utils.h \ $(core)/crypto.h \ $(core)/nm-connection-private.h \ $(core)/nm-core-internal.h \ @@ -64,7 +64,7 @@ libnm_core_private_headers = \ $(core)/nm-utils-private.h libnm_core_sources = \ - $(top_builddir)/shared/nm-shared-utils.c \ + $(top_builddir)/shared/nm-utils/nm-shared-utils.c \ $(core_build)/nm-core-enum-types.c \ $(core)/crypto.c \ $(core)/nm-connection.c \ diff --git a/libnm-core/crypto.h b/libnm-core/crypto.h index c77ada5a..8c4cfefe 100644 --- a/libnm-core/crypto.h +++ b/libnm-core/crypto.h @@ -24,8 +24,6 @@ #ifndef __CRYPTO_H__ #define __CRYPTO_H__ -#include "nm-default.h" - #define MD5_HASH_LEN 20 #define CIPHER_DES_EDE3_CBC "DES-EDE3-CBC" #define CIPHER_DES_CBC "DES-CBC" diff --git a/libnm-core/nm-connection.c b/libnm-core/nm-connection.c index 04385aae..f39d41c5 100644 --- a/libnm-core/nm-connection.c +++ b/libnm-core/nm-connection.c @@ -23,10 +23,12 @@ #include "nm-default.h" #include <string.h> +#include <arpa/inet.h> #include "nm-connection.h" #include "nm-connection-private.h" #include "nm-utils.h" +#include "nm-utils-private.h" #include "nm-setting-private.h" #include "nm-core-internal.h" @@ -780,6 +782,24 @@ _normalize_ip_config (NMConnection *self, GHashTable *parameters) 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); @@ -842,6 +862,84 @@ _normalize_bond_mode (NMConnection *self, GHashTable *parameters) return FALSE; } +static gboolean +_normalize_wireless_mac_address_randomization (NMConnection *self, GHashTable *parameters) +{ + NMSettingWireless *s_wifi = nm_connection_get_setting_wireless (self); + const char *cloned_mac_address; + NMSettingMacRandomization mac_address_randomization; + + if (!s_wifi) + return FALSE; + + mac_address_randomization = nm_setting_wireless_get_mac_address_randomization (s_wifi); + if (!NM_IN_SET (mac_address_randomization, + NM_SETTING_MAC_RANDOMIZATION_DEFAULT, + NM_SETTING_MAC_RANDOMIZATION_NEVER, + NM_SETTING_MAC_RANDOMIZATION_ALWAYS)) + return FALSE; + + cloned_mac_address = nm_setting_wireless_get_cloned_mac_address (s_wifi); + if (cloned_mac_address) { + if (nm_streq (cloned_mac_address, "random")) { + if (mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_ALWAYS) + return FALSE; + mac_address_randomization = NM_SETTING_MAC_RANDOMIZATION_ALWAYS; + } else if (nm_streq (cloned_mac_address, "permanent")) { + if (mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_NEVER) + return FALSE; + mac_address_randomization = NM_SETTING_MAC_RANDOMIZATION_NEVER; + } else { + if (mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) + return FALSE; + mac_address_randomization = NM_SETTING_MAC_RANDOMIZATION_DEFAULT; + } + g_object_set (s_wifi, NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION, mac_address_randomization, NULL); + return TRUE; + } + if (mac_address_randomization != NM_SETTING_MAC_RANDOMIZATION_DEFAULT) { + g_object_set (s_wifi, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_ALWAYS + ? "random" : "permanent", + NULL); + return TRUE; + } + return FALSE; +} + +static gboolean +_normalize_team_config (NMConnection *self, GHashTable *parameters) +{ + NMSettingTeam *s_team = nm_connection_get_setting_team (self); + + if (s_team) { + const char *config = nm_setting_team_get_config (s_team); + + if (config && !_nm_utils_check_valid_json (config, NULL)) { + g_object_set (s_team, NM_SETTING_TEAM_CONFIG, NULL, NULL); + return TRUE; + } + } + return FALSE; +} + +static gboolean +_normalize_team_port_config (NMConnection *self, GHashTable *parameters) +{ + NMSettingTeamPort *s_team_port = nm_connection_get_setting_team_port (self); + + if (s_team_port) { + const char *config = nm_setting_team_port_get_config (s_team_port); + + if (config && !_nm_utils_check_valid_json (config, NULL)) { + g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, NULL, NULL); + return TRUE; + } + } + return FALSE; +} + /** * nm_connection_verify: * @connection: the #NMConnection to verify @@ -1084,6 +1182,9 @@ nm_connection_normalize (NMConnection *connection, was_modified |= _normalize_ip_config (connection, parameters); was_modified |= _normalize_infiniband_mtu (connection, parameters); was_modified |= _normalize_bond_mode (connection, parameters); + was_modified |= _normalize_wireless_mac_address_randomization (connection, parameters); + was_modified |= _normalize_team_config (connection, parameters); + was_modified |= _normalize_team_port_config (connection, parameters); /* Verify anew. */ success = _nm_connection_verify (connection, error); diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 2041a1d3..94e473d3 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -34,7 +34,6 @@ **/ -#include "nm-default.h" #include "nm-connection.h" #include "nm-core-enum-types.h" #include "nm-setting-8021x.h" @@ -124,6 +123,10 @@ guint32 _nm_setting_get_setting_priority (NMSetting *setting); gboolean _nm_setting_get_property (NMSetting *setting, const char *name, GValue *value); +guint _nm_utils_hwaddr_length (const char *asc); + +char *_nm_utils_bin2str (gconstpointer addr, gsize length, gboolean upper_case); + GSList * _nm_utils_hash_values_to_slist (GHashTable *hash); GHashTable *_nm_utils_copy_strdict (GHashTable *strdict); @@ -145,9 +148,6 @@ gssize _nm_utils_ptrarray_find_first (gpointer *list, gssize len, gconstpointer gssize _nm_utils_ptrarray_find_binary_search (gpointer *list, gsize len, gpointer needle, GCompareDataFunc cmpfcn, gpointer user_data); -gboolean _nm_utils_string_in_list (const char *str, - const char **valid_strings); - gssize _nm_utils_strv_find_first (char **list, gssize len, const char *needle); char **_nm_utils_strv_cleanup (char **strv, @@ -284,6 +284,16 @@ void _nm_setting_vlan_get_priorities (NMSettingVlan *setting, /***********************************************************/ +struct ether_addr; + +gboolean _nm_utils_generate_mac_address_mask_parse (const char *value, + struct ether_addr *out_mask, + struct ether_addr **out_ouis, + gsize *out_ouis_len, + GError **error); + +/***********************************************************/ + typedef enum { NM_BOND_OPTION_TYPE_INT, NM_BOND_OPTION_TYPE_STRING, @@ -296,4 +306,24 @@ typedef enum { NMBondOptionType _nm_setting_bond_get_option_type (NMSettingBond *setting, const char *name); +/***********************************************************/ + +typedef enum { + NM_BOND_MODE_UNKNOWN = 0, + NM_BOND_MODE_ROUNDROBIN, + NM_BOND_MODE_ACTIVEBACKUP, + NM_BOND_MODE_XOR, + NM_BOND_MODE_BROADCAST, + NM_BOND_MODE_8023AD, + NM_BOND_MODE_TLB, + NM_BOND_MODE_ALB, +} NMBondMode; + +NMBondMode _nm_setting_bond_mode_from_string (const char *str); +gboolean _nm_setting_bond_option_supported (const char *option, NMBondMode mode); + +/***********************************************************/ + +gboolean _nm_utils_inet6_is_token (const struct in6_addr *in6addr); + #endif diff --git a/libnm-core/nm-dbus-interface.h b/libnm-core/nm-dbus-interface.h index 1e0fbe68..e5b1b3d6 100644 --- a/libnm-core/nm-dbus-interface.h +++ b/libnm-core/nm-dbus-interface.h @@ -68,6 +68,7 @@ #define NM_DBUS_INTERFACE_DEVICE_VXLAN NM_DBUS_INTERFACE_DEVICE ".Vxlan" #define NM_DBUS_INTERFACE_DEVICE_GRE NM_DBUS_INTERFACE_DEVICE ".Gre" #define NM_DBUS_INTERFACE_DEVICE_IP_TUNNEL NM_DBUS_INTERFACE_DEVICE ".IPTunnel" +#define NM_DBUS_INTERFACE_DEVICE_STATISTICS NM_DBUS_INTERFACE_DEVICE ".Statistics" #define NM_DBUS_INTERFACE_SETTINGS "org.freedesktop.NetworkManager.Settings" #define NM_DBUS_PATH_SETTINGS "/org/freedesktop/NetworkManager/Settings" @@ -689,4 +690,38 @@ typedef enum { NM_IP_TUNNEL_MODE_VTI6 = 9, } NMIPTunnelMode; + +/** + * NMCheckpointCreateFlags: + * @NM_CHECKPOINT_CREATE_FLAG_NONE: no flags + * @NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL: when creating + * a new checkpoint, destroy all existing ones. + * + * The flags for CheckpointCreate call + * + * Since: 1.4 + */ +typedef enum { /*< skip >*/ + NM_CHECKPOINT_CREATE_FLAG_NONE = 0, + NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL = 0x01, +} NMCheckpointCreateFlags; + +/** + * NMRollbackResult: + * @NM_ROLLBACK_RESULT_OK: the rollback succeeded. + * @NM_ROLLBACK_RESULT_ERR_NO_DEVICE: the device no longer exists. + * @NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED: the device is now unmanaged. + * @NM_ROLLBACK_RESULT_ERR_FAILED: other errors during rollback. + * + * The result of a checkpoint Rollback() operation for a specific device. + * + * Since: 1.4 + **/ +typedef enum { /*< skip >*/ + NM_ROLLBACK_RESULT_OK = 0, + NM_ROLLBACK_RESULT_ERR_NO_DEVICE = 1, + NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED = 2, + NM_ROLLBACK_RESULT_ERR_FAILED = 3, +} NMRollbackResult; + #endif /* __NM_DBUS_INTERFACE_H__ */ diff --git a/libnm-core/nm-dbus-types.xml b/libnm-core/nm-dbus-types.xml index 6d5d01bb..1fae76f6 100644 --- a/libnm-core/nm-dbus-types.xml +++ b/libnm-core/nm-dbus-types.xml @@ -1288,4 +1288,74 @@ </refsect3> </refsect2> + <refsect2 id="NMCheckpointCreateFlags" role="enum"> + <title>enum NMCheckpointCreateFlags</title> + <indexterm zone="NMCheckpointCreateFlags"> + <primary>NMCheckpointCreateFlags</primary> + </indexterm> + <para><para>The flags for CheckpointCreate call</para><para>Since: 1.4</para><para></para></para> + <refsect3 role="enum_members"> + <title>Values</title> + <informaltable role="enum_members_table" pgwide="1" frame="none"> + <tgroup cols="4"> + <colspec colname="enum_members_name" colwidth="300px" /> + <colspec colname="enum_members_value" colwidth="100px"/> + <colspec colname="enum_members_description" /> + <tbody> + <row role="constant"> + <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_NONE</para><para></para></entry> + <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry> + <entry role="enum_member_description"><para>no flags</para><para></para></entry> + </row> + <row role="constant"> + <entry role="enum_member_name"><para>NM_CHECKPOINT_CREATE_FLAG_DESTROY_ALL</para><para></para></entry> + <entry role="enum_member_value"><para>= <literal>0x01</literal></para><para></para></entry> + <entry role="enum_member_description"><para>when creating a new checkpoint, destroy all existing ones.</para><para></para></entry> + </row> + </tbody> + </tgroup> + </informaltable> + </refsect3> + </refsect2> + + <refsect2 id="NMRollbackResult" role="enum"> + <title>enum NMRollbackResult</title> + <indexterm zone="NMRollbackResult"> + <primary>NMRollbackResult</primary> + </indexterm> + <para><para>The result of a checkpoint Rollback() operation for a specific device.</para><para>Since: 1.4</para><para></para></para> + <refsect3 role="enum_members"> + <title>Values</title> + <informaltable role="enum_members_table" pgwide="1" frame="none"> + <tgroup cols="4"> + <colspec colname="enum_members_name" colwidth="300px" /> + <colspec colname="enum_members_value" colwidth="100px"/> + <colspec colname="enum_members_description" /> + <tbody> + <row role="constant"> + <entry role="enum_member_name"><para>NM_ROLLBACK_RESULT_OK</para><para></para></entry> + <entry role="enum_member_value"><para>= <literal>0</literal></para><para></para></entry> + <entry role="enum_member_description"><para>the rollback succeeded.</para><para></para></entry> + </row> + <row role="constant"> + <entry role="enum_member_name"><para>NM_ROLLBACK_RESULT_ERR_NO_DEVICE</para><para></para></entry> + <entry role="enum_member_value"><para>= <literal>1</literal></para><para></para></entry> + <entry role="enum_member_description"><para>the device no longer exists.</para><para></para></entry> + </row> + <row role="constant"> + <entry role="enum_member_name"><para>NM_ROLLBACK_RESULT_ERR_DEVICE_UNMANAGED</para><para></para></entry> + <entry role="enum_member_value"><para>= <literal>2</literal></para><para></para></entry> + <entry role="enum_member_description"><para>the device is now unmanaged.</para><para></para></entry> + </row> + <row role="constant"> + <entry role="enum_member_name"><para>NM_ROLLBACK_RESULT_ERR_FAILED</para><para></para></entry> + <entry role="enum_member_value"><para>= <literal>3</literal></para><para></para></entry> + <entry role="enum_member_description"><para>other errors during rollback.</para><para></para></entry> + </row> + </tbody> + </tgroup> + </informaltable> + </refsect3> + </refsect2> + </refentry> diff --git a/libnm-core/nm-keyfile-internal.h b/libnm-core/nm-keyfile-internal.h index 7873aa7c..41bec0cf 100644 --- a/libnm-core/nm-keyfile-internal.h +++ b/libnm-core/nm-keyfile-internal.h @@ -24,7 +24,6 @@ #include <sys/types.h> -#include "nm-default.h" #include "nm-connection.h" #include "nm-setting-8021x.h" diff --git a/libnm-core/nm-keyfile-reader.c b/libnm-core/nm-keyfile-reader.c index 4a17d2c4..54a694ef 100644 --- a/libnm-core/nm-keyfile-reader.c +++ b/libnm-core/nm-keyfile-reader.c @@ -31,6 +31,7 @@ #include <arpa/inet.h> #include <string.h> +#include "nm-common-macros.h" #include "nm-core-internal.h" #include "nm-keyfile-utils.h" @@ -581,19 +582,28 @@ ip6_addr_gen_mode_parser (KeyfileReaderInfo *info, NMSetting *setting, const cha } static void -mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize enforce_length) +mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key, gsize enforce_length, gboolean cloned_mac_addr) { const char *setting_name = nm_setting_get_name (setting); - char *tmp_string = NULL, *p, *mac_str; - gint *tmp_list; - GByteArray *array = NULL; + gs_free char *tmp_string = NULL; + const char *p, *mac_str; + gs_free guint8 *buf_arr = NULL; + guint buf_len = 0; gsize length; - p = tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); + tmp_string = nm_keyfile_plugin_kf_get_string (info->keyfile, setting_name, key, NULL); + + if ( cloned_mac_addr + && NM_CLONED_MAC_IS_SPECIAL (tmp_string)) { + mac_str = tmp_string; + goto out; + } + if (tmp_string && tmp_string[0]) { /* Look for enough ':' characters to signify a MAC address */ guint i = 0; + p = tmp_string; while (*p) { if (*p == ':') i++; @@ -602,23 +612,24 @@ mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key if (enforce_length == 0 || enforce_length == i+1) { /* If we found enough it's probably a string-format MAC address */ - array = g_byte_array_sized_new (i+1); - g_byte_array_set_size (array, i+1); - if (!nm_utils_hwaddr_aton (tmp_string, array->data, array->len)) { - g_byte_array_unref (array); - array = NULL; - } + buf_len = i + 1; + buf_arr = g_new (guint8, buf_len); + if (!nm_utils_hwaddr_aton (tmp_string, buf_arr, buf_len)) + g_clear_pointer (&buf_arr, g_free); } } - g_free (tmp_string); + g_clear_pointer (&tmp_string, g_free); + + if (!buf_arr) { + gs_free int *tmp_list = NULL; - if (array == NULL) { /* Old format; list of ints */ tmp_list = nm_keyfile_plugin_kf_get_integer_list (info->keyfile, setting_name, key, &length, NULL); if (length > 0 && (enforce_length == 0 || enforce_length == length)) { gsize i; - array = g_byte_array_sized_new (length); + buf_len = length; + buf_arr = g_new (guint8, buf_len); for (i = 0; i < length; i++) { int val = tmp_list[i]; const guint8 v = (guint8) (val & 0xFF); @@ -627,38 +638,42 @@ mac_address_parser (KeyfileReaderInfo *info, NMSetting *setting, const char *key handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid byte element '%d' (not between 0 and 255 inclusive)"), val); - g_byte_array_free (array, TRUE); - g_free (tmp_list); return; } - g_byte_array_append (array, &v, 1); + buf_arr[i] = v; } } - g_free (tmp_list); } - if (!array) { + if (!buf_arr) { handle_warn (info, key, NM_KEYFILE_WARN_SEVERITY_WARN, _("ignoring invalid MAC address")); return; } - mac_str = nm_utils_hwaddr_ntoa (array->data, array->len); + tmp_string = nm_utils_hwaddr_ntoa (buf_arr, buf_len); + mac_str = tmp_string; + +out: g_object_set (setting, key, mac_str, NULL); - g_free (mac_str); - g_byte_array_free (array, TRUE); } static void mac_address_parser_ETHER (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { - mac_address_parser (info, setting, key, ETH_ALEN); + mac_address_parser (info, setting, key, ETH_ALEN, FALSE); +} + +static void +mac_address_parser_ETHER_cloned (KeyfileReaderInfo *info, NMSetting *setting, const char *key) +{ + mac_address_parser (info, setting, key, ETH_ALEN, TRUE); } static void mac_address_parser_INFINIBAND (KeyfileReaderInfo *info, NMSetting *setting, const char *key) { - mac_address_parser (info, setting, key, INFINIBAND_ALEN); + mac_address_parser (info, setting, key, INFINIBAND_ALEN, FALSE); } static void @@ -1209,7 +1224,7 @@ static KeyParser key_parsers[] = { { NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, TRUE, - mac_address_parser_ETHER }, + mac_address_parser_ETHER_cloned }, { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS, TRUE, @@ -1217,7 +1232,7 @@ static KeyParser key_parsers[] = { { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, TRUE, - mac_address_parser_ETHER }, + mac_address_parser_ETHER_cloned }, { NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_BSSID, TRUE, diff --git a/libnm-core/nm-keyfile-writer.c b/libnm-core/nm-keyfile-writer.c index 0ce7641c..e0e6ba4b 100644 --- a/libnm-core/nm-keyfile-writer.c +++ b/libnm-core/nm-keyfile-writer.c @@ -239,10 +239,11 @@ write_hash_of_string (GKeyFile *file, 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 +251,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 *), nm_strcmp_p_with_data, 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 +276,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-property-compare.h b/libnm-core/nm-property-compare.h index 3f105818..b859989f 100644 --- a/libnm-core/nm-property-compare.h +++ b/libnm-core/nm-property-compare.h @@ -23,8 +23,6 @@ #ifndef __NM_PROPERTY_COMPARE_H__ #define __NM_PROPERTY_COMPARE_H__ -#include "nm-default.h" - int nm_property_compare (GVariant *value1, GVariant *value2); #endif /* __NM_PROPERTY_COMPARE_H__ */ diff --git a/libnm-core/nm-setting-8021x.c b/libnm-core/nm-setting-8021x.c index 4db420de..aa9d4619 100644 --- a/libnm-core/nm-setting-8021x.c +++ b/libnm-core/nm-setting-8021x.c @@ -2776,7 +2776,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } - if (priv->phase1_peapver && !_nm_utils_string_in_list (priv->phase1_peapver, valid_phase1_peapver)) { + if (priv->phase1_peapver && !g_strv_contains (valid_phase1_peapver, priv->phase1_peapver)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -2786,7 +2786,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->phase1_peaplabel && !_nm_utils_string_in_list (priv->phase1_peaplabel, valid_phase1_peaplabel)) { + if (priv->phase1_peaplabel && !g_strv_contains (valid_phase1_peaplabel, priv->phase1_peaplabel)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -2796,7 +2796,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->phase1_fast_provisioning && !_nm_utils_string_in_list (priv->phase1_fast_provisioning, valid_phase1_fast_pac)) { + if (priv->phase1_fast_provisioning && !g_strv_contains (valid_phase1_fast_pac, priv->phase1_fast_provisioning)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -2806,7 +2806,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->phase2_auth && !_nm_utils_string_in_list (priv->phase2_auth, valid_phase2_auth)) { + if (priv->phase2_auth && !g_strv_contains (valid_phase2_auth, priv->phase2_auth)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -2816,7 +2816,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->phase2_autheap && !_nm_utils_string_in_list (priv->phase2_autheap, valid_phase2_autheap)) { + if (priv->phase2_autheap && !g_strv_contains (valid_phase2_autheap, priv->phase2_autheap)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, diff --git a/libnm-core/nm-setting-bond.c b/libnm-core/nm-setting-bond.c index e008878d..a82d0d79 100644 --- a/libnm-core/nm-setting-bond.c +++ b/libnm-core/nm-setting-bond.c @@ -92,7 +92,7 @@ static const BondDefault defaults[] = { { "slow", "fast", NULL } }, { NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, "", NM_BOND_OPTION_TYPE_IFNAME }, { NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO,"65535", NM_BOND_OPTION_TYPE_INT, 1, 65535 }, - { NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, "", NM_BOND_OPTION_TYPE_MAC }, + { NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, NULL, NM_BOND_OPTION_TYPE_MAC }, { NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY, "0", NM_BOND_OPTION_TYPE_INT, 0, 1023}, { NM_SETTING_BOND_OPTION_ALL_SLAVES_ACTIVE,"0", NM_BOND_OPTION_TYPE_INT, 0, 1}, { NM_SETTING_BOND_OPTION_ARP_ALL_TARGETS, "any", NM_BOND_OPTION_TYPE_BOTH, 0, 1, {"any", "all"}}, @@ -427,11 +427,22 @@ nm_setting_bond_get_valid_options (NMSettingBond *setting) const char * nm_setting_bond_get_option_default (NMSettingBond *setting, const char *name) { + const char *mode; guint i; g_return_val_if_fail (NM_IS_SETTING_BOND (setting), NULL); g_return_val_if_fail (nm_setting_bond_validate_option (name, NULL), NULL); + if (nm_streq (name, NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM)) { + /* The default value depends on the current mode */ + mode = nm_setting_bond_get_option_by_name (setting, NM_SETTING_BOND_OPTION_MODE); + if ( nm_streq0 (mode, "4") + || nm_streq0 (mode, "802.3ad")) + return "00:00:00:00:00:00"; + else + return ""; + } + for (i = 0; i < G_N_ELEMENTS (defaults); i++) { if (g_strcmp0 (defaults[i].opt, name) == 0) return defaults[i].val; @@ -463,6 +474,60 @@ _nm_setting_bond_get_option_type (NMSettingBond *setting, const char *name) g_assert_not_reached (); } +NMBondMode +_nm_setting_bond_mode_from_string (const char *str) +{ + g_return_val_if_fail (str, NM_BOND_MODE_UNKNOWN); + + if (nm_streq (str, "balance-rr")) + return NM_BOND_MODE_ROUNDROBIN; + if (nm_streq (str, "active-backup")) + return NM_BOND_MODE_ACTIVEBACKUP; + if (nm_streq (str, "balance-xor")) + return NM_BOND_MODE_XOR; + if (nm_streq (str, "broadcast")) + return NM_BOND_MODE_BROADCAST; + if (nm_streq (str, "802.3ad")) + return NM_BOND_MODE_8023AD; + if (nm_streq (str, "balance-tlb")) + return NM_BOND_MODE_TLB; + if (nm_streq (str, "balance-alb")) + return NM_BOND_MODE_ALB; + + return NM_BOND_MODE_UNKNOWN; +} + +#define BIT(x) (1 << (x)) + +const static struct { + const char *option; + NMBondMode unsupp_modes; +} bond_unsupp_modes[] = { + { NM_SETTING_BOND_OPTION_PACKETS_PER_SLAVE, ~(BIT (NM_BOND_MODE_ROUNDROBIN)) }, + { NM_SETTING_BOND_OPTION_ARP_VALIDATE, BIT (NM_BOND_MODE_8023AD) | BIT (NM_BOND_MODE_TLB) | BIT (NM_BOND_MODE_ALB) }, + { NM_SETTING_BOND_OPTION_ARP_INTERVAL, BIT (NM_BOND_MODE_8023AD) | BIT (NM_BOND_MODE_TLB) | BIT (NM_BOND_MODE_ALB) }, + { NM_SETTING_BOND_OPTION_LACP_RATE, ~(BIT (NM_BOND_MODE_8023AD)) }, + { NM_SETTING_BOND_OPTION_PRIMARY, ~(BIT (NM_BOND_MODE_ACTIVEBACKUP) | BIT (NM_BOND_MODE_TLB) | BIT (NM_BOND_MODE_ALB)) }, + { NM_SETTING_BOND_OPTION_ACTIVE_SLAVE, ~(BIT (NM_BOND_MODE_ACTIVEBACKUP) | BIT (NM_BOND_MODE_TLB) | BIT (NM_BOND_MODE_ALB)) }, + { NM_SETTING_BOND_OPTION_TLB_DYNAMIC_LB, ~(BIT (NM_BOND_MODE_TLB)) }, + { NM_SETTING_BOND_OPTION_AD_ACTOR_SYS_PRIO, ~(BIT (NM_BOND_MODE_8023AD)) }, + { NM_SETTING_BOND_OPTION_AD_ACTOR_SYSTEM, ~(BIT (NM_BOND_MODE_8023AD)) }, + { NM_SETTING_BOND_OPTION_AD_USER_PORT_KEY, ~(BIT (NM_BOND_MODE_8023AD)) }, +}; + +gboolean +_nm_setting_bond_option_supported (const char *option, NMBondMode mode) +{ + guint i; + + for (i = 0; i < G_N_ELEMENTS (bond_unsupp_modes); i++) { + if (nm_streq (option, bond_unsupp_modes[i].option)) + return !NM_FLAGS_HAS (bond_unsupp_modes[i].unsupp_modes, BIT (mode)); + } + + return TRUE; +} + static gboolean verify (NMSetting *setting, NMConnection *connection, GError **error) { diff --git a/libnm-core/nm-setting-connection.c b/libnm-core/nm-setting-connection.c index e1cd913a..f6b801c6 100644 --- a/libnm-core/nm-setting-connection.c +++ b/libnm-core/nm-setting-connection.c @@ -63,6 +63,7 @@ typedef struct { typedef struct { char *id; char *uuid; + char *stable_id; char *interface_name; char *type; char *master; @@ -99,6 +100,7 @@ enum { PROP_GATEWAY_PING_TIMEOUT, PROP_METERED, PROP_LLDP, + PROP_STABLE_ID, LAST_PROP }; @@ -231,6 +233,24 @@ nm_setting_connection_get_uuid (NMSettingConnection *setting) } /** + * nm_setting_connection_get_stable_id: + * @setting: the #NMSettingConnection + * + * Returns the #NMSettingConnection:stable_id property of the connection. + * + * Returns: the stable-id for the connection + * + * Since: 1.4 + **/ +const char * +nm_setting_connection_get_stable_id (NMSettingConnection *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_CONNECTION (setting), NULL); + + return NM_SETTING_CONNECTION_GET_PRIVATE (setting)->stable_id; +} + +/** * nm_setting_connection_get_interface_name: * @setting: the #NMSettingConnection * @@ -1128,6 +1148,7 @@ finalize (GObject *object) g_free (priv->id); g_free (priv->uuid); + g_free (priv->stable_id); g_free (priv->interface_name); g_free (priv->type); g_free (priv->zone); @@ -1174,6 +1195,10 @@ set_property (GObject *object, guint prop_id, g_free (priv->uuid); priv->uuid = g_value_dup_string (value); break; + case PROP_STABLE_ID: + g_free (priv->stable_id); + priv->stable_id = g_value_dup_string (value); + break; case PROP_INTERFACE_NAME: g_free (priv->interface_name); priv->interface_name = g_value_dup_string (value); @@ -1260,6 +1285,9 @@ get_property (GObject *object, guint prop_id, case PROP_UUID: g_value_set_string (value, nm_setting_connection_get_uuid (setting)); break; + case PROP_STABLE_ID: + g_value_set_string (value, nm_setting_connection_get_stable_id (setting)); + break; case PROP_INTERFACE_NAME: g_value_set_string (value, nm_setting_connection_get_interface_name (setting)); break; @@ -1368,7 +1396,7 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) * property: uuid * variable: UUID(+) * description: UUID for the connection profile. When missing, NetworkManager - * creates the UUID itself (by hashing the file). + * creates the UUID itself (by hashing the filename). * ---end--- */ g_object_class_install_property @@ -1380,6 +1408,33 @@ nm_setting_connection_class_init (NMSettingConnectionClass *setting_class) G_PARAM_STATIC_STRINGS)); /** + * NMSettingConnection:stable-id: + * + * This token to generate stable IDs for the connection. If unset, + * the UUID will be used instead. + * + * The stable-id is used instead of the connection UUID for generating + * IPv6 stable private addresses with ipv6.addr-gen-mode=stable-privacy. + * It is also used to seed the generated cloned MAC address for + * ethernet.cloned-mac-address=stable and wifi.cloned-mac-address=stable. + * + * Since: 1.4 + **/ + /* ---ifcfg-rh--- + * property: stable-id + * variable: STABLE_ID(+) + * description: Token to generate stable IDs. + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_STABLE_ID, + g_param_spec_string (NM_SETTING_CONNECTION_STABLE_ID, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); + + /** * NMSettingConnection:interface-name: * * The name of the network interface this connection is bound to. If not diff --git a/libnm-core/nm-setting-connection.h b/libnm-core/nm-setting-connection.h index 0d4966f0..67263d1f 100644 --- a/libnm-core/nm-setting-connection.h +++ b/libnm-core/nm-setting-connection.h @@ -46,6 +46,7 @@ G_BEGIN_DECLS #define NM_SETTING_CONNECTION_ID "id" #define NM_SETTING_CONNECTION_UUID "uuid" +#define NM_SETTING_CONNECTION_STABLE_ID "stable-id" #define NM_SETTING_CONNECTION_INTERFACE_NAME "interface-name" #define NM_SETTING_CONNECTION_TYPE "type" #define NM_SETTING_CONNECTION_AUTOCONNECT "autoconnect" @@ -116,6 +117,8 @@ GType nm_setting_connection_get_type (void); NMSetting * nm_setting_connection_new (void); const char *nm_setting_connection_get_id (NMSettingConnection *setting); const char *nm_setting_connection_get_uuid (NMSettingConnection *setting); +NM_AVAILABLE_IN_1_4 +const char *nm_setting_connection_get_stable_id (NMSettingConnection *setting); const char *nm_setting_connection_get_interface_name (NMSettingConnection *setting); const char *nm_setting_connection_get_connection_type (NMSettingConnection *setting); gboolean nm_setting_connection_get_autoconnect (NMSettingConnection *setting); diff --git a/libnm-core/nm-setting-ip-config.c b/libnm-core/nm-setting-ip-config.c index bdcbc23b..55ad7bb0 100644 --- a/libnm-core/nm-setting-ip-config.c +++ b/libnm-core/nm-setting-ip-config.c @@ -300,17 +300,17 @@ nm_ip_address_unref (NMIPAddress *address) } /** - * nm_ip_address_equal: + * _nm_ip_address_equal: * @address: the #NMIPAddress * @other: the #NMIPAddress to compare @address to. + * @consider_attributes: whether to check for equality of attributes too. * - * Determines if two #NMIPAddress objects contain the same address and prefix - * (attributes are not compared). + * Determines if two #NMIPAddress objects are equal. * * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. **/ -gboolean -nm_ip_address_equal (NMIPAddress *address, NMIPAddress *other) +static gboolean +_nm_ip_address_equal (NMIPAddress *address, NMIPAddress *other, gboolean consider_attributes) { g_return_val_if_fail (address != NULL, FALSE); g_return_val_if_fail (address->refcount > 0, FALSE); @@ -322,10 +322,46 @@ nm_ip_address_equal (NMIPAddress *address, NMIPAddress *other) || address->prefix != other->prefix || strcmp (address->address, other->address) != 0) return FALSE; + if (consider_attributes) { + GHashTableIter iter; + const char *key; + GVariant *value, *value2; + guint n; + + n = address->attributes ? g_hash_table_size (address->attributes) : 0; + if (n != (other->attributes ? g_hash_table_size (other->attributes) : 0)) + return FALSE; + if (n) { + g_hash_table_iter_init (&iter, address->attributes); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) { + value2 = g_hash_table_lookup (other->attributes, key); + if (!value2) + return FALSE; + if (!g_variant_equal (value, value2)) + return FALSE; + } + } + } return TRUE; } /** + * nm_ip_address_equal: + * @address: the #NMIPAddress + * @other: the #NMIPAddress to compare @address to. + * + * Determines if two #NMIPAddress objects contain the same address and prefix + * (attributes are not compared). + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + **/ +gboolean +nm_ip_address_equal (NMIPAddress *address, NMIPAddress *other) +{ + return _nm_ip_address_equal (address, other, FALSE); +} + +/** * nm_ip_address_dup: * @address: the #NMIPAddress * @@ -717,17 +753,18 @@ nm_ip_route_unref (NMIPRoute *route) } /** - * nm_ip_route_equal: + * _nm_ip_route_equal: * @route: the #NMIPRoute * @other: the #NMIPRoute to compare @route to. + * @consider_attributes: whether to compare attributes too * * Determines if two #NMIPRoute objects contain the same destination, prefix, - * next hop, and metric. (Attributes are not compared.) + * next hop, and metric. * * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. **/ -gboolean -nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other) +static gboolean +_nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other, gboolean consider_attributes) { g_return_val_if_fail (route != NULL, FALSE); g_return_val_if_fail (route->refcount > 0, FALSE); @@ -740,10 +777,46 @@ nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other) || strcmp (route->dest, other->dest) != 0 || g_strcmp0 (route->next_hop, other->next_hop) != 0) return FALSE; + if (consider_attributes) { + GHashTableIter iter; + const char *key; + GVariant *value, *value2; + guint n; + + n = route->attributes ? g_hash_table_size (route->attributes) : 0; + if (n != (other->attributes ? g_hash_table_size (other->attributes) : 0)) + return FALSE; + if (n) { + g_hash_table_iter_init (&iter, route->attributes); + while (g_hash_table_iter_next (&iter, (gpointer *) &key, (gpointer *) &value)) { + value2 = g_hash_table_lookup (other->attributes, key); + if (!value2) + return FALSE; + if (!g_variant_equal (value, value2)) + return FALSE; + } + } + } return TRUE; } /** + * nm_ip_route_equal: + * @route: the #NMIPRoute + * @other: the #NMIPRoute to compare @route to. + * + * Determines if two #NMIPRoute objects contain the same destination, prefix, + * next hop, and metric. (Attributes are not compared.) + * + * Returns: %TRUE if the objects contain the same values, %FALSE if they do not. + **/ +gboolean +nm_ip_route_equal (NMIPRoute *route, NMIPRoute *other) +{ + return _nm_ip_route_equal (route, other, FALSE); +} + +/** * nm_ip_route_dup: * @route: the #NMIPRoute * @@ -1691,7 +1764,7 @@ nm_setting_ip_config_clear_dns_options (NMSettingIPConfig *setting, gboolean is_ * * Returns: the priority of DNS servers * - * Since: 1.2.4 + * Since: 1.4 **/ gint nm_setting_ip_config_get_dns_priority (NMSettingIPConfig *setting) @@ -1700,6 +1773,7 @@ nm_setting_ip_config_get_dns_priority (NMSettingIPConfig *setting) return NM_SETTING_IP_CONFIG_GET_PRIVATE (setting)->dns_priority; } +NM_BACKPORT_SYMBOL (libnm_1_2_4, gint, nm_setting_ip_config_get_dns_priority, (NMSettingIPConfig *setting), (setting)); /** * nm_setting_ip_config_get_num_addresses: @@ -2305,6 +2379,48 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return TRUE; } +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + NMSettingIPConfigPrivate *a_priv, *b_priv; + NMSettingClass *parent_class; + guint i; + + if (nm_streq (prop_spec->name, NM_SETTING_IP_CONFIG_ADDRESSES)) { + a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting); + b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (other); + + if (a_priv->addresses->len != b_priv->addresses->len) + return FALSE; + for (i = 0; i < a_priv->addresses->len; i++) { + if (!_nm_ip_address_equal (a_priv->addresses->pdata[i], b_priv->addresses->pdata[i], TRUE)) + return FALSE; + } + return TRUE; + } + + if (nm_streq (prop_spec->name, NM_SETTING_IP_CONFIG_ROUTES)) { + a_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (setting); + b_priv = NM_SETTING_IP_CONFIG_GET_PRIVATE (other); + + if (a_priv->routes->len != b_priv->routes->len) + return FALSE; + for (i = 0; i < a_priv->routes->len; i++) { + if (!_nm_ip_route_equal (a_priv->routes->pdata[i], b_priv->routes->pdata[i], TRUE)) + return FALSE; + } + return TRUE; + } + + /* Otherwise chain up to parent to handle generic compare */ + parent_class = NM_SETTING_CLASS (nm_setting_ip_config_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +/*****************************************************************************/ static void nm_setting_ip_config_init (NMSettingIPConfig *setting) @@ -2535,6 +2651,7 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class) object_class->get_property = get_property; object_class->finalize = finalize; parent_class->verify = verify; + parent_class->compare_property = compare_property; /* Properties */ @@ -2627,7 +2744,7 @@ nm_setting_ip_config_class_init (NMSettingIPConfigClass *setting_class) * priority, only DNS servers from configurations with the lowest priority * value will be used. * - * Since: 1.2.4 + * Since: 1.4 **/ g_object_class_install_property (object_class, PROP_DNS_PRIORITY, diff --git a/libnm-core/nm-setting-ip-config.h b/libnm-core/nm-setting-ip-config.h index a41c5466..b18d6a94 100644 --- a/libnm-core/nm-setting-ip-config.h +++ b/libnm-core/nm-setting-ip-config.h @@ -220,7 +220,7 @@ 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 +NM_AVAILABLE_IN_1_4 gint nm_setting_ip_config_get_dns_priority (NMSettingIPConfig *setting); guint nm_setting_ip_config_get_num_addresses (NMSettingIPConfig *setting); diff --git a/libnm-core/nm-setting-ip4-config.c b/libnm-core/nm-setting-ip4-config.c index b3c8462e..70fab0e2 100644 --- a/libnm-core/nm-setting-ip4-config.c +++ b/libnm-core/nm-setting-ip4-config.c @@ -666,6 +666,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 4d320e22..50b36543 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.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; + } + /* Failures from here on are NORMALIZABLE... */ if ( !strcmp (method, NM_SETTING_IP6_CONFIG_METHOD_IGNORE) @@ -400,6 +461,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; @@ -419,6 +484,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; @@ -426,6 +494,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); @@ -436,6 +515,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 */ @@ -568,6 +648,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: * @@ -657,6 +748,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.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 2966e558..3b29aaa2 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_4 +const char *nm_setting_ip6_config_get_token (NMSettingIP6Config *setting); G_END_DECLS diff --git a/libnm-core/nm-setting-private.h b/libnm-core/nm-setting-private.h index 6560c4a2..8d09e3d7 100644 --- a/libnm-core/nm-setting-private.h +++ b/libnm-core/nm-setting-private.h @@ -21,7 +21,6 @@ #ifndef __NM_SETTING_PRIVATE_H__ #define __NM_SETTING_PRIVATE_H__ -#include "nm-default.h" #include "nm-setting.h" #include "nm-connection.h" #include "nm-core-enum-types.h" diff --git a/libnm-core/nm-setting-team-port.c b/libnm-core/nm-setting-team-port.c index 9671f1d9..0d175d5e 100644 --- a/libnm-core/nm-setting-team-port.c +++ b/libnm-core/nm-setting-team-port.c @@ -85,6 +85,8 @@ 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 (connection) { NMSettingConnection *s_con; const char *slave_type; @@ -113,9 +115,54 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } } + + 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); + /* for backward compatibility, we accept invalid json and normalize it */ + if (!priv->config[0]) { + /* be more forgiving to "" and let it verify() as valid because + * at least anaconda used to write such configs */ + return NM_SETTING_VERIFY_NORMALIZABLE; + } + return NM_SETTING_VERIFY_NORMALIZABLE_ERROR; + } + } + + /* NOTE: normalizable/normalizable-errors must appear at the end with decreasing severity. + * Take care to properly order statements with priv->config above. */ + 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 +220,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 d47b1e72..a559e0db 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,56 @@ 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); + /* for backward compatibility, we accept invalid json and normalize it */ + if (!priv->config[0]) { + /* be more forgiving to "" and let it verify() as valid because + * at least anaconda used to write such configs */ + return NM_SETTING_VERIFY_NORMALIZABLE; + } + return NM_SETTING_VERIFY_NORMALIZABLE_ERROR; + } + } + + /* NOTE: normalizable/normalizable-errors must appear at the end with decreasing severity. + * Take care to properly order statements with priv->config above. */ + + 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 +192,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-setting-vpn.c b/libnm-core/nm-setting-vpn.c index c9a1e20b..aa4ddc3a 100644 --- a/libnm-core/nm-setting-vpn.c +++ b/libnm-core/nm-setting-vpn.c @@ -566,8 +566,7 @@ get_secret_flags (NMSetting *setting, GError **error) { NMSettingVpnPrivate *priv = NM_SETTING_VPN_GET_PRIVATE (setting); - gboolean success = FALSE; - char *flags_key; + gs_free char *flags_key = NULL; gpointer val; unsigned long tmp; NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE; @@ -576,28 +575,21 @@ get_secret_flags (NMSetting *setting, if (g_hash_table_lookup_extended (priv->data, flags_key, NULL, &val)) { errno = 0; tmp = strtoul ((const char *) val, NULL, 10); - if ((errno == 0) && (tmp <= NM_SETTING_SECRET_FLAGS_ALL)) { - flags = (NMSettingSecretFlags) tmp; - success = TRUE; - } else { + if ((errno != 0) || (tmp > NM_SETTING_SECRET_FLAGS_ALL)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, _("failed to convert value '%s' to uint"), (const char *) val); g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, flags_key); + return FALSE; } - } else { - g_set_error_literal (error, - NM_CONNECTION_ERROR, - NM_CONNECTION_ERROR_MISSING_PROPERTY, - _("secret flags property not found")); - g_prefix_error (error, "%s.%s: ", NM_SETTING_VPN_SETTING_NAME, flags_key); + flags = (NMSettingSecretFlags) tmp; } - g_free (flags_key); + if (out_flags) *out_flags = flags; - return success; + return TRUE; } static gboolean diff --git a/libnm-core/nm-setting-wired.c b/libnm-core/nm-setting-wired.c index 132f8e05..fc5ab3e5 100644 --- a/libnm-core/nm-setting-wired.c +++ b/libnm-core/nm-setting-wired.c @@ -28,6 +28,7 @@ #include <net/ethernet.h> #include "nm-utils.h" +#include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" @@ -52,6 +53,7 @@ typedef struct { gboolean auto_negotiate; char *device_mac_address; char *cloned_mac_address; + char *generate_mac_address_mask; GArray *mac_address_blacklist; guint32 mtu; char **s390_subchannels; @@ -69,6 +71,7 @@ enum { PROP_AUTO_NEGOTIATE, PROP_MAC_ADDRESS, PROP_CLONED_MAC_ADDRESS, + PROP_GENERATE_MAC_ADDRESS_MASK, PROP_MAC_ADDRESS_BLACKLIST, PROP_MTU, PROP_S390_SUBCHANNELS, @@ -188,6 +191,22 @@ nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting) } /** + * nm_setting_wired_get_generate_mac_address_mask: + * @setting: the #NMSettingWired + * + * Returns: the #NMSettingWired:generate-mac-address-mask property of the setting + * + * Since: 1.4 + **/ +const char * +nm_setting_wired_get_generate_mac_address_mask (NMSettingWired *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), NULL); + + return NM_SETTING_WIRED_GET_PRIVATE (setting)->generate_mac_address_mask; +} + +/** * nm_setting_wired_get_mac_address_blacklist: * @setting: the #NMSettingWired * @@ -504,7 +523,7 @@ nm_setting_wired_add_s390_option (NMSettingWired *setting, g_return_val_if_fail (NM_IS_SETTING_WIRED (setting), FALSE); g_return_val_if_fail (key != NULL, FALSE); g_return_val_if_fail (strlen (key), FALSE); - g_return_val_if_fail (_nm_utils_string_in_list (key, valid_s390_opts), FALSE); + g_return_val_if_fail (g_strv_contains (valid_s390_opts, key), FALSE); g_return_val_if_fail (value != NULL, FALSE); value_len = strlen (value); @@ -611,8 +630,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) GHashTableIter iter; const char *key, *value; int i; + GError *local = NULL; - if (priv->port && !_nm_utils_string_in_list (priv->port, valid_ports)) { + if (priv->port && !g_strv_contains (valid_ports, priv->port)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -622,7 +642,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->duplex && !_nm_utils_string_in_list (priv->duplex, valid_duplex)) { + if (priv->duplex && !g_strv_contains (valid_duplex, priv->duplex)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -668,7 +688,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } - if (priv->s390_nettype && !_nm_utils_string_in_list (priv->s390_nettype, valid_nettype)) { + if (priv->s390_nettype && !g_strv_contains (valid_nettype, priv->s390_nettype)) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -679,7 +699,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) g_hash_table_iter_init (&iter, priv->s390_options); while (g_hash_table_iter_next (&iter, (gpointer) &key, (gpointer) &value)) { - if ( !_nm_utils_string_in_list (key, valid_s390_opts) + if ( !g_strv_contains (valid_s390_opts, key) || !strlen (value) || (strlen (value) > 200)) { g_set_error (error, @@ -692,7 +712,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } - if (priv->cloned_mac_address && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { + if ( priv->cloned_mac_address + && !NM_CLONED_MAC_IS_SPECIAL (priv->cloned_mac_address) + && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -701,6 +723,20 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + /* generate-mac-address-mask only makes sense with cloned-mac-address "random" or + * "stable". Still, let's not be so strict about that and accept the value + * even if it is unused. */ + if (!_nm_utils_generate_mac_address_mask_parse (priv->generate_mac_address_mask, + NULL, NULL, NULL, &local)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + local->message); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRED_SETTING_NAME, NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK); + g_error_free (local); + return FALSE; + } + if ( NM_FLAGS_ANY (priv->wol, NM_SETTING_WIRED_WAKE_ON_LAN_EXCLUSIVE_FLAGS) && !nm_utils_is_power_of_two (priv->wol)) { g_set_error_literal (error, @@ -732,6 +768,25 @@ 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 (nm_streq (prop_spec->name, NM_SETTING_WIRED_CLONED_MAC_ADDRESS)) { + return nm_streq0 (NM_SETTING_WIRED_GET_PRIVATE (setting)->cloned_mac_address, + NM_SETTING_WIRED_GET_PRIVATE (other)->cloned_mac_address); + } + + parent_class = NM_SETTING_CLASS (nm_setting_wired_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +/*****************************************************************************/ + static void clear_blacklist_item (char **item_p) { @@ -763,6 +818,7 @@ finalize (GObject *object) g_free (priv->device_mac_address); g_free (priv->cloned_mac_address); + g_free (priv->generate_mac_address_mask); g_array_unref (priv->mac_address_blacklist); if (priv->s390_subchannels) @@ -807,6 +863,10 @@ set_property (GObject *object, guint prop_id, priv->cloned_mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value), ETH_ALEN); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_free (priv->generate_mac_address_mask); + priv->generate_mac_address_mask = g_value_dup_string (value); + break; case PROP_MAC_ADDRESS_BLACKLIST: blacklist = g_value_get_boxed (value); g_array_set_size (priv->mac_address_blacklist, 0); @@ -872,6 +932,9 @@ get_property (GObject *object, guint prop_id, case PROP_CLONED_MAC_ADDRESS: g_value_set_string (value, nm_setting_wired_get_cloned_mac_address (setting)); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_value_set_string (value, nm_setting_wired_get_generate_mac_address_mask (setting)); + break; case PROP_MAC_ADDRESS_BLACKLIST: g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data); break; @@ -900,18 +963,19 @@ get_property (GObject *object, guint prop_id, } static void -nm_setting_wired_class_init (NMSettingWiredClass *setting_class) +nm_setting_wired_class_init (NMSettingWiredClass *setting_wired_class) { - GObjectClass *object_class = G_OBJECT_CLASS (setting_class); - NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + GObjectClass *object_class = G_OBJECT_CLASS (setting_wired_class); + NMSettingClass *setting_class = NM_SETTING_CLASS (setting_wired_class); - g_type_class_add_private (setting_class, sizeof (NMSettingWiredPrivate)); + g_type_class_add_private (setting_wired_class, sizeof (NMSettingWiredPrivate)); /* virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; - parent_class->verify = verify; + setting_class->verify = verify; + setting_class->compare_property = compare_property; /* Properties */ /** @@ -1023,7 +1087,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRED_MAC_ADDRESS, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRED_MAC_ADDRESS, G_VARIANT_TYPE_BYTESTRING, _nm_utils_hwaddr_to_dbus, _nm_utils_hwaddr_from_dbus); @@ -1033,6 +1097,20 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) * * If specified, request that the device use this MAC address instead of its * permanent MAC address. This is known as MAC cloning or spoofing. + * + * Beside explicitly specifing a MAC address, the special values "preserve", "permanent", + * "random" and "stable" are supported. + * "preserve" means not to touch the MAC address on activation. + * "permanent" means to use the permanent hardware address of the device. + * "random" creates a random MAC address on each connect. + * "stable" creates a hashed MAC address based on connection.stable-id (or + * the connection's UUID) and a machine dependent key. + * + * If unspecified, the value can be overwritten via global defaults, see manual + * of NetworkManager.conf. If still unspecified, it defaults to "permanent". + * + * On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated + * "cloned-mac-address". **/ /* ---keyfile--- * property: cloned-mac-address @@ -1047,6 +1125,12 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons * notation (e.g. 00:22:68:14:5A:99). * ---end--- + * ---dbus--- + * property: cloned-mac-address + * format: byte array + * description: This D-Bus field is deprecated in favor of "assigned-mac-address" + * which is more flexible and allows specifying special variants like "random". + * ---end--- */ g_object_class_install_property (object_class, PROP_CLONED_MAC_ADDRESS, @@ -1055,10 +1139,75 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, - G_VARIANT_TYPE_BYTESTRING, - _nm_utils_hwaddr_to_dbus, - _nm_utils_hwaddr_from_dbus); + _nm_setting_class_override_property (setting_class, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, + G_VARIANT_TYPE_BYTESTRING, + _nm_utils_hwaddr_cloned_get, + _nm_utils_hwaddr_cloned_set, + _nm_utils_hwaddr_cloned_not_set); + + /* ---dbus--- + * property: assigned-mac-address + * format: string + * description: The new field for the cloned MAC address. It can be either + * a hardware address in ASCII representation, or one of the special values + * "preserve", "permanent", "random" or "stable". + * This field replaces the deprecated "cloned-mac-address" on D-Bus, which + * can only contain explict hardware addresses. + * ---end--- + */ + _nm_setting_class_add_dbus_only_property (setting_class, + "assigned-mac-address", + G_VARIANT_TYPE_STRING, + _nm_utils_hwaddr_cloned_data_synth, + _nm_utils_hwaddr_cloned_data_set); + + /** + * NMSettingWired:generate-mac-address-mask: + * + * With #NMSettingWired:cloned-mac-address setting "random" or "stable", + * by default all bits of the MAC address are scrambled and a locally-administered, + * unicast MAC address is created. This property allows to specify that certain bits + * are fixed. Note that the least significant bit of the first MAC address will + * always be unset to create a unicast MAC address. + * + * If the property is %NULL, it is eligible to be overwritten by a default + * connection setting. If the value is still %NULL or an empty string, the + * default is to create a locally-administered, unicast MAC address. + * + * If the value contains one MAC address, this address is used as mask. The set + * bits of the mask are to be filled with the current MAC address of the device, + * while the unset bits are subject to randomization. + * Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address + * and only randomize the lower 3 bytes using the "random" or "stable" algorithm. + * + * If the value contains one additional MAC address after the mask, + * this address is used instead of the current MAC address to fill the bits + * that shall not be randomized. For example, a value of + * "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address + * to 68:F7:28, while the lower bits are randomized. A value of + * "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled + * globally-administered, burned-in MAC address. + * + * If the value contains more then one additional MAC addresses, one of + * them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" + * will create a fully scrambled MAC address, randomly locally or globally + * administered. + **/ + /* ---ifcfg-rh--- + * property: generate-mac-address-mask + * variable: GENERATE_MAC_ADDRESS_MASK + * description: the MAC address mask for generating randomized and stable + * cloned-mac-address. + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_GENERATE_MAC_ADDRESS_MASK, + g_param_spec_string (NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); /** * NMSettingWired:mac-address-blacklist: @@ -1183,7 +1332,7 @@ nm_setting_wired_class_init (NMSettingWiredClass *setting_class) G_PARAM_READWRITE | NM_SETTING_PARAM_INFERRABLE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRED_S390_OPTIONS, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRED_S390_OPTIONS, G_VARIANT_TYPE ("a{ss}"), _nm_utils_strdict_to_dbus, _nm_utils_strdict_from_dbus); diff --git a/libnm-core/nm-setting-wired.h b/libnm-core/nm-setting-wired.h index 6bc66782..30b0b6c2 100644 --- a/libnm-core/nm-setting-wired.h +++ b/libnm-core/nm-setting-wired.h @@ -85,6 +85,7 @@ typedef enum { /*< flags >*/ #define NM_SETTING_WIRED_AUTO_NEGOTIATE "auto-negotiate" #define NM_SETTING_WIRED_MAC_ADDRESS "mac-address" #define NM_SETTING_WIRED_CLONED_MAC_ADDRESS "cloned-mac-address" +#define NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK "generate-mac-address-mask" #define NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST "mac-address-blacklist" #define NM_SETTING_WIRED_MTU "mtu" #define NM_SETTING_WIRED_S390_SUBCHANNELS "s390-subchannels" @@ -117,6 +118,9 @@ gboolean nm_setting_wired_get_auto_negotiate (NMSettingWired *setting const char * nm_setting_wired_get_mac_address (NMSettingWired *setting); const char * nm_setting_wired_get_cloned_mac_address (NMSettingWired *setting); +NM_AVAILABLE_IN_1_4 +const char * nm_setting_wired_get_generate_mac_address_mask (NMSettingWired *setting); + const char * const *nm_setting_wired_get_mac_address_blacklist (NMSettingWired *setting); guint32 nm_setting_wired_get_num_mac_blacklist_items (NMSettingWired *setting); const char * nm_setting_wired_get_mac_blacklist_item (NMSettingWired *setting, diff --git a/libnm-core/nm-setting-wireless-security.c b/libnm-core/nm-setting-wireless-security.c index 64e2aa45..f98e9e57 100644 --- a/libnm-core/nm-setting-wireless-security.c +++ b/libnm-core/nm-setting-wireless-security.c @@ -863,7 +863,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (!_nm_utils_string_in_list (priv->key_mgmt, valid_key_mgmt)) { + if (!g_strv_contains (valid_key_mgmt, priv->key_mgmt)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -936,7 +936,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->auth_alg && !_nm_utils_string_in_list (priv->auth_alg, valid_auth_algs)) { + if (priv->auth_alg && !g_strv_contains (valid_auth_algs, priv->auth_alg)) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -958,7 +958,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) const char *wpa_none[] = { "wpa-none", NULL }; /* For ad-hoc connections, pairwise must be "none" */ - if (_nm_utils_string_in_list (priv->key_mgmt, wpa_none)) { + if (g_strv_contains (wpa_none, priv->key_mgmt)) { GSList *iter; gboolean found = FALSE; diff --git a/libnm-core/nm-setting-wireless.c b/libnm-core/nm-setting-wireless.c index ee4ac6f2..a7f58fe5 100644 --- a/libnm-core/nm-setting-wireless.c +++ b/libnm-core/nm-setting-wireless.c @@ -22,11 +22,13 @@ #include "nm-default.h" +#include "nm-setting-wireless.h" + #include <string.h> #include <net/ethernet.h> -#include "nm-setting-wireless.h" #include "nm-utils.h" +#include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" @@ -54,6 +56,7 @@ typedef struct { guint32 tx_power; char *device_mac_address; char *cloned_mac_address; + char *generate_mac_address_mask; GArray *mac_address_blacklist; guint32 mtu; GSList *seen_bssids; @@ -73,6 +76,7 @@ enum { PROP_TX_POWER, PROP_MAC_ADDRESS, PROP_CLONED_MAC_ADDRESS, + PROP_GENERATE_MAC_ADDRESS_MASK, PROP_MAC_ADDRESS_BLACKLIST, PROP_MTU, PROP_SEEN_BSSIDS, @@ -420,6 +424,22 @@ nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting) } /** + * nm_setting_wireless_get_generate_mac_address_mask: + * @setting: the #NMSettingWireless + * + * Returns: the #NMSettingWireless:generate-mac-address-mask property of the setting + * + * Since: 1.4 + **/ +const char * +nm_setting_wireless_get_generate_mac_address_mask (NMSettingWireless *setting) +{ + g_return_val_if_fail (NM_IS_SETTING_WIRELESS (setting), NULL); + + return NM_SETTING_WIRELESS_GET_PRIVATE (setting)->generate_mac_address_mask; +} + +/** * nm_setting_wireless_get_mac_address_blacklist: * @setting: the #NMSettingWireless * @@ -721,6 +741,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) GSList *iter; int i; gsize length; + GError *local = NULL; if (!priv->ssid) { g_set_error_literal (error, @@ -741,7 +762,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->mode && !_nm_utils_string_in_list (priv->mode, valid_modes)) { + if (priv->mode && !g_strv_contains (valid_modes, priv->mode)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -751,7 +772,7 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->band && !_nm_utils_string_in_list (priv->band, valid_bands)) { + if (priv->band && !g_strv_contains (valid_bands, priv->band)) { g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -801,7 +822,9 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } - if (priv->cloned_mac_address && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { + if ( priv->cloned_mac_address + && !NM_CLONED_MAC_IS_SPECIAL (priv->cloned_mac_address) + && !nm_utils_hwaddr_valid (priv->cloned_mac_address, ETH_ALEN)) { g_set_error_literal (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY, @@ -810,6 +833,20 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) return FALSE; } + /* generate-mac-address-mask only makes sense with cloned-mac-address "random" or + * "stable". Still, let's not be so strict about that and accept the value + * even if it is unused. */ + if (!_nm_utils_generate_mac_address_mask_parse (priv->generate_mac_address_mask, + NULL, NULL, NULL, &local)) { + g_set_error_literal (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + local->message); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK); + g_error_free (local); + return FALSE; + } + for (i = 0; i < priv->mac_address_blacklist->len; i++) { const char *mac = g_array_index (priv->mac_address_blacklist, const char *, i); @@ -836,9 +873,61 @@ verify (NMSetting *setting, NMConnection *connection, GError **error) } } + if (!NM_IN_SET (priv->mac_address_randomization, + NM_SETTING_MAC_RANDOMIZATION_DEFAULT, + NM_SETTING_MAC_RANDOMIZATION_NEVER, + NM_SETTING_MAC_RANDOMIZATION_ALWAYS)) { + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("invalid value")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_MAC_ADDRESS_RANDOMIZATION); + return FALSE; + } + + /* from here on, check for NM_SETTING_VERIFY_NORMALIZABLE conditions. */ + + if (priv->cloned_mac_address) { + if ( priv->mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_ALWAYS + && nm_streq (priv->cloned_mac_address, "random")) + goto mac_addr_rand_ok; + if ( priv->mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_NEVER + && nm_streq (priv->cloned_mac_address, "permanent")) + goto mac_addr_rand_ok; + if (priv->mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) + goto mac_addr_rand_ok; + } else if (priv->mac_address_randomization == NM_SETTING_MAC_RANDOMIZATION_DEFAULT) + goto mac_addr_rand_ok; + g_set_error (error, + NM_CONNECTION_ERROR, + NM_CONNECTION_ERROR_INVALID_PROPERTY, + _("conflicting value of mac-address-randomization and cloned-mac-address")); + g_prefix_error (error, "%s.%s: ", NM_SETTING_WIRELESS_SETTING_NAME, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS); + return NM_SETTING_VERIFY_NORMALIZABLE; +mac_addr_rand_ok: + return TRUE; } +static gboolean +compare_property (NMSetting *setting, + NMSetting *other, + const GParamSpec *prop_spec, + NMSettingCompareFlags flags) +{ + NMSettingClass *parent_class; + + if (nm_streq (prop_spec->name, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS)) { + return nm_streq0 (NM_SETTING_WIRELESS_GET_PRIVATE (setting)->cloned_mac_address, + NM_SETTING_WIRELESS_GET_PRIVATE (other)->cloned_mac_address); + } + + parent_class = NM_SETTING_CLASS (nm_setting_wireless_parent_class); + return parent_class->compare_property (setting, other, prop_spec, flags); +} + +/*****************************************************************************/ + static GVariant * nm_setting_wireless_get_security (NMSetting *setting, NMConnection *connection, @@ -879,6 +968,7 @@ finalize (GObject *object) g_free (priv->bssid); g_free (priv->device_mac_address); g_free (priv->cloned_mac_address); + g_free (priv->generate_mac_address_mask); g_array_unref (priv->mac_address_blacklist); g_slist_free_full (priv->seen_bssids, g_free); @@ -931,6 +1021,10 @@ set_property (GObject *object, guint prop_id, priv->cloned_mac_address = _nm_utils_hwaddr_canonical_or_invalid (g_value_get_string (value), ETH_ALEN); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_free (priv->generate_mac_address_mask); + priv->generate_mac_address_mask = g_value_dup_string (value); + break; case PROP_MAC_ADDRESS_BLACKLIST: blacklist = g_value_get_boxed (value); g_array_set_size (priv->mac_address_blacklist, 0); @@ -998,6 +1092,9 @@ get_property (GObject *object, guint prop_id, case PROP_CLONED_MAC_ADDRESS: g_value_set_string (value, nm_setting_wireless_get_cloned_mac_address (setting)); break; + case PROP_GENERATE_MAC_ADDRESS_MASK: + g_value_set_string (value, nm_setting_wireless_get_generate_mac_address_mask (setting)); + break; case PROP_MAC_ADDRESS_BLACKLIST: g_value_set_boxed (value, (char **) priv->mac_address_blacklist->data); break; @@ -1023,18 +1120,19 @@ get_property (GObject *object, guint prop_id, } static void -nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) +nm_setting_wireless_class_init (NMSettingWirelessClass *setting_wireless_class) { - GObjectClass *object_class = G_OBJECT_CLASS (setting_class); - NMSettingClass *parent_class = NM_SETTING_CLASS (setting_class); + GObjectClass *object_class = G_OBJECT_CLASS (setting_wireless_class); + NMSettingClass *setting_class = NM_SETTING_CLASS (setting_wireless_class); - g_type_class_add_private (setting_class, sizeof (NMSettingWirelessPrivate)); + g_type_class_add_private (setting_wireless_class, sizeof (NMSettingWirelessPrivate)); /* virtual methods */ object_class->set_property = set_property; object_class->get_property = get_property; object_class->finalize = finalize; - parent_class->verify = verify; + setting_class->verify = verify; + setting_class->compare_property = compare_property; /* Properties */ /** @@ -1154,7 +1252,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRELESS_BSSID, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRELESS_BSSID, G_VARIANT_TYPE_BYTESTRING, _nm_utils_hwaddr_to_dbus, _nm_utils_hwaddr_from_dbus); @@ -1231,7 +1329,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRELESS_MAC_ADDRESS, + _nm_setting_class_transform_property (setting_class, NM_SETTING_WIRELESS_MAC_ADDRESS, G_VARIANT_TYPE_BYTESTRING, _nm_utils_hwaddr_to_dbus, _nm_utils_hwaddr_from_dbus); @@ -1239,8 +1337,22 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) /** * NMSettingWireless:cloned-mac-address: * - * If specified, request that the Wi-Fi device use this MAC address instead - * of its permanent MAC address. This is known as MAC cloning or spoofing. + * If specified, request that the device use this MAC address instead of its + * permanent MAC address. This is known as MAC cloning or spoofing. + * + * Beside explicitly specifing a MAC address, the special values "preserve", "permanent", + * "random" and "stable" are supported. + * "preserve" means not to touch the MAC address on activation. + * "permanent" means to use the permanent hardware address of the device. + * "random" creates a random MAC address on each connect. + * "stable" creates a hashed MAC address based on connection.stable-id (or + * the connection's UUID) and a machine dependent key. + * + * If unspecified, the value can be overwritten via global defaults, see manual + * of NetworkManager.conf. If still unspecified, it defaults to "permanent". + * + * On D-Bus, this field is expressed as "assigned-mac-address" or the deprecated + * "cloned-mac-address". **/ /* ---keyfile--- * property: cloned-mac-address @@ -1255,6 +1367,12 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) * description: Cloned (spoofed) MAC address in traditional hex-digits-and-colons * notation (e.g. 00:22:68:14:5A:99). * ---end--- + * ---dbus--- + * property: cloned-mac-address + * format: byte array + * description: This D-Bus field is deprecated in favor of "assigned-mac-address" + * which is more flexible and allows specifying special variants like "random". + * ---end--- */ g_object_class_install_property (object_class, PROP_CLONED_MAC_ADDRESS, @@ -1262,10 +1380,75 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - _nm_setting_class_transform_property (parent_class, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, - G_VARIANT_TYPE_BYTESTRING, - _nm_utils_hwaddr_to_dbus, - _nm_utils_hwaddr_from_dbus); + _nm_setting_class_override_property (setting_class, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, + G_VARIANT_TYPE_BYTESTRING, + _nm_utils_hwaddr_cloned_get, + _nm_utils_hwaddr_cloned_set, + _nm_utils_hwaddr_cloned_not_set); + + /* ---dbus--- + * property: assigned-mac-address + * format: string + * description: The new field for the cloned MAC address. It can be either + * a hardware address in ASCII representation, or one of the special values + * "preserve", "permanent", "random" or "stable". + * This field replaces the deprecated "cloned-mac-address" on D-Bus, which + * can only contain explict hardware addresses. + * ---end--- + */ + _nm_setting_class_add_dbus_only_property (setting_class, + "assigned-mac-address", + G_VARIANT_TYPE_STRING, + _nm_utils_hwaddr_cloned_data_synth, + _nm_utils_hwaddr_cloned_data_set); + + /** + * NMSettingWireless:generate-mac-address-mask: + * + * With #NMSettingWireless:cloned-mac-address setting "random" or "stable", + * by default all bits of the MAC address are scrambled and a locally-administered, + * unicast MAC address is created. This property allows to specify that certain bits + * are fixed. Note that the least significant bit of the first MAC address will + * always be unset to create a unicast MAC address. + * + * If the property is %NULL, it is eligible to be overwritten by a default + * connection setting. If the value is still %NULL or an empty string, the + * default is to create a locally-administered, unicast MAC address. + * + * If the value contains one MAC address, this address is used as mask. The set + * bits of the mask are to be filled with the current MAC address of the device, + * while the unset bits are subject to randomization. + * Setting "FE:FF:FF:00:00:00" means to preserve the OUI of the current MAC address + * and only randomize the lower 3 bytes using the "random" or "stable" algorithm. + * + * If the value contains one additional MAC address after the mask, + * this address is used instead of the current MAC address to fill the bits + * that shall not be randomized. For example, a value of + * "FE:FF:FF:00:00:00 68:F7:28:00:00:00" will set the OUI of the MAC address + * to 68:F7:28, while the lower bits are randomized. A value of + * "02:00:00:00:00:00 00:00:00:00:00:00" will create a fully scrambled + * globally-administered, burned-in MAC address. + * + * If the value contains more then one additional MAC addresses, one of + * them is chosen randomly. For example, "02:00:00:00:00:00 00:00:00:00:00:00 02:00:00:00:00:00" + * will create a fully scrambled MAC address, randomly locally or globally + * administered. + **/ + /* ---ifcfg-rh--- + * property: generate-mac-address-mask + * variable: GENERATE_MAC_ADDRESS_MASK + * description: the MAC address mask for generating randomized and stable + * cloned-mac-address. + * ---end--- + */ + g_object_class_install_property + (object_class, PROP_GENERATE_MAC_ADDRESS_MASK, + g_param_spec_string (NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK, "", "", + NULL, + G_PARAM_READWRITE | + NM_SETTING_PARAM_FUZZY_IGNORE | + G_PARAM_STATIC_STRINGS)); /** * NMSettingWireless:mac-address-blacklist: @@ -1398,6 +1581,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) * (always randomize the MAC address). * * Since: 1.2 + * Deprecated: 1.4: Deprecated by NMSettingWireless:cloned-mac-address property **/ /* ---ifcfg-rh--- * property: mac-address-randomization @@ -1428,7 +1612,7 @@ nm_setting_wireless_class_init (NMSettingWirelessClass *setting_class) * NetworkManager daemons. * ---end--- */ - _nm_setting_class_add_dbus_only_property (parent_class, "security", + _nm_setting_class_add_dbus_only_property (setting_class, "security", G_VARIANT_TYPE_STRING, nm_setting_wireless_get_security, NULL); } diff --git a/libnm-core/nm-setting-wireless.h b/libnm-core/nm-setting-wireless.h index 574cee54..35fa79c1 100644 --- a/libnm-core/nm-setting-wireless.h +++ b/libnm-core/nm-setting-wireless.h @@ -50,6 +50,7 @@ G_BEGIN_DECLS #define NM_SETTING_WIRELESS_TX_POWER "tx-power" #define NM_SETTING_WIRELESS_MAC_ADDRESS "mac-address" #define NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS "cloned-mac-address" +#define NM_SETTING_WIRELESS_GENERATE_MAC_ADDRESS_MASK "generate-mac-address-mask" #define NM_SETTING_WIRELESS_MAC_ADDRESS_BLACKLIST "mac-address-blacklist" #define NM_SETTING_WIRELESS_MTU "mtu" #define NM_SETTING_WIRELESS_SEEN_BSSIDS "seen-bssids" @@ -126,6 +127,9 @@ guint32 nm_setting_wireless_get_tx_power (NMSettingWireless const char *nm_setting_wireless_get_mac_address (NMSettingWireless *setting); const char *nm_setting_wireless_get_cloned_mac_address (NMSettingWireless *setting); +NM_AVAILABLE_IN_1_4 +const char *nm_setting_wireless_get_generate_mac_address_mask (NMSettingWireless *setting); + const char * const *nm_setting_wireless_get_mac_address_blacklist (NMSettingWireless *setting); guint32 nm_setting_wireless_get_num_mac_blacklist_items (NMSettingWireless *setting); const char * nm_setting_wireless_get_mac_blacklist_item (NMSettingWireless *setting, diff --git a/libnm-core/nm-utils-private.h b/libnm-core/nm-utils-private.h index 68aaaa1c..bd54756a 100644 --- a/libnm-core/nm-utils-private.h +++ b/libnm-core/nm-utils-private.h @@ -31,8 +31,34 @@ 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_cloned_get (NMSetting *setting, + const char *property); +gboolean _nm_utils_hwaddr_cloned_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error); +gboolean _nm_utils_hwaddr_cloned_not_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + NMSettingParseFlags parse_flags, + GError **error); +GVariant * _nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, + NMConnection *connection, + const char *property); +gboolean _nm_utils_hwaddr_cloned_data_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error); + GVariant * _nm_utils_hwaddr_to_dbus (const GValue *prop_value); void _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, GValue *prop_value); diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index 1080d9ea..a31a9190 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -33,6 +33,11 @@ #include <gmodule.h> #include <sys/stat.h> +#if WITH_JANSSON +#include <jansson.h> +#endif + +#include "nm-common-macros.h" #include "nm-utils-private.h" #include "nm-setting-private.h" #include "crypto.h" @@ -438,12 +443,6 @@ nm_utils_same_ssid (const guint8 *ssid1, gsize len1, return memcmp (ssid1, ssid2, len1) == 0 ? TRUE : FALSE; } -gboolean -_nm_utils_string_in_list (const char *str, const char **valid_strings) -{ - return _nm_utils_strv_find_first ((char **) valid_strings, -1, str) >= 0; -} - /** * _nm_utils_strv_find_first: * @list: the strv list to search @@ -526,7 +525,7 @@ _nm_utils_string_slist_validate (GSList *list, const char **valid_values) GSList *iter; for (iter = list; iter; iter = iter->next) { - if (!_nm_utils_string_in_list ((char *) iter->data, valid_values)) + if (!g_strv_contains (valid_values, (char *) iter->data)) return FALSE; } @@ -3068,24 +3067,15 @@ nm_utils_hwaddr_aton (const char *asc, gpointer buffer, gsize length) return NULL; } -/** - * nm_utils_hwaddr_ntoa: - * @addr: (type guint8) (array length=length): a binary hardware address - * @length: the length of @addr - * - * Converts @addr to textual form. - * - * Return value: (transfer full): the textual form of @addr - */ -char * -nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length) +static char * +_bin2str (gconstpointer addr, gsize length, gboolean upper_case) { const guint8 *in = addr; char *out, *result; - const char *LOOKUP = "0123456789ABCDEF"; + const char *LOOKUP = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; g_return_val_if_fail (addr != NULL, g_strdup ("")); - g_return_val_if_fail (length > 0 && length <= NM_UTILS_HWADDR_LEN_MAX, g_strdup ("")); + g_return_val_if_fail (length > 0, g_strdup ("")); result = out = g_malloc (length * 3); while (length--) { @@ -3101,6 +3091,37 @@ nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length) return result; } +/** + * nm_utils_hwaddr_ntoa: + * @addr: (type guint8) (array length=length): a binary hardware address + * @length: the length of @addr + * + * Converts @addr to textual form. + * + * Return value: (transfer full): the textual form of @addr + */ +char * +nm_utils_hwaddr_ntoa (gconstpointer addr, gsize length) +{ + return _bin2str (addr, length, TRUE); +} + +/** + * _nm_utils_bin2str: + * @addr: (type guint8) (array length=length): a binary hardware address + * @length: the length of @addr + * @upper_case: the case for the hexadecimal digits. + * + * Converts @addr to textual form. + * + * Return value: (transfer full): the textual form of @addr + */ +char * +_nm_utils_bin2str (gconstpointer addr, gsize length, gboolean upper_case) +{ + return _bin2str (addr, length, upper_case); +} + static int hwaddr_binary_len (const char *asc) { @@ -3117,6 +3138,33 @@ hwaddr_binary_len (const char *asc) } /** + * _nm_utils_hwaddr_length: + * @asc: the ASCII representation of the hardware address + * + * Validates that @asc is a valid representation of a hardware + * address up to (including) %NM_UTILS_HWADDR_LEN_MAX bytes. + * + * Returns: binary length of the hardware address @asc or + * 0 on error. + */ +guint +_nm_utils_hwaddr_length (const char *asc) +{ + int l; + + if (!asc) + return 0; + + l = hwaddr_binary_len (asc); + if (l <= 0 || l > NM_UTILS_HWADDR_LEN_MAX) + return 0; + + if (!nm_utils_hwaddr_valid (asc, l)) + return 0; + return l; +} + +/** * nm_utils_hwaddr_valid: * @asc: the ASCII representation of a hardware address * @length: the length of address that @asc is expected to convert to @@ -3288,24 +3336,124 @@ nm_utils_hwaddr_matches (gconstpointer hwaddr1, return !memcmp (hwaddr1, hwaddr2, hwaddr1_len); } -GVariant * -_nm_utils_hwaddr_to_dbus (const GValue *prop_value) +/*****************************************************************************/ + +static GVariant * +_nm_utils_hwaddr_to_dbus_impl (const char *str) { - const char *str = g_value_get_string (prop_value); guint8 buf[NM_UTILS_HWADDR_LEN_MAX]; int len; - if (str) { - len = hwaddr_binary_len (str); - g_return_val_if_fail (len > 0 && len <= NM_UTILS_HWADDR_LEN_MAX, NULL); - if (!nm_utils_hwaddr_aton (str, buf, len)) - len = 0; - } else - len = 0; + if (!str) + return NULL; + + len = _nm_utils_hwaddr_length (str); + if (len == 0) + return NULL; + + if (!nm_utils_hwaddr_aton (str, buf, len)) + return NULL; return g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE, buf, len, 1); } +GVariant * +_nm_utils_hwaddr_cloned_get (NMSetting *setting, + const char *property) +{ + gs_free char *addr = NULL; + + nm_assert (nm_streq0 (property, "cloned-mac-address")); + + g_object_get (setting, "cloned-mac-address", &addr, NULL); + return _nm_utils_hwaddr_to_dbus_impl (addr); +} + +gboolean +_nm_utils_hwaddr_cloned_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + gsize length; + const guint8 *array; + char *str; + + nm_assert (nm_streq0 (property, "cloned-mac-address")); + + if (!_nm_setting_use_legacy_property (setting, connection_dict, "cloned-mac-address", "assigned-mac-address")) + return TRUE; + + length = 0; + array = g_variant_get_fixed_array (value, &length, 1); + + if (!length) + return TRUE; + + str = nm_utils_hwaddr_ntoa (array, length); + g_object_set (setting, + "cloned-mac-address", + str, + NULL); + g_free (str); + return TRUE; +} + +gboolean +_nm_utils_hwaddr_cloned_not_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + NMSettingParseFlags parse_flags, + GError **error) +{ + nm_assert (nm_streq0 (property, "cloned-mac-address")); + return TRUE; +} + +GVariant * +_nm_utils_hwaddr_cloned_data_synth (NMSetting *setting, + NMConnection *connection, + const char *property) +{ + gs_free char *addr = NULL; + + nm_assert (nm_streq0 (property, "assigned-mac-address")); + + g_object_get (setting, + "cloned-mac-address", + &addr, + NULL); + return addr ? g_variant_new_string (addr) : NULL; +} + +gboolean +_nm_utils_hwaddr_cloned_data_set (NMSetting *setting, + GVariant *connection_dict, + const char *property, + GVariant *value, + NMSettingParseFlags parse_flags, + GError **error) +{ + nm_assert (nm_streq0 (property, "assigned-mac-address")); + + if (_nm_setting_use_legacy_property (setting, connection_dict, "cloned-mac-address", "assigned-mac-address")) + return TRUE; + + g_object_set (setting, + "cloned-mac-address", + g_variant_get_string (value, NULL), + NULL); + return TRUE; +} + +GVariant * +_nm_utils_hwaddr_to_dbus (const GValue *prop_value) +{ + return _nm_utils_hwaddr_to_dbus_impl (g_value_get_string (prop_value)); +} + void _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, GValue *prop_value) @@ -3318,6 +3466,93 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value, g_value_take_string (prop_value, str); } +/*****************************************************************************/ + +static char * +_split_word (char *s) +{ + /* takes @s and truncates the string on the first white-space. + * then it returns the first word afterwards (again seeking + * over leading white-space). */ + for (; s[0]; s++) { + if (g_ascii_isspace (s[0])) { + s[0] = '\0'; + s++; + while (g_ascii_isspace (s[0])) + s++; + return s; + } + } + return s; +} + +gboolean +_nm_utils_generate_mac_address_mask_parse (const char *value, + struct ether_addr *out_mask, + struct ether_addr **out_ouis, + gsize *out_ouis_len, + GError **error) +{ + gs_free char *s_free = NULL; + char *s, *s_next; + struct ether_addr mask; + gs_unref_array GArray *ouis = NULL; + + g_return_val_if_fail (!error || !*error, FALSE); + + if (!value || !*value) { + /* NULL and "" are valid values and both mean the default + * "q */ + if (out_mask) { + memset (out_mask, 0, sizeof (*out_mask)); + out_mask->ether_addr_octet[0] |= 0x02; + } + NM_SET_OUT (out_ouis, NULL); + NM_SET_OUT (out_ouis_len, 0); + return TRUE; + } + + s_free = g_strdup (value); + s = s_free; + + /* skip over leading whitespace */ + while (g_ascii_isspace (s[0])) + s++; + + /* parse the first mask */ + s_next = _split_word (s); + if (!nm_utils_hwaddr_aton (s, &mask, ETH_ALEN)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("not a valid ethernet MAC address for mask at position %lld"), + (long long) (s - s_free)); + return FALSE; + } + + if (s_next[0]) { + ouis = g_array_sized_new (FALSE, FALSE, sizeof (struct ether_addr), 4); + + do { + s = s_next; + s_next = _split_word (s); + + g_array_set_size (ouis, ouis->len + 1); + if (!nm_utils_hwaddr_aton (s, &g_array_index (ouis, struct ether_addr, ouis->len - 1), ETH_ALEN)) { + g_set_error (error, NM_UTILS_ERROR, NM_UTILS_ERROR_UNKNOWN, + _("not a valid ethernet MAC address #%u at position %lld"), + ouis->len, (long long) (s - s_free)); + return FALSE; + } + } while (s_next[0]); + } + + NM_SET_OUT (out_mask, mask); + NM_SET_OUT (out_ouis_len, ouis ? ouis->len : 0); + NM_SET_OUT (out_ouis, ouis ? ((struct ether_addr *) g_array_free (g_steal_pointer (&ouis), FALSE)) : NULL); + return TRUE; +} + +/*****************************************************************************/ + /** * nm_utils_bin2hexstr: * @src: (type guint8) (array length=len): an array of bytes @@ -3562,6 +3797,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 +4359,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 3b448819..d7f112bf 100644 --- a/libnm-core/nm-version.h +++ b/libnm-core/nm-version.h @@ -90,10 +90,18 @@ # define NM_AVAILABLE_IN_1_2 #endif -#if NM_VERSION_MAX_ALLOWED < NM_VERSION_1_2_4 -# define NM_AVAILABLE_IN_1_2_4 G_UNAVAILABLE(1.2,4) +#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) #else -# define NM_AVAILABLE_IN_1_2_4 +# 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 #endif #endif /* NM_VERSION_H */ diff --git a/libnm-core/nm-vpn-dbus-interface.h b/libnm-core/nm-vpn-dbus-interface.h index 9226458d..3295a1ae 100644 --- a/libnm-core/nm-vpn-dbus-interface.h +++ b/libnm-core/nm-vpn-dbus-interface.h @@ -242,6 +242,9 @@ typedef enum { */ #define NM_VPN_PLUGIN_IP4_CONFIG_ROUTES "routes" +/* whether the previous IP4 routing configuration should be preserved. */ +#define NM_VPN_PLUGIN_IP4_CONFIG_PRESERVE_ROUTES "preserve-routes" + /* boolean: prevent this VPN connection from ever getting the default route */ #define NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT "never-default" @@ -293,6 +296,9 @@ typedef enum { */ #define NM_VPN_PLUGIN_IP6_CONFIG_ROUTES "routes" +/* whether the previous IP6 routing configuration should be preserved. */ +#define NM_VPN_PLUGIN_IP6_CONFIG_PRESERVE_ROUTES "preserve-routes" + /* boolean: prevent this VPN connection from ever getting the default route */ #define NM_VPN_PLUGIN_IP6_CONFIG_NEVER_DEFAULT "never-default" diff --git a/libnm-core/nm-vpn-editor-plugin.c b/libnm-core/nm-vpn-editor-plugin.c index 6c78c209..7cb02abd 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" @@ -73,6 +74,159 @@ nm_vpn_editor_plugin_default_init (NMVpnEditorPluginInterface *iface) /*********************************************************************/ +typedef struct { + NMVpnPluginInfo *plugin_info; +} NMVpnEditorPluginPrivate; + +static void +_private_destroy (gpointer data) +{ + NMVpnEditorPluginPrivate *priv = data; + + if (priv->plugin_info) + g_object_remove_weak_pointer ((GObject *) priv->plugin_info, (gpointer *) &priv->plugin_info); + + g_slice_free (NMVpnEditorPluginPrivate, priv); +} + +static NMVpnEditorPluginPrivate * +_private_get (NMVpnEditorPlugin *plugin, gboolean create) +{ + static GQuark quark = 0; + NMVpnEditorPluginPrivate *priv; + + nm_assert (NM_IS_VPN_EDITOR_PLUGIN (plugin)); + + if (G_UNLIKELY (quark == 0)) + quark = g_quark_from_string ("nm-vpn-editor-plugin-private"); + + priv = g_object_get_qdata ((GObject *) plugin, quark); + if (G_LIKELY (priv)) + return priv; + if (!create) + return NULL; + priv = g_slice_new0 (NMVpnEditorPluginPrivate); + g_object_set_qdata_full ((GObject *) plugin, quark, priv, _private_destroy); + return priv; +} + +#define NM_VPN_EDITOR_PLUGIN_GET_PRIVATE(plugin) _private_get (plugin, TRUE) +#define NM_VPN_EDITOR_PLUGIN_TRY_GET_PRIVATE(plugin) _private_get (plugin, FALSE) + +/*********************************************************************/ + +/** + * nm_vpn_editor_plugin_get_plugin_info: + * @plugin: the #NMVpnEditorPlugin instance + * + * Returns: (transfer none): if set, return the #NMVpnPluginInfo instance. + * + * Since: 1.4 + */ +NMVpnPluginInfo * +nm_vpn_editor_plugin_get_plugin_info (NMVpnEditorPlugin *plugin) +{ + NMVpnEditorPluginPrivate *priv; + + g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), NULL); + + priv = NM_VPN_EDITOR_PLUGIN_TRY_GET_PRIVATE (plugin); + return priv ? priv->plugin_info : NULL; +} + +/** + * nm_vpn_editor_plugin_set_plugin_info: + * @plugin: the #NMVpnEditorPlugin instance + * @plugin_info: (allow-none): a #NMVpnPluginInfo instance or %NULL + * + * Set or clear the plugin-info instance. + * This takes a weak reference on @plugin_info, to avoid circular + * reference as the plugin-info might also reference the editor-plugin. + * + * Since: 1.4 + */ +void +nm_vpn_editor_plugin_set_plugin_info (NMVpnEditorPlugin *plugin, NMVpnPluginInfo *plugin_info) +{ + NMVpnEditorPluginInterface *interface; + NMVpnEditorPluginPrivate *priv; + + g_return_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin)); + + if (!plugin_info) { + priv = NM_VPN_EDITOR_PLUGIN_TRY_GET_PRIVATE (plugin); + if (!priv) + return; + } else { + g_return_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info)); + priv = NM_VPN_EDITOR_PLUGIN_GET_PRIVATE (plugin); + } + + if (priv->plugin_info == plugin_info) + return; + if (priv->plugin_info) + g_object_remove_weak_pointer ((GObject *) priv->plugin_info, (gpointer *) &priv->plugin_info); + priv->plugin_info = plugin_info; + if (priv->plugin_info) + g_object_add_weak_pointer ((GObject *) priv->plugin_info, (gpointer *) &priv->plugin_info); + + if (plugin_info) { + interface = NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin); + if (interface->notify_plugin_info_set) + interface->notify_plugin_info_set (plugin, plugin_info); + } + +} + +/*********************************************************************/ + +/** + * nm_vpn_editor_plugin_get_vt: + * @plugin: the #NMVpnEditorPlugin + * @vt: (out): buffer to be filled with the VT table of the plugin + * @vt_size: the size of the buffer. Can be 0 to only query the + * size of plugin's VT. + * + * Returns an opaque VT function table for the plugin to extend + * functionality. The actual meaning of NMVpnEditorPluginVT is not + * defined in public API of libnm, instead it must be agreed by + * both the plugin and the caller. See the header-only file + * 'nm-vpn-editor-plugin-call.h' which defines the meaning. + * + * Returns: the actual size of the @plugin's virtual function table. + * + * Since: 1.4 + **/ +gsize +nm_vpn_editor_plugin_get_vt (NMVpnEditorPlugin *plugin, + NMVpnEditorPluginVT *vt, + gsize vt_size) +{ + const NMVpnEditorPluginVT *p_vt = NULL; + gsize p_vt_size = 0; + NMVpnEditorPluginInterface *interface; + + g_return_val_if_fail (NM_IS_VPN_EDITOR_PLUGIN (plugin), 0); + + if (vt_size) { + g_return_val_if_fail (vt, 0); + memset (vt, 0, vt_size); + } + + interface = NM_VPN_EDITOR_PLUGIN_GET_INTERFACE (plugin); + if (interface->get_vt) { + p_vt = interface->get_vt (plugin, &p_vt_size); + if (!p_vt) + p_vt_size = 0; + g_return_val_if_fail (p_vt_size, 0); + memcpy (vt, p_vt, MIN (vt_size, p_vt_size)); + } + + return p_vt_size; +} + +/*********************************************************************/ + static NMVpnEditorPlugin * _nm_vpn_editor_plugin_load (const char *plugin_name, gboolean do_file_checks, @@ -232,7 +386,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 +410,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.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 9ff23a81..fb51f7e6 100644 --- a/libnm-core/nm-vpn-editor-plugin.h +++ b/libnm-core/nm-vpn-editor-plugin.h @@ -34,6 +34,8 @@ G_BEGIN_DECLS +struct _NMVpnPluginInfo; + typedef struct _NMVpnEditorPlugin NMVpnEditorPlugin; typedef struct _NMVpnEditor NMVpnEditor; @@ -80,6 +82,8 @@ typedef enum /*< flags >*/ { /* D-Bus service name of the plugin's VPN service */ #define NM_VPN_EDITOR_PLUGIN_SERVICE "service" +typedef struct _NMVpnEditorPluginVT NMVpnEditorPluginVT; + /** * NMVpnEditorPluginInterface: * @g_iface: the parent interface @@ -97,6 +101,8 @@ typedef enum /*< flags >*/ { * @get_suggested_filename: For a given connection, return a suggested file * name. Returned value will be %NULL or a suggested file name to be freed by * the caller. + * @get_vt: return a virtual function table to implement further functions in + * the plugin, without requiring to update libnm. Used by nm_vpn_editor_plugin_get_vt(). * * Interface for VPN editor plugins. */ @@ -119,6 +125,12 @@ typedef struct { GError **error); char * (*get_suggested_filename) (NMVpnEditorPlugin *plugin, NMConnection *connection); + + void (*notify_plugin_info_set) (NMVpnEditorPlugin *plugin, + struct _NMVpnPluginInfo *plugin_info); + + const NMVpnEditorPluginVT *(*get_vt) (NMVpnEditorPlugin *plugin, + gsize *out_vt_size); } NMVpnEditorPluginInterface; GType nm_vpn_editor_plugin_get_type (void); @@ -129,6 +141,11 @@ NMVpnEditor *nm_vpn_editor_plugin_get_editor (NMVpnEditorPlugin *plugin, NMVpnEditorPluginCapability nm_vpn_editor_plugin_get_capabilities (NMVpnEditorPlugin *plugin); +NM_AVAILABLE_IN_1_4 +gsize nm_vpn_editor_plugin_get_vt (NMVpnEditorPlugin *plugin, + NMVpnEditorPluginVT *vt, + gsize vt_size); + NMConnection *nm_vpn_editor_plugin_import (NMVpnEditorPlugin *plugin, const char *path, GError **error); @@ -147,6 +164,18 @@ NMVpnEditorPlugin *nm_vpn_editor_plugin_load_from_file (const char *plugin_name gpointer user_data, GError **error); +NM_AVAILABLE_IN_1_4 +NMVpnEditorPlugin *nm_vpn_editor_plugin_load (const char *plugin_name, + const char *check_service, + GError **error); + +NM_AVAILABLE_IN_1_4 +struct _NMVpnPluginInfo *nm_vpn_editor_plugin_get_plugin_info (NMVpnEditorPlugin *plugin); +NM_AVAILABLE_IN_1_4 +void nm_vpn_editor_plugin_set_plugin_info (NMVpnEditorPlugin *plugin, struct _NMVpnPluginInfo *plugin_info); + +#include "nm-vpn-plugin-info.h" + 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 2e6275e3..a4727619 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.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 @@ -424,7 +491,7 @@ nm_vpn_plugin_info_list_add (GSList **list, NMVpnPluginInfo *plugin_info, GError } /* the plugin must have unique values for certain properties. E.g. two different - * plugins cannot share the same service name. */ + * plugins cannot share the same service type. */ if (!_check_no_conflict (plugin_info, iter->data, error)) return FALSE; } @@ -506,10 +573,24 @@ nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename) return NULL; } +static NMVpnPluginInfo * +_list_find_by_service (GSList *list, const char *service) +{ + for (; list; list = list->next) { + NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (list->data); + + if ( nm_streq (priv->service, service) + || _nm_utils_strv_find_first (priv->aliases, -1, service) >= 0) + return list->data; + } + return NULL; +} + /** * nm_vpn_plugin_info_list_find_by_service: * @list: (element-type NMVpnPluginInfo): list of plugins - * @service: service to search + * @service: service to search. This can be the main service-type + * or one of the provided aliases. * * Returns: (transfer none): the first plugin with a matching @service (or %NULL). * @@ -518,27 +599,180 @@ nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename) NMVpnPluginInfo * nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service) { + if (!service) + g_return_val_if_reached (NULL); + return _list_find_by_service (list, service); +} + +/* known_names are well known short names for the service-type. They all implicitly + * have a prefix "org.freedesktop.NetworkManager." + known_name. */ +static const char *known_names[] = { + "openvpn", + "vpnc", + "pptp", + "openconnect", + "openswan", + "libreswan", + "strongswan", + "ssh", + "l2tp", + "iodine", + "fortisslvpn", +}; + +/** + * nm_vpn_plugin_info_list_find_service_type: + * @list: (element-type NMVpnPluginInfo): a possibly empty #GSList of #NMVpnPluginInfo instances + * @name: a name to lookup the service-type. + * + * A VPN plugin provides one or several service-types, like org.freedesktop.NetworkManager.libreswan + * Certain plugins provide more then one service type, via aliases (org.freedesktop.NetworkManager.openswan). + * This function looks up a service-type (or an alias) based on a name. + * + * Preferably, the name can be a full service-type/alias of an installed + * plugin. Otherwise, it can be the name of a VPN plugin (in which case, the + * primary, non-aliased service-type is returned). Otherwise, it can be + * one of several well known short-names (which is a hard-coded list of + * types in libnm). On success, this returns a full qualified service-type + * (or an alias). It doesn't say, that such an plugin is actually available, + * but it could be retrieved via nm_vpn_plugin_info_list_find_by_service(). + * + * Returns: (transfer full): the resolved service-type or %NULL on failure. + * + * Since: 1.4 + */ +char * +nm_vpn_plugin_info_list_find_service_type (GSList *list, const char *name) +{ GSList *iter; + char *n; - if (!service) + if (!name) g_return_val_if_reached (NULL); + if (!*name) + return NULL; + + /* First, try to interpret @name as a full service-type (or alias). */ + if (_list_find_by_service (list, name)) + return g_strdup (name); - /* First, consider the primary service name. */ + /* try to interpret @name as plugin name, in which case we return + * the main service-type (not an alias). */ for (iter = list; iter; iter = iter->next) { - if (strcmp (NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data)->service, service) == 0) - return iter->data; + NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data); + + if (nm_streq (priv->name, name)) + return g_strdup (priv->service); } - /* Then look into the aliases. */ + /* check the hard-coded list of short-names. They all have have the same + * well-known prefix org.freedesktop.NetworkManager and the name. */ + if (_nm_utils_strv_find_first ((char **) known_names, G_N_ELEMENTS (known_names), name) >= 0) + return g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name); + + /* try, if there exists a plugin with @name under org.freedesktop.NetworkManager. + * Allow this to be a valid abbreviation. */ + n = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name); + if (_list_find_by_service (list, n)) + return n; + g_free (n); + + /* currently, VPN plugins have no way to define a short-name for their + * alias name, unless the alias name is prefixed by org.freedesktop.NetworkManager. */ + + return NULL; +} + +static const char * +_service_type_get_default_abbreviation (const char *service_type) +{ + if (!g_str_has_prefix (service_type, NM_DBUS_INTERFACE)) + return NULL; + service_type += NM_STRLEN (NM_DBUS_INTERFACE); + if (service_type[0] != '.') + return NULL; + service_type++; + if (!service_type[0]) + return NULL; + return service_type; +} + +/** + * nm_vpn_plugin_info_list_get_service_types: + * @list: (element-type NMVpnPluginInfo): a possibly empty #GSList of #NMVpnPluginInfo + * @only_existing: only include results that are actually in @list. + * Otherwise, the result is extended with a hard-code list or + * well-known plugins + * @with_abbreviations: if %FALSE, only full service types are returned. + * Otherwise, this also includes abbreviated names that can be used + * with nm_vpn_plugin_info_list_find_service_type(). + * + * Returns: (transfer full): a %NULL terminated strv list of strings. + * The list itself and the values must be freed with g_strfreev(). + * + * Since: 1.4 + */ +char ** +nm_vpn_plugin_info_list_get_service_types (GSList *list, + gboolean only_existing, + gboolean with_abbreviations) +{ + GSList *iter; + GPtrArray *l; + guint i, j; + const char *n; + + l = g_ptr_array_sized_new (20); + for (iter = list; iter; iter = iter->next) { - char **aliases = (NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data))->aliases; + NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data); - if (!aliases) - continue; - if (_nm_utils_strv_find_first (aliases, -1, service) >= 0) - return iter->data; + g_ptr_array_add (l, g_strdup (priv->service)); + if (priv->aliases) { + for (i = 0; priv->aliases[i]; i++) + g_ptr_array_add (l, g_strdup (priv->aliases[i])); + } + + if (with_abbreviations) { + g_ptr_array_add (l, g_strdup (priv->name)); + n = _service_type_get_default_abbreviation (priv->service); + if (n) + g_ptr_array_add (l, g_strdup (n)); + for (i = 0; priv->aliases[i]; i++) { + n = _service_type_get_default_abbreviation (priv->aliases[i]); + if (n) + g_ptr_array_add (l, g_strdup (n)); + } + } } - return NULL; + + if (!only_existing) { + for (i = 0; i < G_N_ELEMENTS (known_names); i++) { + g_ptr_array_add (l, g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, known_names[i])); + if (with_abbreviations) + g_ptr_array_add (l, g_strdup (known_names[i])); + } + } + + if (l->len <= 0) { + g_ptr_array_free (l, TRUE); + return g_new0 (char *, 1); + } + + /* sort the result and remove duplicates. */ + g_ptr_array_sort (l, nm_strcmp_p); + for (i = 1, j = 1; i < l->len; i++) { + if (nm_streq (l->pdata[j-1], l->pdata[i])) + g_free (l->pdata[i]); + else + l->pdata[j++] = l->pdata[i]; + } + + if (j == l->len) + g_ptr_array_add (l, NULL); + else + l->pdata[j] = NULL; + return (char **) g_ptr_array_free (l, FALSE); } /*********************************************************************/ @@ -576,6 +810,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.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.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.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 * @@ -630,6 +935,32 @@ nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self) /** + * nm_vpn_plugin_info_get_aliases: + * @self: plugin info instance + * + * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): + * the aliases from the name-file. + * + * Since: 1.4 + */ +const char *const* +nm_vpn_plugin_info_get_aliases (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 (priv->aliases) + return (const char *const*) priv->aliases; + + /* For convenience, we always want to return non-NULL, even for empty + * aliases. Hack around that, by making a NULL terminated array using + * the NULL of priv->aliases. */ + return (const char *const*) &priv->aliases; +} + +/** * nm_vpn_plugin_info_lookup_property: * @self: plugin info instance * @group: group name @@ -753,11 +1084,13 @@ 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, error); + if (priv->editor_plugin) + nm_vpn_editor_plugin_set_plugin_info (priv->editor_plugin, self); return priv->editor_plugin; } @@ -865,6 +1198,8 @@ init_sync (GInitable *initable, GCancellable *cancellable, GError **error) } priv->aliases = g_key_file_get_string_list (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "aliases", NULL, NULL); + if (priv->aliases && !priv->aliases[0]) + g_clear_pointer (&priv->aliases, g_free); priv->keys = g_hash_table_new_full (_nm_utils_strstrdictkey_hash, _nm_utils_strstrdictkey_equal, @@ -947,6 +1282,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 c7fef857..908ee482 100644 --- a/libnm-core/nm-vpn-plugin-info.h +++ b/libnm-core/nm-vpn-plugin-info.h @@ -44,10 +44,12 @@ G_BEGIN_DECLS #define NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM "libnm" #define NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME "GNOME" +struct _NMVpnPluginInfo; + /** * NMVpnPluginInfo: */ -typedef struct { +typedef struct _NMVpnPluginInfo { NM_AVAILABLE_IN_1_2 GObject parent; } NMVpnPluginInfo; @@ -73,16 +75,28 @@ NMVpnPluginInfo *nm_vpn_plugin_info_new_with_data (const char *filename, GKeyFile *keyfile, GError **error); +NM_AVAILABLE_IN_1_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_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_4 +const char *nm_vpn_plugin_info_get_auth_dialog (NMVpnPluginInfo *self); +NM_AVAILABLE_IN_1_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_4 +const char *const*nm_vpn_plugin_info_get_aliases (NMVpnPluginInfo *self); NM_AVAILABLE_IN_1_2 const char *nm_vpn_plugin_info_lookup_property (NMVpnPluginInfo *self, const char *group, const char *key); @@ -102,6 +116,12 @@ NMVpnPluginInfo *nm_vpn_plugin_info_list_find_by_filename (GSList *list, const c NM_AVAILABLE_IN_1_2 NMVpnPluginInfo *nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service); +NM_AVAILABLE_IN_1_4 +char *nm_vpn_plugin_info_list_find_service_type (GSList *list, const char *name); +NM_AVAILABLE_IN_1_4 +char **nm_vpn_plugin_info_list_get_service_types (GSList *list, + gboolean only_existing, + gboolean with_abbreviations); NM_AVAILABLE_IN_1_2 NMVpnEditorPlugin *nm_vpn_plugin_info_get_editor_plugin (NMVpnPluginInfo *self); diff --git a/libnm-core/tests/Makefile.am b/libnm-core/tests/Makefile.am index 1926c86f..485fd141 100644 --- a/libnm-core/tests/Makefile.am +++ b/libnm-core/tests/Makefile.am @@ -9,8 +9,6 @@ BUILT_SOURCES = $(GLIB_GENERATED) if ENABLE_TESTS -certsdir = $(srcdir)/certs - AM_CPPFLAGS = \ -I${top_srcdir}/shared \ -I${top_builddir}/shared \ @@ -18,7 +16,7 @@ AM_CPPFLAGS = \ -I$(top_builddir)/libnm-core \ -DNETWORKMANAGER_COMPILATION \ $(GLIB_CFLAGS) \ - -DTEST_CERT_DIR=\"$(certsdir)\" + -DTEST_CERT_DIR=\"$(abs_srcdir)/certs\" noinst_PROGRAMS = \ test-compare \ diff --git a/libnm-core/tests/Makefile.in b/libnm-core/tests/Makefile.in index 1ffcabc7..75ccb08e 100644 --- a/libnm-core/tests/Makefile.in +++ b/libnm-core/tests/Makefile.in @@ -97,8 +97,9 @@ host_triplet = @host@ @ENABLE_TESTS_TRUE@ test-settings-defaults$(EXEEXT) subdir = libnm-core/tests ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_lib_readline.m4 \ - $(top_srcdir)/m4/compiler_warnings.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/attributes.m4 \ + $(top_srcdir)/m4/ax_lib_readline.m4 \ + $(top_srcdir)/m4/compiler_options.m4 \ $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gnome-code-coverage.m4 \ $(top_srcdir)/m4/gtk-doc.m4 $(top_srcdir)/m4/iconv.m4 \ @@ -487,6 +488,7 @@ DHCLIENT_PATH = @DHCLIENT_PATH@ DHCPCD_PATH = @DHCPCD_PATH@ DISTRO_NETWORK_SERVICE = @DISTRO_NETWORK_SERVICE@ DLLTOOL = @DLLTOOL@ +DL_LIBS = @DL_LIBS@ DNSMASQ_PATH = @DNSMASQ_PATH@ DNSSEC_TRIGGER_SCRIPT = @DNSSEC_TRIGGER_SCRIPT@ DSYMUTIL = @DSYMUTIL@ @@ -543,13 +545,14 @@ INTROSPECTION_MAKEFILE = @INTROSPECTION_MAKEFILE@ INTROSPECTION_SCANNER = @INTROSPECTION_SCANNER@ INTROSPECTION_TYPELIBDIR = @INTROSPECTION_TYPELIBDIR@ IPTABLES_PATH = @IPTABLES_PATH@ +JANSSON_CFLAGS = @JANSSON_CFLAGS@ +JANSSON_LIBS = @JANSSON_LIBS@ KERNEL_FIRMWARE_DIR = @KERNEL_FIRMWARE_DIR@ LCOV = @LCOV@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAUDIT_CFLAGS = @LIBAUDIT_CFLAGS@ LIBAUDIT_LIBS = @LIBAUDIT_LIBS@ -LIBDL = @LIBDL@ LIBICONV = @LIBICONV@ LIBINTL = @LIBINTL@ LIBM = @LIBM@ @@ -561,6 +564,8 @@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBSOUP_CFLAGS = @LIBSOUP_CFLAGS@ LIBSOUP_LIBS = @LIBSOUP_LIBS@ +LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@ +LIBSYSTEMD_LIBS = @LIBSYSTEMD_LIBS@ LIBTEAMDCTL_CFLAGS = @LIBTEAMDCTL_CFLAGS@ LIBTEAMDCTL_LIBS = @LIBTEAMDCTL_LIBS@ LIBTOOL = @LIBTOOL@ @@ -621,6 +626,8 @@ QT_CFLAGS = @QT_CFLAGS@ QT_LIBS = @QT_LIBS@ RANLIB = @RANLIB@ READLINE_LIBS = @READLINE_LIBS@ +SANITIZERS = @SANITIZERS@ +SANITIZER_ENV = @SANITIZER_ENV@ SED = @SED@ SELINUX_CFLAGS = @SELINUX_CFLAGS@ SELINUX_LIBS = @SELINUX_LIBS@ @@ -656,6 +663,7 @@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_RANLIB = @ac_ct_RANLIB@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ @@ -722,7 +730,6 @@ nm_core_tests_enum_types_sources = test-general-enums.h GLIB_MKENUMS_H_FLAGS = --identifier-prefix NM GLIB_MKENUMS_C_FLAGS = --identifier-prefix NM BUILT_SOURCES = $(GLIB_GENERATED) -@ENABLE_TESTS_TRUE@certsdir = $(srcdir)/certs @ENABLE_TESTS_TRUE@AM_CPPFLAGS = \ @ENABLE_TESTS_TRUE@ -I${top_srcdir}/shared \ @ENABLE_TESTS_TRUE@ -I${top_builddir}/shared \ @@ -730,7 +737,7 @@ BUILT_SOURCES = $(GLIB_GENERATED) @ENABLE_TESTS_TRUE@ -I$(top_builddir)/libnm-core \ @ENABLE_TESTS_TRUE@ -DNETWORKMANAGER_COMPILATION \ @ENABLE_TESTS_TRUE@ $(GLIB_CFLAGS) \ -@ENABLE_TESTS_TRUE@ -DTEST_CERT_DIR=\"$(certsdir)\" +@ENABLE_TESTS_TRUE@ -DTEST_CERT_DIR=\"$(abs_srcdir)/certs\" @ENABLE_TESTS_TRUE@LDADD = \ @ENABLE_TESTS_TRUE@ $(top_builddir)/libnm-core/libnm-core.la \ diff --git a/libnm-core/tests/test-compare.c b/libnm-core/tests/test-compare.c index dca5c7d3..7f2ba980 100644 --- a/libnm-core/tests/test-compare.c +++ b/libnm-core/tests/test-compare.c @@ -26,7 +26,7 @@ #include "nm-property-compare.h" -#include "nm-test-utils.h" +#include "nm-utils/nm-test-utils.h" static void compare_ints (void) diff --git a/libnm-core/tests/test-crypto.c b/libnm-core/tests/test-crypto.c index bd87c710..9bab985f 100644 --- a/libnm-core/tests/test-crypto.c +++ b/libnm-core/tests/test-crypto.c @@ -33,7 +33,7 @@ #include "nm-errors.h" #include "nm-core-internal.h" -#include "nm-test-utils.h" +#include "nm-utils/nm-test-utils.h" #if 0 static const char *pem_rsa_key_begin = "-----BEGIN RSA PRIVATE KEY-----"; diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index c71e2d26..6aee2527 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -62,7 +62,7 @@ #include "test-general-enums.h" -#include "nm-test-utils.h" +#include "nm-utils/nm-test-utils.h" /* When passing a "bool" typed argument to a variadic function that * expects a gboolean, the compiler will promote the integer type @@ -145,54 +145,54 @@ test_setting_vpn_items (void) nm_setting_vpn_remove_secret (s_vpn, "foobar4"); /* Try to add some blank values and make sure they are rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*key != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (key != NULL)); nm_setting_vpn_add_data_item (s_vpn, NULL, NULL); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (key) > 0)); nm_setting_vpn_add_data_item (s_vpn, "", ""); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*item != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (item != NULL)); nm_setting_vpn_add_data_item (s_vpn, "foobar1", NULL); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strlen (item) > 0*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (item) > 0)); nm_setting_vpn_add_data_item (s_vpn, "foobar1", ""); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*key != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (key != NULL)); nm_setting_vpn_add_data_item (s_vpn, NULL, "blahblah1"); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (key) > 0)); nm_setting_vpn_add_data_item (s_vpn, "", "blahblah1"); g_test_assert_expected_messages (); nm_setting_vpn_foreach_data_item (s_vpn, vpn_check_empty_func, NULL); /* Try to add some blank secrets and make sure they are rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*key != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (key != NULL)); nm_setting_vpn_add_secret (s_vpn, NULL, NULL); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (key) > 0)); nm_setting_vpn_add_secret (s_vpn, "", ""); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*secret != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (secret != NULL)); nm_setting_vpn_add_secret (s_vpn, "foobar1", NULL); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strlen (secret) > 0*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (secret) > 0)); nm_setting_vpn_add_secret (s_vpn, "foobar1", ""); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*key != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (key != NULL)); nm_setting_vpn_add_secret (s_vpn, NULL, "blahblah1"); g_test_assert_expected_messages (); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strlen (key) > 0*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strlen (key) > 0)); nm_setting_vpn_add_secret (s_vpn, "", "blahblah1"); g_test_assert_expected_messages (); @@ -1621,47 +1621,47 @@ test_setting_connection_permissions_helpers (void) s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); /* Ensure a bad [type] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strcmp (ptype, \"user\") == 0*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strcmp (ptype, "user") == 0)); success = nm_setting_connection_add_permission (s_con, "foobar", "blah", NULL); g_test_assert_expected_messages (); g_assert (!success); /* Ensure a bad [type] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*ptype*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (ptype)); success = nm_setting_connection_add_permission (s_con, NULL, "blah", NULL); g_test_assert_expected_messages (); g_assert (!success); /* Ensure a bad [item] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*uname*"); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*p != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (uname)); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (p != NULL)); success = nm_setting_connection_add_permission (s_con, "user", NULL, NULL); g_test_assert_expected_messages (); g_assert (!success); /* Ensure a bad [item] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*uname[0] != '\\0'*"); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*p != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (uname[0] != '\0')); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (p != NULL)); success = nm_setting_connection_add_permission (s_con, "user", "", NULL); g_test_assert_expected_messages (); g_assert (!success); /* Ensure an [item] with ':' is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strchr (uname, ':')*"); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*p != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strchr (uname, ':') == NULL)); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (p != NULL)); success = nm_setting_connection_add_permission (s_con, "user", "ad:asdf", NULL); g_test_assert_expected_messages (); g_assert (!success); /* Ensure a non-UTF-8 [item] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*g_utf8_validate (uname, -1, NULL)*"); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*p != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (g_utf8_validate (uname, -1, NULL) == TRUE)); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (p != NULL)); success = nm_setting_connection_add_permission (s_con, "user", buf, NULL); g_test_assert_expected_messages (); g_assert (!success); /* Ensure a non-NULL [detail] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*detail == NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (detail == NULL)); success = nm_setting_connection_add_permission (s_con, "user", "dafasdf", "asdf"); g_test_assert_expected_messages (); g_assert (!success); @@ -1732,49 +1732,49 @@ test_setting_connection_permissions_property (void) s_con = NM_SETTING_CONNECTION (nm_setting_connection_new ()); /* Ensure a bad [type] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0)); add_permission_property (s_con, "foobar", "blah", -1, NULL); g_test_assert_expected_messages (); g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0); /* Ensure a bad [type] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (strncmp (str, PERM_USER_PREFIX, strlen (PERM_USER_PREFIX)) == 0)); add_permission_property (s_con, NULL, "blah", -1, NULL); g_test_assert_expected_messages (); g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0); /* Ensure a bad [item] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*last_colon > str*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (last_colon > str)); add_permission_property (s_con, "user", NULL, -1, NULL); g_test_assert_expected_messages (); g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0); /* Ensure a bad [item] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*last_colon > str*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (last_colon > str)); add_permission_property (s_con, "user", "", -1, NULL); g_test_assert_expected_messages (); g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0); /* Ensure an [item] with ':' in the middle is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*str[i] != ':'*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (str[i] != ':')); add_permission_property (s_con, "user", "ad:asdf", -1, NULL); g_test_assert_expected_messages (); g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0); /* Ensure an [item] with ':' at the end is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*str[i] != ':'*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (str[i] != ':')); add_permission_property (s_con, "user", "adasdfaf:", -1, NULL); g_test_assert_expected_messages (); g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0); /* Ensure a non-UTF-8 [item] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*g_utf8_validate (str, -1, NULL)*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (g_utf8_validate (str, -1, NULL) == TRUE)); add_permission_property (s_con, "user", buf, (int) sizeof (buf), NULL); g_test_assert_expected_messages (); g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0); /* Ensure a non-NULL [detail] is rejected */ - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*(last_colon + 1) == '\\0'*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (*(last_colon + 1) == '\0')); add_permission_property (s_con, "user", "dafasdf", -1, "asdf"); g_test_assert_expected_messages (); g_assert_cmpint (nm_setting_connection_get_num_permissions (s_con), ==, 0); @@ -1916,6 +1916,7 @@ test_connection_diff_a_only (void) { NM_SETTING_CONNECTION_SETTING_NAME, { { NM_SETTING_CONNECTION_ID, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_UUID, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_CONNECTION_STABLE_ID, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_INTERFACE_NAME, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_TYPE, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_CONNECTION_TIMESTAMP, NM_SETTING_DIFF_RESULT_IN_A }, @@ -1940,6 +1941,7 @@ test_connection_diff_a_only (void) { NM_SETTING_WIRED_AUTO_NEGOTIATE, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_CLONED_MAC_ADDRESS, NM_SETTING_DIFF_RESULT_IN_A }, + { NM_SETTING_WIRED_GENERATE_MAC_ADDRESS_MASK, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_MAC_ADDRESS_BLACKLIST, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_MTU, NM_SETTING_DIFF_RESULT_IN_A }, { NM_SETTING_WIRED_S390_SUBCHANNELS, NM_SETTING_DIFF_RESULT_IN_A }, @@ -2385,6 +2387,154 @@ test_setting_compare_id (void) } static void +test_setting_compare_addresses (void) +{ + gs_unref_object NMSetting *s1 = NULL, *s2 = NULL; + gboolean success; + NMIPAddress *a; + GHashTable *result = NULL; + + s1 = nm_setting_ip4_config_new (); + s2 = nm_setting_ip4_config_new (); + + a = nm_ip_address_new (AF_INET, "192.168.7.5", 24, NULL); + + nm_ip_address_set_attribute (a, "label", g_variant_new_string ("xoxoxo")); + nm_setting_ip_config_add_address ((NMSettingIPConfig *) s1, a); + + nm_ip_address_set_attribute (a, "label", g_variant_new_string ("hello")); + nm_setting_ip_config_add_address ((NMSettingIPConfig *) s2, a); + + nm_ip_address_unref (a); + + if (nmtst_get_rand_int () % 2) + NMTST_SWAP (s1, s2); + + success = nm_setting_compare (s1, s2, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + + success = nm_setting_diff (s1, s2, NM_SETTING_COMPARE_FLAG_EXACT, FALSE, &result); + g_assert (!success); + g_clear_pointer (&result, g_hash_table_unref); +} + +static void +test_setting_compare_routes (void) +{ + gs_unref_object NMSetting *s1 = NULL, *s2 = NULL; + gboolean success; + NMIPRoute *r; + GHashTable *result = NULL; + + s1 = nm_setting_ip4_config_new (); + s2 = nm_setting_ip4_config_new (); + + r = nm_ip_route_new (AF_INET, "192.168.12.0", 24, "192.168.11.1", 473, NULL); + + nm_ip_route_set_attribute (r, "label", g_variant_new_string ("xoxoxo")); + nm_setting_ip_config_add_route ((NMSettingIPConfig *) s1, r); + + nm_ip_route_set_attribute (r, "label", g_variant_new_string ("hello")); + nm_setting_ip_config_add_route ((NMSettingIPConfig *) s2, r); + + nm_ip_route_unref (r); + + if (nmtst_get_rand_int () % 2) + NMTST_SWAP (s1, s2); + + success = nm_setting_compare (s1, s2, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + + success = nm_setting_diff (s1, s2, NM_SETTING_COMPARE_FLAG_EXACT, FALSE, &result); + g_assert (!success); + g_clear_pointer (&result, g_hash_table_unref); +} + +static void +test_setting_compare_wired_cloned_mac_address (void) +{ + gs_unref_object NMSetting *old = NULL, *new = NULL; + gboolean success; + gs_free char *str1 = NULL; + + old = nm_setting_wired_new (); + g_object_set (old, + NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "stable", + NULL); + + g_assert_cmpstr ("stable", ==, nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) old)); + g_object_get (old, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable", ==, str1); + g_clear_pointer (&str1, g_free); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "11:22:33:44:55:66", NULL); + + g_assert_cmpstr ("11:22:33:44:55:66", ==, nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) new)); + g_object_get (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("11:22:33:44:55:66", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, "stable-bia", NULL); + + g_assert_cmpstr ("stable-bia", ==, nm_setting_wired_get_cloned_mac_address ((NMSettingWired *) new)); + g_object_get (new, NM_SETTING_WIRED_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable-bia", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); +} + +static void +test_setting_compare_wireless_cloned_mac_address (void) +{ + gs_unref_object NMSetting *old = NULL, *new = NULL; + gboolean success; + gs_free char *str1 = NULL; + + old = nm_setting_wireless_new (); + g_object_set (old, + NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "stable", + NULL); + + g_assert_cmpstr ("stable", ==, nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) old)); + g_object_get (old, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable", ==, str1); + g_clear_pointer (&str1, g_free); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "11:22:33:44:55:66", NULL); + + g_assert_cmpstr ("11:22:33:44:55:66", ==, nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) new)); + g_object_get (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("11:22:33:44:55:66", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); + + new = nm_setting_duplicate (old); + g_object_set (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, "stable-bia", NULL); + + g_assert_cmpstr ("stable-bia", ==, nm_setting_wireless_get_cloned_mac_address ((NMSettingWireless *) new)); + g_object_get (new, NM_SETTING_WIRELESS_CLONED_MAC_ADDRESS, &str1, NULL); + g_assert_cmpstr ("stable-bia", ==, str1); + g_clear_pointer (&str1, g_free); + + success = nm_setting_compare (old, new, NM_SETTING_COMPARE_FLAG_EXACT); + g_assert (!success); + g_clear_object (&new); +} + +static void test_setting_compare_timestamp (void) { gs_unref_object NMSetting *old = NULL, *new = NULL; @@ -2755,7 +2905,7 @@ test_setting_connection_changed_signal (void) ASSERT_CHANGED (nm_setting_connection_add_permission (s_con, "user", "billsmith", NULL)); ASSERT_CHANGED (nm_setting_connection_remove_permission (s_con, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*iter != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (iter != NULL)); ASSERT_UNCHANGED (nm_setting_connection_remove_permission (s_con, 1)); g_test_assert_expected_messages (); @@ -2763,7 +2913,7 @@ test_setting_connection_changed_signal (void) ASSERT_CHANGED (nm_setting_connection_add_secondary (s_con, uuid)); ASSERT_CHANGED (nm_setting_connection_remove_secondary (s_con, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL)); ASSERT_UNCHANGED (nm_setting_connection_remove_secondary (s_con, 1)); g_test_assert_expected_messages (); @@ -2815,7 +2965,7 @@ test_setting_ip4_changed_signal (void) ASSERT_CHANGED (nm_setting_ip_config_add_dns (s_ip4, "11.22.0.0")); ASSERT_CHANGED (nm_setting_ip_config_remove_dns (s_ip4, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < priv->dns->len*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns->len)); ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns (s_ip4, 1)); g_test_assert_expected_messages (); @@ -2825,7 +2975,7 @@ test_setting_ip4_changed_signal (void) ASSERT_CHANGED (nm_setting_ip_config_add_dns_search (s_ip4, "foobar.com")); ASSERT_CHANGED (nm_setting_ip_config_remove_dns_search (s_ip4, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < priv->dns_search->len*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns_search->len)); ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns_search (s_ip4, 1)); g_test_assert_expected_messages (); @@ -2837,7 +2987,7 @@ test_setting_ip4_changed_signal (void) ASSERT_CHANGED (nm_setting_ip_config_add_address (s_ip4, addr)); ASSERT_CHANGED (nm_setting_ip_config_remove_address (s_ip4, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < priv->addresses->len*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->addresses->len)); ASSERT_UNCHANGED (nm_setting_ip_config_remove_address (s_ip4, 1)); g_test_assert_expected_messages (); @@ -2850,7 +3000,7 @@ test_setting_ip4_changed_signal (void) ASSERT_CHANGED (nm_setting_ip_config_add_route (s_ip4, route)); ASSERT_CHANGED (nm_setting_ip_config_remove_route (s_ip4, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < priv->routes->len*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->routes->len)); ASSERT_UNCHANGED (nm_setting_ip_config_remove_route (s_ip4, 1)); g_test_assert_expected_messages (); @@ -2860,7 +3010,7 @@ test_setting_ip4_changed_signal (void) ASSERT_CHANGED (nm_setting_ip_config_add_dns_option (s_ip4, "debug")); ASSERT_CHANGED (nm_setting_ip_config_remove_dns_option (s_ip4, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < priv->dns_options->len*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns_options->len)); ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns_option (s_ip4, 1)); g_test_assert_expected_messages (); @@ -2891,7 +3041,7 @@ test_setting_ip6_changed_signal (void) ASSERT_CHANGED (nm_setting_ip_config_add_dns (s_ip6, "1:2:3::4:5:6")); ASSERT_CHANGED (nm_setting_ip_config_remove_dns (s_ip6, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < priv->dns->len*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns->len)); ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns (s_ip6, 1)); g_test_assert_expected_messages (); @@ -2901,7 +3051,7 @@ test_setting_ip6_changed_signal (void) ASSERT_CHANGED (nm_setting_ip_config_add_dns_search (s_ip6, "foobar.com")); ASSERT_CHANGED (nm_setting_ip_config_remove_dns_search (s_ip6, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < priv->dns_search->len*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->dns_search->len)); ASSERT_UNCHANGED (nm_setting_ip_config_remove_dns_search (s_ip6, 1)); g_test_assert_expected_messages (); @@ -2914,7 +3064,7 @@ test_setting_ip6_changed_signal (void) ASSERT_CHANGED (nm_setting_ip_config_add_address (s_ip6, addr)); ASSERT_CHANGED (nm_setting_ip_config_remove_address (s_ip6, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < priv->addresses->len*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->addresses->len)); ASSERT_UNCHANGED (nm_setting_ip_config_remove_address (s_ip6, 1)); g_test_assert_expected_messages (); @@ -2927,7 +3077,7 @@ test_setting_ip6_changed_signal (void) ASSERT_CHANGED (nm_setting_ip_config_add_route (s_ip6, route)); ASSERT_CHANGED (nm_setting_ip_config_remove_route (s_ip6, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < priv->routes->len*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < priv->routes->len)); ASSERT_UNCHANGED (nm_setting_ip_config_remove_route (s_ip6, 1)); g_test_assert_expected_messages (); @@ -2957,7 +3107,7 @@ test_setting_vlan_changed_signal (void) ASSERT_CHANGED (nm_setting_vlan_add_priority (s_vlan, NM_VLAN_INGRESS_MAP, 1, 3)); ASSERT_CHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_INGRESS_MAP, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < g_slist_length (list)*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < g_slist_length (list))); ASSERT_UNCHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_INGRESS_MAP, 1)); g_test_assert_expected_messages (); ASSERT_CHANGED (nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_INGRESS_MAP, "1:3")); @@ -2965,7 +3115,7 @@ test_setting_vlan_changed_signal (void) ASSERT_CHANGED (nm_setting_vlan_add_priority (s_vlan, NM_VLAN_EGRESS_MAP, 1, 3)); ASSERT_CHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_EGRESS_MAP, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*idx < g_slist_length (list)*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (idx < g_slist_length (list))); ASSERT_UNCHANGED (nm_setting_vlan_remove_priority (s_vlan, NM_VLAN_EGRESS_MAP, 1)); g_test_assert_expected_messages (); ASSERT_CHANGED (nm_setting_vlan_add_priority_str (s_vlan, NM_VLAN_EGRESS_MAP, "1:3")); @@ -3064,7 +3214,7 @@ test_setting_wireless_security_changed_signal (void) /* Protos */ ASSERT_CHANGED (nm_setting_wireless_security_add_proto (s_wsec, "wpa")); ASSERT_CHANGED (nm_setting_wireless_security_remove_proto (s_wsec, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL)); ASSERT_UNCHANGED (nm_setting_wireless_security_remove_proto (s_wsec, 1)); g_test_assert_expected_messages (); @@ -3074,7 +3224,7 @@ test_setting_wireless_security_changed_signal (void) /* Pairwise ciphers */ ASSERT_CHANGED (nm_setting_wireless_security_add_pairwise (s_wsec, "tkip")); ASSERT_CHANGED (nm_setting_wireless_security_remove_pairwise (s_wsec, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL)); ASSERT_UNCHANGED (nm_setting_wireless_security_remove_pairwise (s_wsec, 1)); g_test_assert_expected_messages (); @@ -3084,7 +3234,7 @@ test_setting_wireless_security_changed_signal (void) /* Group ciphers */ ASSERT_CHANGED (nm_setting_wireless_security_add_group (s_wsec, "ccmp")); ASSERT_CHANGED (nm_setting_wireless_security_remove_group (s_wsec, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL)); ASSERT_UNCHANGED (nm_setting_wireless_security_remove_group (s_wsec, 1)); g_test_assert_expected_messages (); @@ -3119,7 +3269,7 @@ test_setting_802_1x_changed_signal (void) /* EAP methods */ ASSERT_CHANGED (nm_setting_802_1x_add_eap_method (s_8021x, "tls")); ASSERT_CHANGED (nm_setting_802_1x_remove_eap_method (s_8021x, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL)); ASSERT_UNCHANGED (nm_setting_802_1x_remove_eap_method (s_8021x, 1)); g_test_assert_expected_messages (); @@ -3129,7 +3279,7 @@ test_setting_802_1x_changed_signal (void) /* alternate subject matches */ ASSERT_CHANGED (nm_setting_802_1x_add_altsubject_match (s_8021x, "EMAIL:server@example.com")); ASSERT_CHANGED (nm_setting_802_1x_remove_altsubject_match (s_8021x, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL)); ASSERT_UNCHANGED (nm_setting_802_1x_remove_altsubject_match (s_8021x, 1)); g_test_assert_expected_messages (); @@ -3139,7 +3289,7 @@ test_setting_802_1x_changed_signal (void) /* phase2 alternate subject matches */ ASSERT_CHANGED (nm_setting_802_1x_add_phase2_altsubject_match (s_8021x, "EMAIL:server@example.com")); ASSERT_CHANGED (nm_setting_802_1x_remove_phase2_altsubject_match (s_8021x, 0)); - g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, "*elt != NULL*"); + g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, NMTST_G_RETURN_MSG (elt != NULL)); ASSERT_UNCHANGED (nm_setting_802_1x_remove_phase2_altsubject_match (s_8021x, 1)); g_test_assert_expected_messages (); @@ -3773,7 +3923,6 @@ test_connection_normalize_may_fail (void) { gs_unref_object NMConnection *con = NULL; NMSettingIPConfig *s_ip4, *s_ip6; - gs_free_error GError *error = NULL; con = nmtst_create_minimal_connection ("test2", NULL, NM_SETTING_WIRED_SETTING_NAME, NULL); nmtst_assert_connection_verifies_and_normalizable (con); @@ -4477,6 +4626,110 @@ test_nm_utils_dns_option_find_idx (void) /******************************************************************************/ +static void +_json_config_check_valid (const char *conf, gboolean expected) +{ + gs_free_error 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, }; @@ -5074,6 +5327,10 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/test_setting_to_dbus_transform", test_setting_to_dbus_transform); g_test_add_func ("/core/general/test_setting_to_dbus_enum", test_setting_to_dbus_enum); g_test_add_func ("/core/general/test_setting_compare_id", test_setting_compare_id); + g_test_add_func ("/core/general/test_setting_compare_addresses", test_setting_compare_addresses); + g_test_add_func ("/core/general/test_setting_compare_routes", test_setting_compare_routes); + g_test_add_func ("/core/general/test_setting_compare_wired_cloned_mac_address", test_setting_compare_wired_cloned_mac_address); + g_test_add_func ("/core/general/test_setting_compare_wirless_cloned_mac_address", test_setting_compare_wireless_cloned_mac_address); g_test_add_func ("/core/general/test_setting_compare_timestamp", test_setting_compare_timestamp); #define ADD_FUNC(name, func, secret_flags, comp_flags, remove_secret) \ g_test_add_data_func_full ("/core/general/" G_STRINGIFY (func) "_" name, \ @@ -5163,7 +5420,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 (); diff --git a/libnm-core/tests/test-keyfile.c b/libnm-core/tests/test-keyfile.c index 92b6c1f5..ecb7cd5a 100644 --- a/libnm-core/tests/test-keyfile.c +++ b/libnm-core/tests/test-keyfile.c @@ -28,7 +28,7 @@ #include "nm-setting-wired.h" #include "nm-setting-8021x.h" -#include "nm-test-utils.h" +#include "nm-utils/nm-test-utils.h" #define TEST_WIRED_TLS_CA_CERT TEST_CERT_DIR"/test-ca-cert.pem" diff --git a/libnm-core/tests/test-secrets.c b/libnm-core/tests/test-secrets.c index a32ed4f1..0149348d 100644 --- a/libnm-core/tests/test-secrets.c +++ b/libnm-core/tests/test-secrets.c @@ -38,7 +38,7 @@ #include "nm-utils.h" #include "nm-core-internal.h" -#include "nm-test-utils.h" +#include "nm-utils/nm-test-utils.h" #define TEST_NEED_SECRETS_EAP_TLS_CA_CERT TEST_CERT_DIR "/test_ca_cert.pem" #define TEST_NEED_SECRETS_EAP_TLS_CLIENT_CERT TEST_CERT_DIR "/test_key_and_cert.pem" @@ -639,7 +639,7 @@ test_update_secrets_null_setting_name_with_setting_hash (void) secrets = build_wep_secrets (wepkey); g_test_expect_message ("libnm", G_LOG_LEVEL_CRITICAL, - "*nm_connection_update_secrets*setting_name != NULL || full_connection*"); + NMTST_G_RETURN_MSG (setting_name != NULL || full_connection)); success = nm_connection_update_secrets (connection, NULL, secrets, &error); g_test_assert_expected_messages (); g_assert_no_error (error); diff --git a/libnm-core/tests/test-setting-8021x.c b/libnm-core/tests/test-setting-8021x.c index 56563a6f..268a9f52 100644 --- a/libnm-core/tests/test-setting-8021x.c +++ b/libnm-core/tests/test-setting-8021x.c @@ -27,7 +27,7 @@ #include "nm-setting-connection.h" #include "nm-setting-8021x.h" -#include "nm-test-utils.h" +#include "nm-utils/nm-test-utils.h" static void compare_blob_data (const char *test, diff --git a/libnm-core/tests/test-setting-bond.c b/libnm-core/tests/test-setting-bond.c index a6afa1c8..91a81997 100644 --- a/libnm-core/tests/test-setting-bond.c +++ b/libnm-core/tests/test-setting-bond.c @@ -26,7 +26,7 @@ #include "nm-setting-connection.h" #include "nm-errors.h" -#include "nm-test-utils.h" +#include "nm-utils/nm-test-utils.h" static void create_bond_connection (NMConnection **con, NMSettingBond **s_bond) diff --git a/libnm-core/tests/test-setting-dcb.c b/libnm-core/tests/test-setting-dcb.c index 09cf8d98..55c1c516 100644 --- a/libnm-core/tests/test-setting-dcb.c +++ b/libnm-core/tests/test-setting-dcb.c @@ -28,7 +28,7 @@ #include "nm-connection.h" #include "nm-errors.h" -#include "nm-test-utils.h" +#include "nm-utils/nm-test-utils.h" #define DCB_FLAGS_ALL (NM_SETTING_DCB_FLAG_ENABLE | \ NM_SETTING_DCB_FLAG_ADVERTISE | \ diff --git a/libnm-core/tests/test-settings-defaults.c b/libnm-core/tests/test-settings-defaults.c index 366910ac..95570b03 100644 --- a/libnm-core/tests/test-settings-defaults.c +++ b/libnm-core/tests/test-settings-defaults.c @@ -38,7 +38,7 @@ #include "nm-setting-wireless.h" #include "nm-setting-wireless-security.h" -#include "nm-test-utils.h" +#include "nm-utils/nm-test-utils.h" static void test_defaults (GType type, const char *name) |