mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-07-21 18:24:49 +00:00
![Thomas Haller](/assets/img/avatar_default.png)
and _nm_utils_inet6_ntop() instead of nm_utils_inet6_ntop(). nm_utils_inet4_ntop()/nm_utils_inet6_ntop() are public API of libnm. For one, that means they are only available in code that links with libnm/libnm-core. But such basic helpers should be available everywhere. Also, they accept NULL as destination buffers. We keep that behavior for potential libnm users, but internally we never want to use the static buffers. This patch needs to take care that there are no callers of _nm_utils_inet[46]_ntop() that pass NULL buffers. Also, _nm_utils_inet[46]_ntop() are inline functions and the compiler can get rid of them. We should consistently use the same variant of the helper. The only downside is that the "good" name is already taken. The leading underscore is rather ugly and inconsistent. Also, with our internal variants we can use "static array indices in function parameter declarations" next. Thereby the compiler helps to ensure that the provided buffers are of the right size.
3436 lines
107 KiB
C
3436 lines
107 KiB
C
// SPDX-License-Identifier: LGPL-2.1+
|
|
/*
|
|
* Copyright (C) 2007 - 2018 Red Hat, Inc.
|
|
* Copyright (C) 2007 - 2008 Novell, Inc.
|
|
*/
|
|
|
|
#include "nm-default.h"
|
|
|
|
#include "nm-connection.h"
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include "nm-connection-private.h"
|
|
#include "nm-utils.h"
|
|
#include "nm-setting-private.h"
|
|
#include "nm-core-internal.h"
|
|
|
|
/**
|
|
* SECTION:nm-connection
|
|
* @short_description: Describes a connection to specific network or provider
|
|
*
|
|
* An #NMConnection describes all the settings and configuration values that
|
|
* are necessary to configure network devices for operation on a specific
|
|
* network. Connections are the fundamental operating object for
|
|
* NetworkManager; no device is connected without a #NMConnection, or
|
|
* disconnected without having been connected with a #NMConnection.
|
|
*
|
|
* Each #NMConnection contains a list of #NMSetting objects usually referenced
|
|
* by name (using nm_connection_get_setting_by_name()) or by type (with
|
|
* nm_connection_get_setting()). The settings describe the actual parameters
|
|
* with which the network devices are configured, including device-specific
|
|
* parameters (MTU, SSID, APN, channel, rate, etc) and IP-level parameters
|
|
* (addresses, routes, addressing methods, etc).
|
|
*
|
|
*/
|
|
|
|
/*****************************************************************************/
|
|
|
|
enum {
|
|
SECRETS_UPDATED,
|
|
SECRETS_CLEARED,
|
|
CHANGED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
typedef struct {
|
|
NMConnection *self;
|
|
|
|
GHashTable *settings;
|
|
|
|
/* D-Bus path of the connection, if any */
|
|
char *path;
|
|
} NMConnectionPrivate;
|
|
|
|
G_DEFINE_INTERFACE (NMConnection, nm_connection, G_TYPE_OBJECT)
|
|
|
|
static NMConnectionPrivate *nm_connection_get_private (NMConnection *connection);
|
|
#define NM_CONNECTION_GET_PRIVATE(o) (nm_connection_get_private ((NMConnection *)o))
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gpointer
|
|
_gtype_to_hash_key (GType gtype)
|
|
{
|
|
#if NM_MORE_ASSERTS
|
|
_nm_unused const gsize *const test_gtype_typedef = >ype;
|
|
|
|
nm_assert ((GType) (GPOINTER_TO_SIZE (GSIZE_TO_POINTER (gtype))) == gtype);
|
|
G_STATIC_ASSERT_EXPR (sizeof (gpointer) >= sizeof (gsize));
|
|
G_STATIC_ASSERT_EXPR (sizeof (gsize) == sizeof (GType));
|
|
#endif
|
|
|
|
return GSIZE_TO_POINTER (gtype);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
setting_changed_cb (NMSetting *setting,
|
|
GParamSpec *pspec,
|
|
NMConnection *self)
|
|
{
|
|
g_signal_emit (self, signals[CHANGED], 0);
|
|
}
|
|
|
|
static void
|
|
_setting_release (NMConnection *connection, NMSetting *setting)
|
|
{
|
|
g_signal_handlers_disconnect_by_func (setting, setting_changed_cb, connection);
|
|
}
|
|
|
|
static gboolean
|
|
_setting_release_hfr (gpointer key, gpointer value, gpointer user_data)
|
|
{
|
|
_setting_release (user_data, value);
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
_nm_connection_add_setting (NMConnection *connection, NMSetting *setting)
|
|
{
|
|
NMConnectionPrivate *priv;
|
|
GType setting_type;
|
|
NMSetting *s_old;
|
|
|
|
nm_assert (NM_IS_CONNECTION (connection));
|
|
nm_assert (NM_IS_SETTING (setting));
|
|
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
setting_type = G_OBJECT_TYPE (setting);
|
|
|
|
if ((s_old = g_hash_table_lookup (priv->settings, _gtype_to_hash_key (setting_type))))
|
|
_setting_release (connection, s_old);
|
|
|
|
g_hash_table_insert (priv->settings, _gtype_to_hash_key (setting_type), setting);
|
|
|
|
g_signal_connect (setting, "notify", (GCallback) setting_changed_cb, connection);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_add_setting:
|
|
* @connection: a #NMConnection
|
|
* @setting: (transfer full): the #NMSetting to add to the connection object
|
|
*
|
|
* Adds a #NMSetting to the connection, replacing any previous #NMSetting of the
|
|
* same name which has previously been added to the #NMConnection. The
|
|
* connection takes ownership of the #NMSetting object and does not increase
|
|
* the setting object's reference count.
|
|
**/
|
|
void
|
|
nm_connection_add_setting (NMConnection *connection, NMSetting *setting)
|
|
{
|
|
g_return_if_fail (NM_IS_CONNECTION (connection));
|
|
g_return_if_fail (NM_IS_SETTING (setting));
|
|
|
|
_nm_connection_add_setting (connection, setting);
|
|
g_signal_emit (connection, signals[CHANGED], 0);
|
|
}
|
|
|
|
gboolean
|
|
_nm_connection_remove_setting (NMConnection *connection, GType setting_type)
|
|
{
|
|
NMConnectionPrivate *priv;
|
|
NMSetting *setting;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
|
|
g_return_val_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING), FALSE);
|
|
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
setting = g_hash_table_lookup (priv->settings, _gtype_to_hash_key (setting_type));
|
|
if (setting) {
|
|
g_signal_handlers_disconnect_by_func (setting, setting_changed_cb, connection);
|
|
g_hash_table_remove (priv->settings, _gtype_to_hash_key (setting_type));
|
|
g_signal_emit (connection, signals[CHANGED], 0);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_remove_setting:
|
|
* @connection: a #NMConnection
|
|
* @setting_type: the #GType of the setting object to remove
|
|
*
|
|
* Removes the #NMSetting with the given #GType from the #NMConnection. This
|
|
* operation dereferences the #NMSetting object.
|
|
**/
|
|
void
|
|
nm_connection_remove_setting (NMConnection *connection, GType setting_type)
|
|
{
|
|
_nm_connection_remove_setting (connection, setting_type);
|
|
}
|
|
|
|
static gpointer
|
|
_connection_get_setting (NMConnection *connection, GType setting_type)
|
|
{
|
|
NMSetting *setting;
|
|
|
|
nm_assert (NM_IS_CONNECTION (connection));
|
|
nm_assert (g_type_is_a (setting_type, NM_TYPE_SETTING));
|
|
|
|
setting = g_hash_table_lookup (NM_CONNECTION_GET_PRIVATE (connection)->settings,
|
|
_gtype_to_hash_key (setting_type));
|
|
nm_assert (!setting || G_TYPE_CHECK_INSTANCE_TYPE (setting, setting_type));
|
|
return setting;
|
|
}
|
|
|
|
static gpointer
|
|
_connection_get_setting_check (NMConnection *connection, GType setting_type)
|
|
{
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
|
|
|
|
return _connection_get_setting (connection, setting_type);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting:
|
|
* @connection: a #NMConnection
|
|
* @setting_type: the #GType of the setting object to return
|
|
*
|
|
* Gets the #NMSetting with the given #GType, if one has been previously added
|
|
* to the #NMConnection.
|
|
*
|
|
* Returns: (transfer none): the #NMSetting, or %NULL if no setting of that type was previously
|
|
* added to the #NMConnection
|
|
**/
|
|
NMSetting *
|
|
nm_connection_get_setting (NMConnection *connection, GType setting_type)
|
|
{
|
|
g_return_val_if_fail (g_type_is_a (setting_type, NM_TYPE_SETTING), NULL);
|
|
|
|
return _connection_get_setting_check (connection, setting_type);
|
|
}
|
|
|
|
NMSettingIPConfig *
|
|
nm_connection_get_setting_ip_config (NMConnection *connection,
|
|
int addr_family)
|
|
{
|
|
nm_assert_addr_family (addr_family);
|
|
|
|
return NM_SETTING_IP_CONFIG (_connection_get_setting (connection,
|
|
(addr_family == AF_INET)
|
|
? NM_TYPE_SETTING_IP4_CONFIG
|
|
: NM_TYPE_SETTING_IP6_CONFIG));
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_by_name:
|
|
* @connection: a #NMConnection
|
|
* @name: a setting name
|
|
*
|
|
* Gets the #NMSetting with the given name, if one has been previously added
|
|
* the #NMConnection.
|
|
*
|
|
* Returns: (transfer none): the #NMSetting, or %NULL if no setting with that name was previously
|
|
* added to the #NMConnection
|
|
**/
|
|
NMSetting *
|
|
nm_connection_get_setting_by_name (NMConnection *connection, const char *name)
|
|
{
|
|
GType type;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
|
|
|
|
type = nm_setting_lookup_type (name);
|
|
return type ? _connection_get_setting (connection, type) : NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
gpointer /* (NMSetting *) */
|
|
_nm_connection_check_main_setting (NMConnection *connection,
|
|
const char *setting_name,
|
|
GError **error)
|
|
{
|
|
NMSetting *setting;
|
|
|
|
nm_assert (NM_IS_CONNECTION (connection));
|
|
nm_assert (setting_name);
|
|
|
|
if (!nm_connection_is_type (connection, setting_name)) {
|
|
nm_utils_error_set (error,
|
|
NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
|
|
"connection type is not \"%s\"",
|
|
setting_name);
|
|
return NULL;
|
|
}
|
|
|
|
setting = nm_connection_get_setting_by_name (connection, setting_name);
|
|
if (!setting) {
|
|
nm_utils_error_set (error,
|
|
NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
|
|
"connection misses \"%s\" settings",
|
|
setting_name);
|
|
return NULL;
|
|
}
|
|
|
|
return setting;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gboolean
|
|
validate_permissions_type (GVariant *variant, GError **error)
|
|
{
|
|
GVariant *s_con;
|
|
GVariant *permissions;
|
|
gboolean valid = TRUE;
|
|
|
|
/* Ensure the connection::permissions item (if present) is the correct
|
|
* type, otherwise the g_object_set() will throw a warning and ignore the
|
|
* error, leaving us with no permissions.
|
|
*/
|
|
s_con = g_variant_lookup_value (variant, NM_SETTING_CONNECTION_SETTING_NAME, NM_VARIANT_TYPE_SETTING);
|
|
if (!s_con)
|
|
return TRUE;
|
|
|
|
permissions = g_variant_lookup_value (s_con, NM_SETTING_CONNECTION_PERMISSIONS, NULL);
|
|
if (permissions) {
|
|
if (!g_variant_is_of_type (permissions, G_VARIANT_TYPE_STRING_ARRAY)) {
|
|
g_set_error_literal (error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("wrong type; should be a list of strings."));
|
|
g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_PERMISSIONS);
|
|
valid = FALSE;
|
|
}
|
|
g_variant_unref (permissions);
|
|
}
|
|
|
|
g_variant_unref (s_con);
|
|
return valid;
|
|
}
|
|
|
|
/**
|
|
* _nm_connection_replace_settings:
|
|
* @connection: a #NMConnection
|
|
* @new_settings: a #GVariant of type %NM_VARIANT_TYPE_CONNECTION, with the new settings
|
|
* @parse_flags: flags.
|
|
* @error: location to store error, or %NULL
|
|
*
|
|
* Replaces @connection's settings with @new_settings (which must be
|
|
* syntactically valid, and describe a known type of connection, but does not
|
|
* need to result in a connection that passes nm_connection_verify()).
|
|
*
|
|
* Returns: %TRUE if connection was updated, %FALSE if @new_settings could not
|
|
* be deserialized (in which case @connection will be unchanged).
|
|
* Only exception is the NM_SETTING_PARSE_FLAGS_NORMALIZE flag: if normalization
|
|
* fails, the input @connection is already modified and the original settings
|
|
* are lost.
|
|
**/
|
|
gboolean
|
|
_nm_connection_replace_settings (NMConnection *connection,
|
|
GVariant *new_settings,
|
|
NMSettingParseFlags parse_flags,
|
|
GError **error)
|
|
{
|
|
NMConnectionPrivate *priv;
|
|
GVariantIter iter;
|
|
const char *setting_name;
|
|
GVariant *setting_dict;
|
|
GSList *settings = NULL, *s;
|
|
gboolean changed, success;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
|
|
g_return_val_if_fail (g_variant_is_of_type (new_settings, NM_VARIANT_TYPE_CONNECTION), FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
nm_assert (!NM_FLAGS_ANY (parse_flags, ~NM_SETTING_PARSE_FLAGS_ALL));
|
|
nm_assert (!NM_FLAGS_ALL (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT | NM_SETTING_PARSE_FLAGS_BEST_EFFORT));
|
|
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
if ( !NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT)
|
|
&& !validate_permissions_type (new_settings, error))
|
|
return FALSE;
|
|
|
|
g_variant_iter_init (&iter, new_settings);
|
|
while (g_variant_iter_next (&iter, "{&s@a{sv}}", &setting_name, &setting_dict)) {
|
|
gs_unref_variant GVariant *setting_dict_free = NULL;
|
|
GError *local = NULL;
|
|
NMSetting *setting;
|
|
GType type;
|
|
|
|
setting_dict_free = setting_dict;
|
|
|
|
type = nm_setting_lookup_type (setting_name);
|
|
if (type == G_TYPE_INVALID) {
|
|
if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
|
|
continue;
|
|
g_set_error_literal (error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("unknown setting name"));
|
|
g_prefix_error (error, "%s: ", setting_name);
|
|
g_slist_free_full (settings, g_object_unref);
|
|
return FALSE;
|
|
}
|
|
|
|
for (s = settings; s; s = s->next) {
|
|
if (G_OBJECT_TYPE (s->data) == type) {
|
|
if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_STRICT)) {
|
|
g_set_error_literal (error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("duplicate setting name"));
|
|
g_prefix_error (error, "%s: ", setting_name);
|
|
g_slist_free_full (settings, g_object_unref);
|
|
return FALSE;
|
|
}
|
|
/* last wins. */
|
|
g_object_unref (s->data);
|
|
settings = g_slist_delete_link (settings, s);
|
|
break;
|
|
}
|
|
}
|
|
|
|
setting = _nm_setting_new_from_dbus (type, setting_dict, new_settings, parse_flags, &local);
|
|
|
|
if (!setting) {
|
|
if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_BEST_EFFORT))
|
|
continue;
|
|
g_propagate_error (error, local);
|
|
g_slist_free_full (settings, g_object_unref);
|
|
return FALSE;
|
|
}
|
|
|
|
settings = g_slist_prepend (settings, setting);
|
|
}
|
|
|
|
if (g_hash_table_size (priv->settings) > 0) {
|
|
g_hash_table_foreach_remove (priv->settings, _setting_release_hfr, connection);
|
|
changed = TRUE;
|
|
} else
|
|
changed = (settings != NULL);
|
|
|
|
/* Note: @settings might be empty in which case the connection
|
|
* has no NMSetting instances... which is fine, just something
|
|
* to be aware of. */
|
|
for (s = settings; s; s = s->next)
|
|
_nm_connection_add_setting (connection, s->data);
|
|
|
|
g_slist_free (settings);
|
|
|
|
/* If verification/normalization fails, the original connection
|
|
* is already lost. From an API point of view, it would be nicer
|
|
* not to touch the input argument if we fail at the end.
|
|
* However, that would require creating a temporary connection
|
|
* to validate it first. As none of the caller cares about the
|
|
* state of the @connection when normalization fails, just do it
|
|
* this way. */
|
|
if (NM_FLAGS_HAS (parse_flags, NM_SETTING_PARSE_FLAGS_NORMALIZE))
|
|
success = nm_connection_normalize (connection, NULL, NULL, error);
|
|
else
|
|
success = TRUE;
|
|
|
|
if (changed)
|
|
g_signal_emit (connection, signals[CHANGED], 0);
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_replace_settings:
|
|
* @connection: a #NMConnection
|
|
* @new_settings: a #GVariant of type %NM_VARIANT_TYPE_CONNECTION, with the new settings
|
|
* @error: location to store error, or %NULL
|
|
*
|
|
* Replaces @connection's settings with @new_settings (which must be
|
|
* syntactically valid, and describe a known type of connection, but does not
|
|
* need to result in a connection that passes nm_connection_verify()).
|
|
*
|
|
* Returns: %TRUE if connection was updated, %FALSE if @new_settings could not
|
|
* be deserialized (in which case @connection will be unchanged).
|
|
**/
|
|
gboolean
|
|
nm_connection_replace_settings (NMConnection *connection,
|
|
GVariant *new_settings,
|
|
GError **error)
|
|
{
|
|
return _nm_connection_replace_settings (connection, new_settings, NM_SETTING_PARSE_FLAGS_NONE, error);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_replace_settings_from_connection:
|
|
* @connection: a #NMConnection
|
|
* @new_connection: a #NMConnection to replace the settings of @connection with
|
|
*
|
|
* Deep-copies the settings of @new_connection and replaces the settings of @connection
|
|
* with the copied settings.
|
|
**/
|
|
void
|
|
nm_connection_replace_settings_from_connection (NMConnection *connection,
|
|
NMConnection *new_connection)
|
|
{
|
|
NMConnectionPrivate *priv, *new_priv;
|
|
GHashTableIter iter;
|
|
NMSetting *setting;
|
|
gboolean changed;
|
|
|
|
g_return_if_fail (NM_IS_CONNECTION (connection));
|
|
g_return_if_fail (NM_IS_CONNECTION (new_connection));
|
|
|
|
/* When 'connection' and 'new_connection' are the same object simply return
|
|
* in order not to destroy 'connection'.
|
|
*/
|
|
if (connection == new_connection)
|
|
return;
|
|
|
|
/* No need to validate permissions like nm_connection_replace_settings()
|
|
* since we're dealing with an NMConnection which has already done that.
|
|
*/
|
|
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
new_priv = NM_CONNECTION_GET_PRIVATE (new_connection);
|
|
|
|
if ((changed = g_hash_table_size (priv->settings) > 0))
|
|
g_hash_table_foreach_remove (priv->settings, _setting_release_hfr, connection);
|
|
|
|
if (g_hash_table_size (new_priv->settings)) {
|
|
g_hash_table_iter_init (&iter, new_priv->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting))
|
|
_nm_connection_add_setting (connection, nm_setting_duplicate (setting));
|
|
changed = TRUE;
|
|
}
|
|
|
|
if (changed)
|
|
g_signal_emit (connection, signals[CHANGED], 0);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_clear_settings:
|
|
* @connection: a #NMConnection
|
|
*
|
|
* Deletes all of @connection's settings.
|
|
**/
|
|
void
|
|
nm_connection_clear_settings (NMConnection *connection)
|
|
{
|
|
NMConnectionPrivate *priv;
|
|
|
|
g_return_if_fail (NM_IS_CONNECTION (connection));
|
|
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
if (g_hash_table_size (priv->settings) > 0) {
|
|
g_hash_table_foreach_remove (priv->settings, _setting_release_hfr, connection);
|
|
g_signal_emit (connection, signals[CHANGED], 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* nm_connection_compare:
|
|
* @a: a #NMConnection
|
|
* @b: a second #NMConnection to compare with the first
|
|
* @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
|
|
*
|
|
* Compares two #NMConnection objects for similarity, with comparison behavior
|
|
* modified by a set of flags. See nm_setting_compare() for a description of
|
|
* each flag's behavior.
|
|
*
|
|
* Returns: %TRUE if the comparison succeeds, %FALSE if it does not
|
|
**/
|
|
gboolean
|
|
nm_connection_compare (NMConnection *a,
|
|
NMConnection *b,
|
|
NMSettingCompareFlags flags)
|
|
{
|
|
GHashTableIter iter;
|
|
NMSetting *src;
|
|
|
|
if (a == b)
|
|
return TRUE;
|
|
if (!a || !b)
|
|
return FALSE;
|
|
|
|
/* B / A: ensure settings in B that are not in A make the comparison fail */
|
|
if (g_hash_table_size (NM_CONNECTION_GET_PRIVATE (a)->settings) !=
|
|
g_hash_table_size (NM_CONNECTION_GET_PRIVATE (b)->settings))
|
|
return FALSE;
|
|
|
|
/* A / B: ensure all settings in A match corresponding ones in B */
|
|
g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (a)->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &src)) {
|
|
NMSetting *cmp = nm_connection_get_setting (b, G_OBJECT_TYPE (src));
|
|
|
|
if ( !cmp
|
|
|| !_nm_setting_compare (a, src, b, cmp, flags))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
diff_one_connection (NMConnection *a,
|
|
NMConnection *b,
|
|
NMSettingCompareFlags flags,
|
|
gboolean invert_results,
|
|
GHashTable *diffs)
|
|
{
|
|
NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (a);
|
|
GHashTableIter iter;
|
|
NMSetting *a_setting = NULL;
|
|
gboolean diff_found = FALSE;
|
|
|
|
g_hash_table_iter_init (&iter, priv->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &a_setting)) {
|
|
NMSetting *b_setting = NULL;
|
|
const char *setting_name = nm_setting_get_name (a_setting);
|
|
GHashTable *results;
|
|
gboolean new_results = TRUE;
|
|
|
|
if (b)
|
|
b_setting = nm_connection_get_setting (b, G_OBJECT_TYPE (a_setting));
|
|
|
|
results = g_hash_table_lookup (diffs, setting_name);
|
|
if (results)
|
|
new_results = FALSE;
|
|
|
|
if (!_nm_setting_diff (a, a_setting, b, b_setting, flags, invert_results, &results))
|
|
diff_found = TRUE;
|
|
|
|
if (new_results && results)
|
|
g_hash_table_insert (diffs, g_strdup (setting_name), results);
|
|
}
|
|
|
|
return diff_found;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_diff:
|
|
* @a: a #NMConnection
|
|
* @b: a second #NMConnection to compare with the first
|
|
* @flags: compare flags, e.g. %NM_SETTING_COMPARE_FLAG_EXACT
|
|
* @out_settings: (element-type utf8 GLib.HashTable): if the
|
|
* connections differ, on return a hash table mapping setting names to
|
|
* second-level GHashTable (utf8 to guint32), which contains the key names that
|
|
* differ mapped to one or more of %NMSettingDiffResult as a bitfield
|
|
*
|
|
* Compares two #NMConnection objects for similarity, with comparison behavior
|
|
* modified by a set of flags. See nm_setting_compare() for a description of
|
|
* each flag's behavior. If the connections differ, settings and keys within
|
|
* each setting that differ are added to the returned @out_settings hash table.
|
|
* No values are returned, only key names.
|
|
*
|
|
* Returns: %TRUE if the connections contain the same values, %FALSE if they do
|
|
* not
|
|
**/
|
|
gboolean
|
|
nm_connection_diff (NMConnection *a,
|
|
NMConnection *b,
|
|
NMSettingCompareFlags flags,
|
|
GHashTable **out_settings)
|
|
{
|
|
GHashTable *diffs;
|
|
gboolean diff_found = FALSE;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (a), FALSE);
|
|
g_return_val_if_fail (!out_settings || !*out_settings, FALSE);
|
|
g_return_val_if_fail (!b || NM_IS_CONNECTION (b), FALSE);
|
|
|
|
if (a == b)
|
|
return TRUE;
|
|
|
|
diffs = g_hash_table_new_full (nm_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
|
|
|
|
/* Diff A to B, then B to A to capture keys in B that aren't in A */
|
|
if (diff_one_connection (a, b, flags, FALSE, diffs))
|
|
diff_found = TRUE;
|
|
if ( b
|
|
&& diff_one_connection (b, a, flags, TRUE, diffs))
|
|
diff_found = TRUE;
|
|
|
|
nm_assert (diff_found == (g_hash_table_size (diffs) != 0));
|
|
|
|
if (g_hash_table_size (diffs) == 0) {
|
|
g_hash_table_destroy (diffs);
|
|
diffs = NULL;
|
|
}
|
|
|
|
NM_SET_OUT (out_settings, diffs);
|
|
|
|
return !diff_found;
|
|
}
|
|
|
|
NMSetting *
|
|
_nm_connection_find_base_type_setting (NMConnection *connection)
|
|
{
|
|
NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
GHashTableIter iter;
|
|
NMSetting *setting = NULL;
|
|
NMSetting *s_iter;
|
|
NMSettingPriority setting_prio = NM_SETTING_PRIORITY_USER;
|
|
NMSettingPriority s_iter_prio;
|
|
|
|
g_hash_table_iter_init (&iter, priv->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &s_iter)) {
|
|
s_iter_prio = _nm_setting_get_base_type_priority (s_iter);
|
|
if (s_iter_prio == NM_SETTING_PRIORITY_INVALID)
|
|
continue;
|
|
|
|
if (setting) {
|
|
if (s_iter_prio > setting_prio) {
|
|
continue;
|
|
} else if (s_iter_prio == setting_prio) {
|
|
NMSettingConnection *s_con = nm_connection_get_setting_connection (connection);
|
|
const char *type;
|
|
|
|
if (s_con) {
|
|
type = nm_setting_connection_get_connection_type (s_con);
|
|
if (type)
|
|
return nm_connection_get_setting_by_name (connection, type);
|
|
}
|
|
return NULL;
|
|
}
|
|
}
|
|
setting = s_iter;
|
|
setting_prio = s_iter_prio;
|
|
}
|
|
return setting;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_connection_uuid (NMConnection *self)
|
|
{
|
|
NMSettingConnection *s_con = nm_connection_get_setting_connection (self);
|
|
char uuid[37];
|
|
|
|
nm_assert (s_con);
|
|
|
|
if (nm_setting_connection_get_uuid (s_con))
|
|
return FALSE;
|
|
|
|
g_object_set (s_con,
|
|
NM_SETTING_CONNECTION_UUID,
|
|
nm_utils_uuid_generate_buf (uuid),
|
|
NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_connection_type (NMConnection *self)
|
|
{
|
|
NMSettingConnection *s_con = nm_connection_get_setting_connection (self);
|
|
NMSetting *s_base = NULL;
|
|
const char *type;
|
|
|
|
type = nm_setting_connection_get_connection_type (s_con);
|
|
|
|
if (type) {
|
|
s_base = nm_connection_get_setting_by_name (self, type);
|
|
|
|
if (!s_base) {
|
|
GType base_type = nm_setting_lookup_type (type);
|
|
|
|
g_return_val_if_fail (base_type, FALSE);
|
|
nm_connection_add_setting (self, g_object_new (base_type, NULL));
|
|
return TRUE;
|
|
}
|
|
} else {
|
|
s_base = _nm_connection_find_base_type_setting (self);
|
|
g_return_val_if_fail (s_base, FALSE);
|
|
|
|
type = nm_setting_get_name (s_base);
|
|
g_object_set (s_con, NM_SETTING_CONNECTION_TYPE, type, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
const char *
|
|
_nm_connection_detect_bluetooth_type (NMConnection *self)
|
|
{
|
|
NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self);
|
|
|
|
if ( s_bt
|
|
&& nm_setting_bluetooth_get_connection_type (s_bt)) {
|
|
if ( nm_connection_get_setting_gsm (self)
|
|
|| nm_connection_get_setting_cdma (self))
|
|
return NM_SETTING_BLUETOOTH_TYPE_DUN;
|
|
if (nm_connection_get_setting_bridge (self))
|
|
return NM_SETTING_BLUETOOTH_TYPE_NAP;
|
|
return NM_SETTING_BLUETOOTH_TYPE_PANU;
|
|
}
|
|
|
|
/* NULL means the connection is not a bluetooth type, or it needs
|
|
* no normalization, as the type is set explicitly. */
|
|
return NULL;
|
|
}
|
|
|
|
const char *
|
|
_nm_connection_detect_slave_type (NMConnection *connection, NMSetting **out_s_port)
|
|
{
|
|
NMConnectionPrivate *priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
GHashTableIter iter;
|
|
const char *slave_type = NULL;
|
|
NMSetting *s_port = NULL, *s_iter;
|
|
|
|
g_hash_table_iter_init (&iter, priv->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &s_iter)) {
|
|
const char *name = nm_setting_get_name (s_iter);
|
|
const char *i_slave_type = NULL;
|
|
|
|
if (!strcmp (name, NM_SETTING_BRIDGE_PORT_SETTING_NAME))
|
|
i_slave_type = NM_SETTING_BRIDGE_SETTING_NAME;
|
|
else if (!strcmp (name, NM_SETTING_TEAM_PORT_SETTING_NAME))
|
|
i_slave_type = NM_SETTING_TEAM_SETTING_NAME;
|
|
else if (!strcmp (name, NM_SETTING_OVS_PORT_SETTING_NAME))
|
|
i_slave_type = NM_SETTING_OVS_BRIDGE_SETTING_NAME;
|
|
else if (!strcmp (name, NM_SETTING_OVS_INTERFACE_SETTING_NAME))
|
|
i_slave_type = NM_SETTING_OVS_PORT_SETTING_NAME;
|
|
else
|
|
continue;
|
|
|
|
if (slave_type) {
|
|
/* there are more then one matching port types, cannot detect the slave type. */
|
|
slave_type = NULL;
|
|
s_port = NULL;
|
|
break;
|
|
}
|
|
slave_type = i_slave_type;
|
|
s_port = s_iter;
|
|
}
|
|
|
|
if (out_s_port)
|
|
*out_s_port = s_port;
|
|
return slave_type;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_connection_slave_type (NMConnection *self)
|
|
{
|
|
NMSettingConnection *s_con = nm_connection_get_setting_connection (self);
|
|
const char *slave_type, *port_type;
|
|
|
|
if (!s_con)
|
|
return FALSE;
|
|
if (!nm_setting_connection_get_master (s_con))
|
|
return FALSE;
|
|
|
|
slave_type = nm_setting_connection_get_slave_type (s_con);
|
|
if (slave_type) {
|
|
if ( _nm_setting_slave_type_is_valid (slave_type, &port_type)
|
|
&& port_type) {
|
|
NMSetting *s_port;
|
|
|
|
s_port = nm_connection_get_setting_by_name (self, port_type);
|
|
if (!s_port) {
|
|
GType p_type = nm_setting_lookup_type (port_type);
|
|
|
|
g_return_val_if_fail (p_type, FALSE);
|
|
nm_connection_add_setting (self, g_object_new (p_type, NULL));
|
|
return TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
if ((slave_type = _nm_connection_detect_slave_type (self, NULL))) {
|
|
g_object_set (s_con, NM_SETTING_CONNECTION_SLAVE_TYPE, slave_type, NULL);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_ethernet_link_neg (NMConnection *self)
|
|
{
|
|
NMSettingWired *s_wired = nm_connection_get_setting_wired (self);
|
|
|
|
if (s_wired) {
|
|
guint32 speed = nm_setting_wired_get_speed (s_wired);
|
|
const char *duplex = nm_setting_wired_get_duplex (s_wired);
|
|
|
|
if ( (speed && !duplex)
|
|
|| (!speed && duplex)) {
|
|
speed = 0;
|
|
duplex = NULL;
|
|
g_object_set (s_wired,
|
|
NM_SETTING_WIRED_SPEED, (guint) speed,
|
|
NM_SETTING_WIRED_DUPLEX, duplex,
|
|
NULL);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* _supports_addr_family:
|
|
* @self: a #NMConnection
|
|
* @family: AF_*
|
|
*
|
|
* Check whether the connection supports certain L3 address family,
|
|
* in order to be able to tell whether is should have the corresponding
|
|
* setting ("ipv4" for AF_INET and "ipv6" for AF_INET6).
|
|
*
|
|
* If AF_UNSPEC is given, then the function checks whether the connection
|
|
* supports any L3 configuration at all.
|
|
*
|
|
* Returns: %TRUE if the AF is supported, %FALSE otherwise
|
|
**/
|
|
static gboolean
|
|
_supports_addr_family (NMConnection *self, int family)
|
|
{
|
|
const char *connection_type = nm_connection_get_connection_type (self);
|
|
NMSettingConnection *s_con;
|
|
|
|
g_return_val_if_fail (connection_type, TRUE);
|
|
if (strcmp (connection_type, NM_SETTING_OVS_INTERFACE_SETTING_NAME) == 0)
|
|
return TRUE;
|
|
if (strcmp (connection_type, NM_SETTING_WPAN_SETTING_NAME) == 0)
|
|
return FALSE;
|
|
if (strcmp (connection_type, NM_SETTING_6LOWPAN_SETTING_NAME) == 0)
|
|
return family == AF_INET6 || family == AF_UNSPEC;
|
|
if ( (s_con = nm_connection_get_setting_connection (self))
|
|
&& (nm_streq0 (nm_setting_connection_get_slave_type (s_con), NM_SETTING_VRF_SETTING_NAME)))
|
|
return TRUE;
|
|
|
|
return !nm_setting_connection_get_master (nm_connection_get_setting_connection (self));
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_ip_config (NMConnection *self, GHashTable *parameters)
|
|
{
|
|
NMSettingIPConfig *s_ip4, *s_ip6;
|
|
NMSettingProxy *s_proxy;
|
|
NMSetting *setting;
|
|
gboolean changed = FALSE;
|
|
guint num, i;
|
|
|
|
s_ip4 = nm_connection_get_setting_ip4_config (self);
|
|
s_ip6 = nm_connection_get_setting_ip6_config (self);
|
|
s_proxy = nm_connection_get_setting_proxy (self);
|
|
|
|
if (_supports_addr_family (self, AF_INET)) {
|
|
|
|
if (!s_ip4) {
|
|
const char *default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_AUTO;
|
|
|
|
if (nm_connection_is_type (self, NM_SETTING_WIREGUARD_SETTING_NAME))
|
|
default_ip4_method = NM_SETTING_IP4_CONFIG_METHOD_DISABLED;
|
|
|
|
/* But if no IP4 setting was specified, assume the caller was just
|
|
* being lazy and use the default method.
|
|
*/
|
|
setting = nm_setting_ip4_config_new ();
|
|
|
|
g_object_set (setting,
|
|
NM_SETTING_IP_CONFIG_METHOD, default_ip4_method,
|
|
NULL);
|
|
nm_connection_add_setting (self, setting);
|
|
changed = TRUE;
|
|
} else {
|
|
if ( nm_setting_ip_config_get_gateway (s_ip4)
|
|
&& nm_setting_ip_config_get_never_default (s_ip4)) {
|
|
g_object_set (s_ip4, NM_SETTING_IP_CONFIG_GATEWAY, NULL, NULL);
|
|
changed = TRUE;
|
|
}
|
|
|
|
if ( nm_streq0 (nm_setting_ip_config_get_method (s_ip4),
|
|
NM_SETTING_IP4_CONFIG_METHOD_DISABLED)
|
|
&& !nm_setting_ip_config_get_may_fail (s_ip4)) {
|
|
g_object_set (s_ip4, NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, NULL);
|
|
changed = TRUE;
|
|
}
|
|
|
|
num = nm_setting_ip_config_get_num_addresses (s_ip4);
|
|
if ( num > 1
|
|
&& nm_streq0 (nm_setting_ip_config_get_method (s_ip4),
|
|
NM_SETTING_IP4_CONFIG_METHOD_SHARED)) {
|
|
for (i = num - 1; i > 0; i--)
|
|
nm_setting_ip_config_remove_address (s_ip4, i);
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
if (s_ip4) {
|
|
nm_connection_remove_setting (self, NM_TYPE_SETTING_IP4_CONFIG);
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
|
|
if (_supports_addr_family (self, AF_INET6)) {
|
|
if (!s_ip6) {
|
|
const char *default_ip6_method = NULL;
|
|
|
|
if (parameters)
|
|
default_ip6_method = g_hash_table_lookup (parameters, NM_CONNECTION_NORMALIZE_PARAM_IP6_CONFIG_METHOD);
|
|
if (!default_ip6_method) {
|
|
if (nm_connection_is_type (self, NM_SETTING_WIREGUARD_SETTING_NAME))
|
|
default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_IGNORE;
|
|
else
|
|
default_ip6_method = NM_SETTING_IP6_CONFIG_METHOD_AUTO;
|
|
}
|
|
|
|
/* If no IP6 setting was specified, then assume that means IP6 config is
|
|
* allowed to fail.
|
|
*/
|
|
setting = nm_setting_ip6_config_new ();
|
|
|
|
g_object_set (setting,
|
|
NM_SETTING_IP_CONFIG_METHOD, default_ip6_method,
|
|
NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE,
|
|
NULL);
|
|
nm_connection_add_setting (self, setting);
|
|
changed = TRUE;
|
|
} else {
|
|
const char *token;
|
|
|
|
token = nm_setting_ip6_config_get_token ((NMSettingIP6Config *) s_ip6);
|
|
if ( token
|
|
&& nm_setting_ip6_config_get_addr_gen_mode ((NMSettingIP6Config *) s_ip6) == NM_SETTING_IP6_CONFIG_ADDR_GEN_MODE_EUI64) {
|
|
struct in6_addr i6_token;
|
|
char normalized[NM_UTILS_INET_ADDRSTRLEN];
|
|
|
|
if ( inet_pton (AF_INET6, token, &i6_token) == 1
|
|
&& _nm_utils_inet6_is_token (&i6_token)) {
|
|
_nm_utils_inet6_ntop (&i6_token, normalized);
|
|
if (g_strcmp0 (token, normalized)) {
|
|
g_object_set (s_ip6, NM_SETTING_IP6_CONFIG_TOKEN, normalized, NULL);
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( nm_setting_ip_config_get_gateway (s_ip6)
|
|
&& nm_setting_ip_config_get_never_default (s_ip6)) {
|
|
g_object_set (s_ip6, NM_SETTING_IP_CONFIG_GATEWAY, NULL, NULL);
|
|
changed = TRUE;
|
|
}
|
|
|
|
if ( NM_IN_STRSET (nm_setting_ip_config_get_method (s_ip6),
|
|
NM_SETTING_IP6_CONFIG_METHOD_IGNORE,
|
|
NM_SETTING_IP6_CONFIG_METHOD_DISABLED)
|
|
&& !nm_setting_ip_config_get_may_fail (s_ip6)) {
|
|
g_object_set (s_ip6, NM_SETTING_IP_CONFIG_MAY_FAIL, TRUE, NULL);
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
} else {
|
|
if (s_ip6) {
|
|
nm_connection_remove_setting (self, NM_TYPE_SETTING_IP6_CONFIG);
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
|
|
if (_supports_addr_family (self, AF_UNSPEC)) {
|
|
if (!s_proxy) {
|
|
setting = nm_setting_proxy_new ();
|
|
nm_connection_add_setting (self, setting);
|
|
changed = TRUE;
|
|
}
|
|
} else {
|
|
if (s_proxy) {
|
|
nm_connection_remove_setting (self, NM_TYPE_SETTING_PROXY);
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_infiniband_mtu (NMConnection *self)
|
|
{
|
|
NMSettingInfiniband *s_infini = nm_connection_get_setting_infiniband (self);
|
|
|
|
if ( !s_infini
|
|
|| nm_setting_infiniband_get_mtu (s_infini) <= NM_INFINIBAND_MAX_MTU
|
|
|| !NM_IN_STRSET (nm_setting_infiniband_get_transport_mode (s_infini), "datagram",
|
|
"connected"))
|
|
return FALSE;
|
|
|
|
g_object_set (s_infini, NM_SETTING_INFINIBAND_MTU, (guint) NM_INFINIBAND_MAX_MTU, NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_bond_mode (NMConnection *self)
|
|
{
|
|
NMSettingBond *s_bond = nm_connection_get_setting_bond (self);
|
|
|
|
/* Convert mode from numeric to string notation */
|
|
if (s_bond) {
|
|
const char *mode = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE);
|
|
int mode_int = nm_utils_bond_mode_string_to_int (mode);
|
|
|
|
if (mode_int != -1) {
|
|
const char *mode_new = nm_utils_bond_mode_int_to_string (mode_int);
|
|
if (g_strcmp0 (mode_new, mode) != 0) {
|
|
nm_setting_bond_add_option (s_bond, NM_SETTING_BOND_OPTION_MODE, mode_new);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_bond_options (NMConnection *self)
|
|
{
|
|
NMSettingBond *s_bond = nm_connection_get_setting_bond (self);
|
|
gboolean changed = FALSE;
|
|
const char *name, *mode_str;
|
|
NMBondMode mode;
|
|
guint32 num, i;
|
|
|
|
/* Strip away unsupported options for current mode */
|
|
if (s_bond) {
|
|
mode_str = nm_setting_bond_get_option_by_name (s_bond, NM_SETTING_BOND_OPTION_MODE);
|
|
mode = _nm_setting_bond_mode_from_string (mode_str);
|
|
if (mode == NM_BOND_MODE_UNKNOWN)
|
|
return FALSE;
|
|
again:
|
|
num = nm_setting_bond_get_num_options (s_bond);
|
|
for (i = 0; i < num; i++) {
|
|
if ( nm_setting_bond_get_option (s_bond, i, &name, NULL)
|
|
&& !_nm_setting_bond_option_supported (name, mode)) {
|
|
nm_setting_bond_remove_option (s_bond, name);
|
|
changed = TRUE;
|
|
goto again;
|
|
}
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_wireless_mac_address_randomization (NMConnection *self)
|
|
{
|
|
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_macsec (NMConnection *self)
|
|
{
|
|
NMSettingMacsec *s_macsec = nm_connection_get_setting_macsec (self);
|
|
gboolean changed = FALSE;
|
|
|
|
if (!s_macsec)
|
|
return FALSE;
|
|
|
|
if (nm_setting_macsec_get_mode (s_macsec) != NM_SETTING_MACSEC_MODE_PSK) {
|
|
if (nm_setting_macsec_get_mka_cak (s_macsec)) {
|
|
g_object_set (s_macsec, NM_SETTING_MACSEC_MKA_CAK, NULL, NULL);
|
|
changed = TRUE;
|
|
}
|
|
if (nm_setting_macsec_get_mka_ckn (s_macsec)) {
|
|
g_object_set (s_macsec, NM_SETTING_MACSEC_MKA_CKN, NULL, NULL);
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
|
|
return changed;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_team_config (NMConnection *self)
|
|
{
|
|
NMSettingTeam *s_team = nm_connection_get_setting_team (self);
|
|
|
|
if (s_team) {
|
|
const char *config = nm_setting_team_get_config (s_team);
|
|
|
|
if (config && !*config) {
|
|
g_object_set (s_team, NM_SETTING_TEAM_CONFIG, NULL, NULL);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_team_port_config (NMConnection *self)
|
|
{
|
|
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 && !*config) {
|
|
g_object_set (s_team_port, NM_SETTING_TEAM_PORT_CONFIG, NULL, NULL);
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_bluetooth_type (NMConnection *self)
|
|
{
|
|
const char *type = _nm_connection_detect_bluetooth_type (self);
|
|
|
|
if (type) {
|
|
g_object_set (nm_connection_get_setting_bluetooth (self),
|
|
NM_SETTING_BLUETOOTH_TYPE, type,
|
|
NULL);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_ovs_interface_type (NMConnection *self)
|
|
{
|
|
NMSettingOvsInterface *s_ovs_interface = nm_connection_get_setting_ovs_interface (self);
|
|
gboolean modified;
|
|
int v;
|
|
|
|
if (!s_ovs_interface)
|
|
return FALSE;
|
|
|
|
v = _nm_setting_ovs_interface_verify_interface_type (s_ovs_interface,
|
|
self,
|
|
TRUE,
|
|
&modified,
|
|
NULL);
|
|
if (v != TRUE)
|
|
g_return_val_if_reached (modified);
|
|
|
|
return modified;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_ip_tunnel_wired_setting (NMConnection *self)
|
|
{
|
|
NMSettingIPTunnel *s_ip_tunnel;
|
|
|
|
s_ip_tunnel = nm_connection_get_setting_ip_tunnel (self);
|
|
if (!s_ip_tunnel)
|
|
return FALSE;
|
|
|
|
if ( nm_connection_get_setting_wired (self)
|
|
&& !NM_IN_SET (nm_setting_ip_tunnel_get_mode (s_ip_tunnel),
|
|
NM_IP_TUNNEL_MODE_GRETAP,
|
|
NM_IP_TUNNEL_MODE_IP6GRETAP)) {
|
|
nm_connection_remove_setting (self, NM_TYPE_SETTING_WIRED);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_sriov_vf_order (NMConnection *self)
|
|
{
|
|
NMSettingSriov *s_sriov;
|
|
|
|
s_sriov = NM_SETTING_SRIOV (nm_connection_get_setting (self, NM_TYPE_SETTING_SRIOV));
|
|
if (!s_sriov)
|
|
return FALSE;
|
|
|
|
return _nm_setting_sriov_sort_vfs (s_sriov);
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_bridge_vlan_order (NMConnection *self)
|
|
{
|
|
NMSettingBridge *s_bridge;
|
|
|
|
s_bridge = nm_connection_get_setting_bridge (self);
|
|
if (!s_bridge)
|
|
return FALSE;
|
|
|
|
return _nm_setting_bridge_sort_vlans (s_bridge);
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_bridge_port_vlan_order (NMConnection *self)
|
|
{
|
|
NMSettingBridgePort *s_port;
|
|
|
|
s_port = nm_connection_get_setting_bridge_port (self);
|
|
if (!s_port)
|
|
return FALSE;
|
|
|
|
return _nm_setting_bridge_port_sort_vlans (s_port);
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_gsm_auto_config (NMConnection *self)
|
|
{
|
|
NMSettingGsm *s_gsm;
|
|
|
|
s_gsm = nm_connection_get_setting_gsm (self);
|
|
if (!s_gsm)
|
|
return FALSE;
|
|
|
|
if (!nm_setting_gsm_get_auto_config (s_gsm))
|
|
return FALSE;
|
|
|
|
if ( !nm_setting_gsm_get_apn (s_gsm)
|
|
&& !nm_setting_gsm_get_username (s_gsm)
|
|
&& !nm_setting_gsm_get_password (s_gsm))
|
|
return FALSE;
|
|
|
|
g_object_set (s_gsm,
|
|
NM_SETTING_GSM_AUTO_CONFIG, FALSE,
|
|
NULL);
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_required_settings (NMConnection *self)
|
|
{
|
|
NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (self);
|
|
NMSetting *s_bridge;
|
|
gboolean changed = FALSE;
|
|
|
|
if (nm_connection_get_setting_vlan (self)) {
|
|
if (!nm_connection_get_setting_wired (self)) {
|
|
nm_connection_add_setting (self, nm_setting_wired_new ());
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
if (s_bt && nm_streq0 (nm_setting_bluetooth_get_connection_type (s_bt), NM_SETTING_BLUETOOTH_TYPE_NAP)) {
|
|
if (!nm_connection_get_setting_bridge (self)) {
|
|
s_bridge = nm_setting_bridge_new ();
|
|
g_object_set (s_bridge, NM_SETTING_BRIDGE_STP, FALSE, NULL);
|
|
nm_connection_add_setting (self, s_bridge);
|
|
changed = TRUE;
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
static gboolean
|
|
_normalize_invalid_slave_port_settings (NMConnection *self)
|
|
{
|
|
NMSettingConnection *s_con = nm_connection_get_setting_connection (self);
|
|
const char *slave_type;
|
|
gboolean changed = FALSE;
|
|
|
|
slave_type = nm_setting_connection_get_slave_type (s_con);
|
|
|
|
if ( !nm_streq0 (slave_type, NM_SETTING_BRIDGE_SETTING_NAME)
|
|
&& _nm_connection_remove_setting (self, NM_TYPE_SETTING_BRIDGE_PORT))
|
|
changed = TRUE;
|
|
|
|
if ( !nm_streq0 (slave_type, NM_SETTING_TEAM_SETTING_NAME)
|
|
&& _nm_connection_remove_setting (self, NM_TYPE_SETTING_TEAM_PORT))
|
|
changed = TRUE;
|
|
|
|
return changed;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_verify:
|
|
* @connection: the #NMConnection to verify
|
|
* @error: location to store error, or %NULL
|
|
*
|
|
* Validates the connection and all its settings. Each setting's properties
|
|
* have allowed values, and some values are dependent on other values. For
|
|
* example, if a Wi-Fi connection is security enabled, the #NMSettingWireless
|
|
* setting object's 'security' property must contain the setting name of the
|
|
* #NMSettingWirelessSecurity object, which must also be present in the
|
|
* connection for the connection to be valid. As another example, the
|
|
* #NMSettingWired object's 'mac-address' property must be a validly formatted
|
|
* MAC address. The returned #GError contains information about which
|
|
* setting and which property failed validation, and how it failed validation.
|
|
*
|
|
* Returns: %TRUE if the connection is valid, %FALSE if it is not
|
|
**/
|
|
gboolean
|
|
nm_connection_verify (NMConnection *connection, GError **error)
|
|
{
|
|
NMSettingVerifyResult result;
|
|
|
|
result = _nm_connection_verify (connection, error);
|
|
|
|
/* we treat normalizable connections as valid. */
|
|
if (result == NM_SETTING_VERIFY_NORMALIZABLE)
|
|
g_clear_error (error);
|
|
|
|
return result == NM_SETTING_VERIFY_SUCCESS || result == NM_SETTING_VERIFY_NORMALIZABLE;
|
|
}
|
|
|
|
NMSettingVerifyResult
|
|
_nm_connection_verify (NMConnection *connection, GError **error)
|
|
{
|
|
NMConnectionPrivate *priv;
|
|
NMSettingConnection *s_con;
|
|
NMSettingIPConfig *s_ip4, *s_ip6;
|
|
NMSettingProxy *s_proxy;
|
|
GHashTableIter iter;
|
|
gpointer value;
|
|
GSList *all_settings = NULL, *setting_i;
|
|
gs_free_error GError *normalizable_error = NULL;
|
|
NMSettingVerifyResult normalizable_error_type = NM_SETTING_VERIFY_SUCCESS;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), NM_SETTING_VERIFY_ERROR);
|
|
g_return_val_if_fail (!error || !*error, NM_SETTING_VERIFY_ERROR);
|
|
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
/* First, make sure there's at least 'connection' setting */
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
if (!s_con) {
|
|
g_set_error_literal (error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_MISSING_SETTING,
|
|
_("setting not found"));
|
|
g_prefix_error (error, "%s: ", NM_SETTING_CONNECTION_SETTING_NAME);
|
|
return NM_SETTING_VERIFY_ERROR;
|
|
}
|
|
|
|
/* Build up the list of settings */
|
|
g_hash_table_iter_init (&iter, priv->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, &value)) {
|
|
/* Order NMSettingConnection so that it will be verified first.
|
|
* The reason is, that errors in this setting might be more fundamental
|
|
* and should be checked and reported with higher priority.
|
|
*/
|
|
if (value == s_con)
|
|
all_settings = g_slist_append (all_settings, value);
|
|
else
|
|
all_settings = g_slist_prepend (all_settings, value);
|
|
}
|
|
all_settings = g_slist_reverse (all_settings);
|
|
|
|
/* Now, run the verify function of each setting */
|
|
for (setting_i = all_settings; setting_i; setting_i = setting_i->next) {
|
|
GError *verify_error = NULL;
|
|
NMSettingVerifyResult verify_result;
|
|
|
|
/* verify all settings. We stop if we find the first non-normalizable
|
|
* @NM_SETTING_VERIFY_ERROR. If we find normalizable errors we continue
|
|
* but remember the error to return it to the user.
|
|
* @NM_SETTING_VERIFY_NORMALIZABLE_ERROR has a higher priority then
|
|
* @NM_SETTING_VERIFY_NORMALIZABLE, so, if we encounter such an error type,
|
|
* we remember it instead (to return it as output).
|
|
**/
|
|
verify_result = _nm_setting_verify (NM_SETTING (setting_i->data), connection, &verify_error);
|
|
if (verify_result == NM_SETTING_VERIFY_NORMALIZABLE ||
|
|
verify_result == NM_SETTING_VERIFY_NORMALIZABLE_ERROR) {
|
|
if ( verify_result == NM_SETTING_VERIFY_NORMALIZABLE_ERROR
|
|
&& normalizable_error_type == NM_SETTING_VERIFY_NORMALIZABLE) {
|
|
/* NORMALIZABLE_ERROR has higher priority. */
|
|
g_clear_error (&normalizable_error);
|
|
}
|
|
if (!normalizable_error) {
|
|
g_propagate_error (&normalizable_error, verify_error);
|
|
verify_error = NULL;
|
|
normalizable_error_type = verify_result;
|
|
}
|
|
} else if (verify_result != NM_SETTING_VERIFY_SUCCESS) {
|
|
g_propagate_error (error, verify_error);
|
|
g_slist_free (all_settings);
|
|
g_return_val_if_fail (verify_result == NM_SETTING_VERIFY_ERROR, NM_SETTING_VERIFY_ERROR);
|
|
return NM_SETTING_VERIFY_ERROR;
|
|
}
|
|
g_clear_error (&verify_error);
|
|
}
|
|
g_slist_free (all_settings);
|
|
|
|
s_ip4 = nm_connection_get_setting_ip4_config (connection);
|
|
s_ip6 = nm_connection_get_setting_ip6_config (connection);
|
|
s_proxy = nm_connection_get_setting_proxy (connection);
|
|
|
|
nm_assert (normalizable_error_type != NM_SETTING_VERIFY_ERROR);
|
|
if (NM_IN_SET (normalizable_error_type, NM_SETTING_VERIFY_SUCCESS,
|
|
NM_SETTING_VERIFY_NORMALIZABLE)) {
|
|
if (_supports_addr_family (connection, AF_INET)) {
|
|
if (!s_ip4 && normalizable_error_type == NM_SETTING_VERIFY_SUCCESS) {
|
|
g_set_error_literal (&normalizable_error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_MISSING_SETTING,
|
|
_("setting is required for non-slave connections"));
|
|
g_prefix_error (&normalizable_error, "%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME);
|
|
|
|
/* having a master without IP config was not a verify() error, accept
|
|
* it for backward compatibility. */
|
|
normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE;
|
|
}
|
|
} else {
|
|
if (s_ip4) {
|
|
g_clear_error (&normalizable_error);
|
|
g_set_error_literal (&normalizable_error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("setting not allowed in slave connection"));
|
|
g_prefix_error (&normalizable_error, "%s: ", NM_SETTING_IP4_CONFIG_SETTING_NAME);
|
|
/* having a slave with IP config *was* and is a verify() error. */
|
|
normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
|
|
}
|
|
}
|
|
|
|
if (_supports_addr_family (connection, AF_INET6)) {
|
|
if (!s_ip6 && normalizable_error_type == NM_SETTING_VERIFY_SUCCESS) {
|
|
g_set_error_literal (&normalizable_error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_MISSING_SETTING,
|
|
_("setting is required for non-slave connections"));
|
|
g_prefix_error (&normalizable_error, "%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME);
|
|
|
|
/* having a master without IP config was not a verify() error, accept
|
|
* it for backward compatibility. */
|
|
normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE;
|
|
}
|
|
} else {
|
|
if (s_ip6) {
|
|
g_clear_error (&normalizable_error);
|
|
g_set_error_literal (&normalizable_error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("setting not allowed in slave connection"));
|
|
g_prefix_error (&normalizable_error, "%s: ", NM_SETTING_IP6_CONFIG_SETTING_NAME);
|
|
/* having a slave with IP config *was* and is a verify() error. */
|
|
normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
|
|
}
|
|
}
|
|
|
|
if (_supports_addr_family (connection, AF_UNSPEC)) {
|
|
if (!s_proxy && normalizable_error_type == NM_SETTING_VERIFY_SUCCESS) {
|
|
g_set_error_literal (&normalizable_error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_MISSING_SETTING,
|
|
_("setting is required for non-slave connections"));
|
|
g_prefix_error (&normalizable_error, "%s: ", NM_SETTING_PROXY_SETTING_NAME);
|
|
|
|
/* having a master without proxy config was not a verify() error, accept
|
|
* it for backward compatibility. */
|
|
normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE;
|
|
}
|
|
} else {
|
|
if (s_proxy) {
|
|
g_clear_error (&normalizable_error);
|
|
g_set_error_literal (&normalizable_error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_INVALID_SETTING,
|
|
_("setting not allowed in slave connection"));
|
|
g_prefix_error (&normalizable_error, "%s: ", NM_SETTING_PROXY_SETTING_NAME);
|
|
/* having a slave with proxy config *was* and is a verify() error. */
|
|
normalizable_error_type = NM_SETTING_VERIFY_NORMALIZABLE_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (normalizable_error_type != NM_SETTING_VERIFY_SUCCESS) {
|
|
g_propagate_error (error, normalizable_error);
|
|
normalizable_error = NULL;
|
|
return normalizable_error_type;
|
|
}
|
|
|
|
return NM_SETTING_VERIFY_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_verify_secrets:
|
|
* @connection: the #NMConnection to verify in
|
|
* @error: location to store error, or %NULL
|
|
*
|
|
* Verifies the secrets in the connection.
|
|
*
|
|
* Returns: %TRUE if the secrets are valid, %FALSE if they are not
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
gboolean
|
|
nm_connection_verify_secrets (NMConnection *connection, GError **error)
|
|
{
|
|
GHashTableIter iter;
|
|
NMSetting *setting;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
|
|
g_return_val_if_fail (!error || !*error, FALSE);
|
|
|
|
g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
|
|
if (!nm_setting_verify_secrets (setting, connection, error))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
_connection_normalize (NMConnection *connection,
|
|
GHashTable *parameters,
|
|
gboolean *modified,
|
|
GError **error)
|
|
{
|
|
NMSettingVerifyResult success;
|
|
gboolean was_modified;
|
|
|
|
#if NM_MORE_ASSERTS > 10
|
|
/* only call this _nm_connection_verify() confirms that the connection
|
|
* requires normalization and is normalizable. */
|
|
nm_assert (NM_IN_SET (_nm_connection_verify (connection, NULL),
|
|
NM_SETTING_VERIFY_NORMALIZABLE,
|
|
NM_SETTING_VERIFY_NORMALIZABLE_ERROR));
|
|
#endif
|
|
|
|
/* Try to perform all kind of normalizations on the settings to fix it.
|
|
* We only do this, after verifying that the connection contains no un-normalizable
|
|
* errors, because in that case we rather fail without touching the settings. */
|
|
|
|
was_modified = FALSE;
|
|
|
|
was_modified |= _normalize_connection_uuid (connection);
|
|
was_modified |= _normalize_connection_type (connection);
|
|
was_modified |= _normalize_connection_slave_type (connection);
|
|
was_modified |= _normalize_required_settings (connection);
|
|
was_modified |= _normalize_invalid_slave_port_settings (connection);
|
|
was_modified |= _normalize_ip_config (connection, parameters);
|
|
was_modified |= _normalize_ethernet_link_neg (connection);
|
|
was_modified |= _normalize_infiniband_mtu (connection);
|
|
was_modified |= _normalize_bond_mode (connection);
|
|
was_modified |= _normalize_bond_options (connection);
|
|
was_modified |= _normalize_wireless_mac_address_randomization (connection);
|
|
was_modified |= _normalize_macsec (connection);
|
|
was_modified |= _normalize_team_config (connection);
|
|
was_modified |= _normalize_team_port_config (connection);
|
|
was_modified |= _normalize_bluetooth_type (connection);
|
|
was_modified |= _normalize_ovs_interface_type (connection);
|
|
was_modified |= _normalize_ip_tunnel_wired_setting (connection);
|
|
was_modified |= _normalize_sriov_vf_order (connection);
|
|
was_modified |= _normalize_bridge_vlan_order (connection);
|
|
was_modified |= _normalize_bridge_port_vlan_order (connection);
|
|
was_modified |= _normalize_gsm_auto_config (connection);
|
|
|
|
was_modified = !!was_modified;
|
|
|
|
/* Verify anew */
|
|
success = _nm_connection_verify (connection, error);
|
|
|
|
NM_SET_OUT (modified, was_modified);
|
|
|
|
if (success != NM_SETTING_VERIFY_SUCCESS) {
|
|
/* we would expect, that after normalization, the connection can be verified.
|
|
* Also treat NM_SETTING_VERIFY_NORMALIZABLE as failure, because there is something
|
|
* odd going on. */
|
|
if (error && !*error) {
|
|
g_set_error_literal (error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_FAILED,
|
|
_("Unexpected failure to normalize the connection"));
|
|
}
|
|
g_warning ("connection did not verify after normalization: %s", error ? (*error)->message : "??");
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
/* we would expect, that the connection was modified during normalization. */
|
|
g_return_val_if_fail (was_modified, TRUE);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_normalize:
|
|
* @connection: the #NMConnection to normalize
|
|
* @parameters: (allow-none) (element-type utf8 gpointer): a #GHashTable with
|
|
* normalization parameters to allow customization of the normalization by providing
|
|
* specific arguments. Unknown arguments will be ignored and the default will be
|
|
* used. The keys must be strings compared with g_str_equal() function.
|
|
* The values are opaque and depend on the parameter name.
|
|
* @modified: (out) (allow-none): outputs whether any settings were modified.
|
|
* @error: location to store error, or %NULL. Contains the reason,
|
|
* why the connection is invalid, if the function returns an error.
|
|
*
|
|
* Does some basic normalization and fixup of well known inconsistencies
|
|
* and deprecated fields. If the connection was modified in any way,
|
|
* the output parameter @modified is set %TRUE.
|
|
*
|
|
* Finally the connection will be verified and %TRUE returns if the connection
|
|
* is valid. As this function only performs some specific normalization steps
|
|
* it cannot repair all connections. If the connection has errors that
|
|
* cannot be normalized, the connection will not be modified.
|
|
*
|
|
* Returns: %TRUE if the connection is valid, %FALSE if it is not
|
|
**/
|
|
gboolean
|
|
nm_connection_normalize (NMConnection *connection,
|
|
GHashTable *parameters,
|
|
gboolean *modified,
|
|
GError **error)
|
|
{
|
|
NMSettingVerifyResult success;
|
|
gs_free_error GError *normalizable_error = NULL;
|
|
|
|
success = _nm_connection_verify (connection, &normalizable_error);
|
|
|
|
if (!NM_IN_SET (success,
|
|
NM_SETTING_VERIFY_NORMALIZABLE,
|
|
NM_SETTING_VERIFY_NORMALIZABLE_ERROR)) {
|
|
if (normalizable_error) {
|
|
nm_assert (success == NM_SETTING_VERIFY_ERROR);
|
|
g_propagate_error (error, g_steal_pointer (&normalizable_error));
|
|
} else
|
|
nm_assert (success == NM_SETTING_VERIFY_SUCCESS);
|
|
|
|
NM_SET_OUT (modified, FALSE);
|
|
|
|
if (success != NM_SETTING_VERIFY_SUCCESS) {
|
|
if ( error
|
|
&& !*error) {
|
|
g_set_error_literal (error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_FAILED,
|
|
_("Unexpected failure to verify the connection"));
|
|
return FALSE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
if (error && *error)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
return _connection_normalize (connection, parameters, modified, error);
|
|
}
|
|
|
|
gboolean
|
|
_nm_connection_ensure_normalized (NMConnection *connection,
|
|
gboolean allow_modify,
|
|
const char *expected_uuid,
|
|
gboolean coerce_uuid,
|
|
NMConnection **out_connection_clone,
|
|
GError **error)
|
|
{
|
|
gs_unref_object NMConnection *connection_clone = NULL;
|
|
gs_free_error GError *local = NULL;
|
|
NMSettingVerifyResult vresult;
|
|
|
|
nm_assert (NM_IS_CONNECTION (connection));
|
|
nm_assert (!out_connection_clone || !*out_connection_clone);
|
|
nm_assert (!expected_uuid || nm_utils_is_uuid (expected_uuid));
|
|
|
|
if (expected_uuid) {
|
|
if (nm_streq0 (expected_uuid, nm_connection_get_uuid (connection)))
|
|
expected_uuid = NULL;
|
|
else if ( !coerce_uuid
|
|
|| (!allow_modify && !out_connection_clone)) {
|
|
g_set_error (error, NM_CONNECTION_ERROR, NM_CONNECTION_ERROR_INVALID_PROPERTY,
|
|
_("unexpected uuid %s instead of %s"),
|
|
nm_connection_get_uuid (connection),
|
|
expected_uuid);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
vresult = _nm_connection_verify (connection, &local);
|
|
if (vresult != NM_SETTING_VERIFY_SUCCESS) {
|
|
if (!NM_IN_SET (vresult, NM_SETTING_VERIFY_NORMALIZABLE,
|
|
NM_SETTING_VERIFY_NORMALIZABLE_ERROR)) {
|
|
g_propagate_error (error, g_steal_pointer (&local));
|
|
return FALSE;
|
|
}
|
|
if (!allow_modify) {
|
|
if (!out_connection_clone) {
|
|
/* even NM_SETTING_VERIFY_NORMALIZABLE is treated as an error. We could normalize,
|
|
* but are not allowed to (and no out argument is provided for cloning). */
|
|
g_propagate_error (error, g_steal_pointer (&local));
|
|
return FALSE;
|
|
}
|
|
connection_clone = nm_simple_connection_new_clone (connection);
|
|
connection = connection_clone;
|
|
}
|
|
if (!_connection_normalize (connection, NULL, NULL, error))
|
|
g_return_val_if_reached (FALSE);
|
|
}
|
|
|
|
if (expected_uuid) {
|
|
NMSettingConnection *s_con;
|
|
|
|
if ( !allow_modify
|
|
&& !connection_clone) {
|
|
nm_assert (out_connection_clone);
|
|
connection_clone = nm_simple_connection_new_clone (connection);
|
|
connection = connection_clone;
|
|
}
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
g_object_set (s_con,
|
|
NM_SETTING_CONNECTION_UUID,
|
|
expected_uuid,
|
|
NULL);
|
|
}
|
|
|
|
NM_SET_OUT (out_connection_clone, g_steal_pointer (&connection_clone));
|
|
return TRUE;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
#if NM_MORE_ASSERTS
|
|
static void
|
|
_nmtst_connection_unchanging_changed_cb (NMConnection *connection, gpointer user_data)
|
|
{
|
|
nm_assert_not_reached ();
|
|
}
|
|
|
|
static void
|
|
_nmtst_connection_unchanging_secrets_updated_cb (NMConnection *connection, const char *setting_name, gpointer user_data)
|
|
{
|
|
nm_assert_not_reached ();
|
|
}
|
|
|
|
const char _nmtst_connection_unchanging_user_data = 0;
|
|
|
|
void
|
|
nmtst_connection_assert_unchanging (NMConnection *connection)
|
|
{
|
|
if (!connection)
|
|
return;
|
|
|
|
nm_assert (NM_IS_CONNECTION (connection));
|
|
|
|
if (g_signal_handler_find (connection,
|
|
G_SIGNAL_MATCH_DATA,
|
|
0,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
(gpointer) &_nmtst_connection_unchanging_user_data) != 0) {
|
|
/* avoid connecting the assertion handler multiple times. */
|
|
return;
|
|
}
|
|
|
|
g_signal_connect (connection,
|
|
NM_CONNECTION_CHANGED,
|
|
G_CALLBACK (_nmtst_connection_unchanging_changed_cb),
|
|
(gpointer) &_nmtst_connection_unchanging_user_data);
|
|
g_signal_connect (connection,
|
|
NM_CONNECTION_SECRETS_CLEARED,
|
|
G_CALLBACK (_nmtst_connection_unchanging_changed_cb),
|
|
(gpointer) &_nmtst_connection_unchanging_user_data);
|
|
g_signal_connect (connection,
|
|
NM_CONNECTION_SECRETS_UPDATED,
|
|
G_CALLBACK (_nmtst_connection_unchanging_secrets_updated_cb),
|
|
(gpointer) &_nmtst_connection_unchanging_user_data);
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_connection_update_secrets:
|
|
* @connection: the #NMConnection
|
|
* @setting_name: the setting object name to which the secrets apply
|
|
* @secrets: a #GVariant of secrets, of type %NM_VARIANT_TYPE_CONNECTION
|
|
* or %NM_VARIANT_TYPE_SETTING
|
|
* @error: location to store error, or %NULL
|
|
*
|
|
* Update the specified setting's secrets, given a dictionary of secrets
|
|
* intended for that setting (deserialized from D-Bus for example). Will also
|
|
* extract the given setting's secrets hash if given a connection dictionary.
|
|
* If @setting_name is %NULL, expects a fully serialized #NMConnection as
|
|
* returned by nm_connection_to_dbus() and will update all secrets from all
|
|
* settings contained in @secrets.
|
|
*
|
|
* Returns: %TRUE if the secrets were successfully updated, %FALSE if the update
|
|
* failed (tried to update secrets for a setting that doesn't exist, etc)
|
|
**/
|
|
gboolean
|
|
nm_connection_update_secrets (NMConnection *connection,
|
|
const char *setting_name,
|
|
GVariant *secrets,
|
|
GError **error)
|
|
{
|
|
NMSetting *setting;
|
|
gboolean success = TRUE;
|
|
gboolean updated = FALSE;
|
|
GVariant *setting_dict = NULL;
|
|
GVariantIter iter;
|
|
const char *key;
|
|
gboolean full_connection;
|
|
int success_detail;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
|
|
|
|
full_connection = g_variant_is_of_type (secrets, NM_VARIANT_TYPE_CONNECTION);
|
|
|
|
g_return_val_if_fail ( full_connection
|
|
|| g_variant_is_of_type (secrets, NM_VARIANT_TYPE_SETTING), FALSE);
|
|
g_return_val_if_fail (!error || !*error, FALSE);
|
|
g_return_val_if_fail (setting_name || full_connection, FALSE);
|
|
|
|
/* Empty @secrets means success */
|
|
if (g_variant_n_children (secrets) == 0)
|
|
return TRUE;
|
|
|
|
if (setting_name) {
|
|
/* Update just one setting's secrets */
|
|
setting = nm_connection_get_setting_by_name (connection, setting_name);
|
|
if (!setting) {
|
|
g_set_error_literal (error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
|
|
setting_name);
|
|
return FALSE;
|
|
}
|
|
|
|
if (full_connection) {
|
|
setting_dict = g_variant_lookup_value (secrets, setting_name, NM_VARIANT_TYPE_SETTING);
|
|
if (!setting_dict) {
|
|
/* The connection dictionary didn't contain any secrets for
|
|
* @setting_name; just return success.
|
|
*/
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
|
|
success_detail = _nm_setting_update_secrets (setting,
|
|
setting_dict ?: secrets,
|
|
error);
|
|
g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
|
|
|
|
g_clear_pointer (&setting_dict, g_variant_unref);
|
|
|
|
if (success_detail == NM_SETTING_UPDATE_SECRET_ERROR) {
|
|
nm_assert (!error || *error);
|
|
return FALSE;
|
|
}
|
|
if (success_detail == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
|
|
updated = TRUE;
|
|
} else {
|
|
/* check first, whether all the settings exist... */
|
|
g_variant_iter_init (&iter, secrets);
|
|
while (g_variant_iter_next (&iter, "{&s@a{sv}}", &key, NULL)) {
|
|
setting = nm_connection_get_setting_by_name (connection, key);
|
|
if (!setting) {
|
|
g_set_error_literal (error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_SETTING_NOT_FOUND,
|
|
key);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Update each setting with any secrets from the connection dictionary */
|
|
g_variant_iter_init (&iter, secrets);
|
|
while (g_variant_iter_next (&iter, "{&s@a{sv}}", &key, &setting_dict)) {
|
|
gs_free_error GError *local = NULL;
|
|
|
|
/* Update the secrets for this setting */
|
|
setting = nm_connection_get_setting_by_name (connection, key);
|
|
|
|
g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
|
|
success_detail = _nm_setting_update_secrets (setting, setting_dict, error ? &local : NULL);
|
|
g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
|
|
|
|
g_variant_unref (setting_dict);
|
|
|
|
if (success_detail == NM_SETTING_UPDATE_SECRET_ERROR) {
|
|
if (success) {
|
|
if (error) {
|
|
nm_assert (local);
|
|
g_propagate_error (error, g_steal_pointer (&local));
|
|
error = NULL;
|
|
} else
|
|
nm_assert (!local);
|
|
success = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
if (success_detail == NM_SETTING_UPDATE_SECRET_SUCCESS_MODIFIED)
|
|
updated = TRUE;
|
|
}
|
|
}
|
|
|
|
if (updated)
|
|
g_signal_emit (connection, signals[SECRETS_UPDATED], 0, setting_name);
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_need_secrets:
|
|
* @connection: the #NMConnection
|
|
* @hints: (out) (element-type utf8) (allow-none) (transfer container):
|
|
* the address of a pointer to a #GPtrArray, initialized to %NULL, which on
|
|
* return points to an allocated #GPtrArray containing the property names of
|
|
* secrets of the #NMSetting which may be required; the caller owns the array
|
|
* and must free the array itself with g_ptr_array_free(), but not free its
|
|
* elements
|
|
*
|
|
* Returns the name of the first setting object in the connection which would
|
|
* need secrets to make a successful connection. The returned hints are only
|
|
* intended as a guide to what secrets may be required, because in some
|
|
* circumstances, there is no way to conclusively determine exactly which
|
|
* secrets are needed.
|
|
*
|
|
* Returns: the setting name of the #NMSetting object which has invalid or
|
|
* missing secrets
|
|
**/
|
|
const char *
|
|
nm_connection_need_secrets (NMConnection *connection,
|
|
GPtrArray **hints)
|
|
{
|
|
NMConnectionPrivate *priv;
|
|
GHashTableIter hiter;
|
|
GSList *settings = NULL;
|
|
GSList *iter;
|
|
const char *name = NULL;
|
|
NMSetting *setting;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
|
|
if (hints)
|
|
g_return_val_if_fail (*hints == NULL, NULL);
|
|
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
/* Get list of settings in priority order */
|
|
g_hash_table_iter_init (&hiter, priv->settings);
|
|
while (g_hash_table_iter_next (&hiter, NULL, (gpointer) &setting))
|
|
settings = g_slist_insert_sorted (settings, setting, _nm_setting_compare_priority);
|
|
|
|
for (iter = settings; iter; iter = g_slist_next (iter)) {
|
|
GPtrArray *secrets;
|
|
|
|
setting = NM_SETTING (iter->data);
|
|
secrets = _nm_setting_need_secrets (setting);
|
|
if (secrets) {
|
|
if (hints)
|
|
*hints = secrets;
|
|
else
|
|
g_ptr_array_free (secrets, TRUE);
|
|
|
|
name = nm_setting_get_name (setting);
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_slist_free (settings);
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_clear_secrets:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* Clears and frees any secrets that may be stored in the connection, to avoid
|
|
* keeping secret data in memory when not needed.
|
|
**/
|
|
void
|
|
nm_connection_clear_secrets (NMConnection *connection)
|
|
{
|
|
return nm_connection_clear_secrets_with_flags (connection, NULL, NULL);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_clear_secrets_with_flags:
|
|
* @connection: the #NMConnection
|
|
* @func: (scope call) (allow-none): function to be called to determine whether a
|
|
* specific secret should be cleared or not. If %NULL, all secrets are cleared.
|
|
* @user_data: caller-supplied data passed to @func
|
|
*
|
|
* Clears and frees secrets determined by @func.
|
|
**/
|
|
void
|
|
nm_connection_clear_secrets_with_flags (NMConnection *connection,
|
|
NMSettingClearSecretsWithFlagsFn func,
|
|
gpointer user_data)
|
|
{
|
|
GHashTableIter iter;
|
|
NMSetting *setting;
|
|
|
|
g_return_if_fail (NM_IS_CONNECTION (connection));
|
|
|
|
g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
|
|
g_signal_handlers_block_by_func (setting, (GCallback) setting_changed_cb, connection);
|
|
_nm_setting_clear_secrets (setting, func, user_data);
|
|
g_signal_handlers_unblock_by_func (setting, (GCallback) setting_changed_cb, connection);
|
|
}
|
|
|
|
g_signal_emit (connection, signals[SECRETS_CLEARED], 0);
|
|
}
|
|
|
|
static gboolean
|
|
_clear_secrets_by_secret_flags_cb (NMSetting *setting,
|
|
const char *secret,
|
|
NMSettingSecretFlags flags,
|
|
gpointer user_data)
|
|
{
|
|
NMSettingSecretFlags filter_flags = GPOINTER_TO_UINT (user_data);
|
|
gboolean remove_secret;
|
|
|
|
if (filter_flags == NM_SETTING_SECRET_FLAG_NONE) {
|
|
/* Can't use bitops with SECRET_FLAG_NONE so handle that specifically */
|
|
remove_secret = (flags != NM_SETTING_SECRET_FLAG_NONE);
|
|
} else {
|
|
/* Otherwise if the secret has at least one of the desired flags keep it */
|
|
remove_secret = !NM_FLAGS_ANY (flags, filter_flags);
|
|
}
|
|
|
|
return remove_secret;
|
|
}
|
|
|
|
/**
|
|
* _nm_connection_clear_secrets_by_secret_flags:
|
|
* @self: the #NMConnection to filter (will be modified)
|
|
* @filter_flags: the secret flags to control whether to drop/remove
|
|
* a secret or to keep it. The meaning of the filter flags is to
|
|
* preseve the secrets. The secrets that have matching (see below)
|
|
* flags are kept, the others are dropped.
|
|
*
|
|
* Removes/drops secrets from @self according to @filter_flags.
|
|
* If @filter_flags is %NM_SETTING_SECRET_NONE, then only secrets that
|
|
* have %NM_SETTING_SECRET_NONE flags are kept.
|
|
* Otherwise, only secrets with secret flags are kept that have at least
|
|
* one of the filter flags.
|
|
*/
|
|
void
|
|
_nm_connection_clear_secrets_by_secret_flags (NMConnection *self,
|
|
NMSettingSecretFlags filter_flags)
|
|
{
|
|
nm_connection_clear_secrets_with_flags (self,
|
|
_clear_secrets_by_secret_flags_cb,
|
|
GUINT_TO_POINTER (filter_flags));
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
/* Returns always a non-NULL, floating variant that must
|
|
* be unrefed by the caller. */
|
|
GVariant *
|
|
_nm_connection_for_each_secret (NMConnection *self,
|
|
GVariant *secrets,
|
|
gboolean remove_non_secrets,
|
|
_NMConnectionForEachSecretFunc callback,
|
|
gpointer callback_data)
|
|
{
|
|
GVariantBuilder secrets_builder;
|
|
GVariantBuilder setting_builder;
|
|
GVariantIter secrets_iter;
|
|
GVariantIter *setting_iter;
|
|
const char *setting_name;
|
|
|
|
/* This function, given a dict of dicts representing new secrets of
|
|
* an NMConnection, walks through each toplevel dict (which represents a
|
|
* NMSetting), and for each setting, walks through that setting dict's
|
|
* properties. For each property that's a secret, it will check that
|
|
* secret's flags in the backing NMConnection object, and call a supplied
|
|
* callback.
|
|
*
|
|
* The one complexity is that the VPN setting's 'secrets' property is
|
|
* *also* a dict (since the key/value pairs are arbitrary and known
|
|
* only to the VPN plugin itself). That means we have three levels of
|
|
* dicts that we potentially have to traverse here. The differences
|
|
* are handled by the virtual for_each_secret() function.
|
|
*/
|
|
|
|
g_return_val_if_fail (callback, NULL);
|
|
|
|
g_variant_iter_init (&secrets_iter, secrets);
|
|
g_variant_builder_init (&secrets_builder, NM_VARIANT_TYPE_CONNECTION);
|
|
while (g_variant_iter_next (&secrets_iter, "{&sa{sv}}", &setting_name, &setting_iter)) {
|
|
_nm_unused nm_auto_free_variant_iter GVariantIter *setting_iter_free = setting_iter;
|
|
NMSetting *setting;
|
|
const char *secret_name;
|
|
GVariant *val;
|
|
|
|
setting = nm_connection_get_setting_by_name (self, setting_name);
|
|
if (!setting)
|
|
continue;
|
|
|
|
g_variant_builder_init (&setting_builder, NM_VARIANT_TYPE_SETTING);
|
|
while (g_variant_iter_next (setting_iter, "{&sv}", &secret_name, &val)) {
|
|
_nm_unused gs_unref_variant GVariant *val_free = val;
|
|
|
|
NM_SETTING_GET_CLASS (setting)->for_each_secret (setting,
|
|
secret_name,
|
|
val,
|
|
remove_non_secrets,
|
|
callback,
|
|
callback_data,
|
|
&setting_builder);
|
|
}
|
|
|
|
g_variant_builder_add (&secrets_builder, "{sa{sv}}", setting_name, &setting_builder);
|
|
}
|
|
|
|
return g_variant_builder_end (&secrets_builder);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
typedef struct {
|
|
NMConnectionFindSecretFunc find_func;
|
|
gpointer find_func_data;
|
|
gboolean found;
|
|
} FindSecretData;
|
|
|
|
static gboolean
|
|
find_secret_for_each_func (NMSettingSecretFlags flags,
|
|
gpointer user_data)
|
|
{
|
|
FindSecretData *data = user_data;
|
|
|
|
if (!data->found)
|
|
data->found = data->find_func (flags, data->find_func_data);
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
_nm_connection_find_secret (NMConnection *self,
|
|
GVariant *secrets,
|
|
NMConnectionFindSecretFunc callback,
|
|
gpointer callback_data)
|
|
{
|
|
gs_unref_variant GVariant *dummy = NULL;
|
|
FindSecretData data = {
|
|
.find_func = callback,
|
|
.find_func_data = callback_data,
|
|
.found = FALSE,
|
|
};
|
|
|
|
dummy = _nm_connection_for_each_secret (self, secrets, FALSE, find_secret_for_each_func, &data);
|
|
return data.found;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_connection_to_dbus:
|
|
* @connection: the #NMConnection
|
|
* @flags: serialization flags, e.g. %NM_CONNECTION_SERIALIZE_ALL
|
|
*
|
|
* Converts the #NMConnection into a #GVariant of type
|
|
* %NM_VARIANT_TYPE_CONNECTION describing the connection, suitable for
|
|
* marshalling over D-Bus or otherwise serializing.
|
|
*
|
|
* Returns: (transfer none): a new floating #GVariant describing the connection,
|
|
* its settings, and each setting's properties.
|
|
**/
|
|
GVariant *
|
|
nm_connection_to_dbus (NMConnection *connection,
|
|
NMConnectionSerializationFlags flags)
|
|
{
|
|
return nm_connection_to_dbus_full (connection, flags, NULL);
|
|
}
|
|
|
|
GVariant *
|
|
nm_connection_to_dbus_full (NMConnection *connection,
|
|
NMConnectionSerializationFlags flags,
|
|
const NMConnectionSerializationOptions *options)
|
|
{
|
|
GVariantBuilder builder;
|
|
gboolean any = FALSE;
|
|
gs_free NMSetting **settings = NULL;
|
|
guint settings_len;
|
|
guint i;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
|
|
|
|
settings = nm_connection_get_settings (connection, &settings_len);
|
|
for (i = 0; i < settings_len; i++) {
|
|
NMSetting *setting = settings[i];
|
|
GVariant *setting_dict;
|
|
|
|
setting_dict = _nm_setting_to_dbus (setting, connection, flags, options);
|
|
if (!setting_dict)
|
|
continue;
|
|
if (!any) {
|
|
any = TRUE;
|
|
g_variant_builder_init (&builder, NM_VARIANT_TYPE_CONNECTION);
|
|
}
|
|
g_variant_builder_add (&builder, "{s@a{sv}}", nm_setting_get_name (setting), setting_dict);
|
|
}
|
|
|
|
if (!any)
|
|
return NULL;
|
|
|
|
return g_variant_builder_end (&builder);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_is_type:
|
|
* @connection: the #NMConnection
|
|
* @type: a setting name to check the connection's type against (like
|
|
* %NM_SETTING_WIRELESS_SETTING_NAME or %NM_SETTING_WIRED_SETTING_NAME)
|
|
*
|
|
* A convenience function to check if the given @connection is a particular
|
|
* type (ie wired, Wi-Fi, ppp, etc). Checks the #NMSettingConnection:type
|
|
* property of the connection and matches that against @type.
|
|
*
|
|
* Returns: %TRUE if the connection is of the given @type, %FALSE if not
|
|
**/
|
|
gboolean
|
|
nm_connection_is_type (NMConnection *connection, const char *type)
|
|
{
|
|
g_return_val_if_fail (type, FALSE);
|
|
|
|
return nm_streq0 (type, nm_connection_get_connection_type (connection));
|
|
}
|
|
|
|
static int
|
|
_for_each_sort (NMSetting **p_a, NMSetting **p_b, void *unused)
|
|
{
|
|
NMSetting *a = *p_a;
|
|
NMSetting *b = *p_b;
|
|
int c;
|
|
|
|
c = _nm_setting_compare_priority (a, b);
|
|
if (c != 0)
|
|
return c;
|
|
return strcmp (nm_setting_get_name (a), nm_setting_get_name (b));
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_settings:
|
|
* @connection: the #NMConnection instance
|
|
* @out_length: (allow-none) (out): the length of the returned array
|
|
*
|
|
* Retrieves the settings in @connection.
|
|
*
|
|
* The returned array is %NULL-terminated.
|
|
*
|
|
* Returns: (array length=out_length) (transfer container): a
|
|
* %NULL-terminated array containing every setting of
|
|
* @connection.
|
|
* If the connection has no settings, %NULL is returned.
|
|
*
|
|
* Since: 1.10
|
|
*/
|
|
NMSetting **
|
|
nm_connection_get_settings (NMConnection *connection,
|
|
guint *out_length)
|
|
{
|
|
NMConnectionPrivate *priv;
|
|
NMSetting **arr;
|
|
GHashTableIter iter;
|
|
NMSetting *setting;
|
|
guint i, size;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
|
|
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
size = g_hash_table_size (priv->settings);
|
|
|
|
if (!size) {
|
|
NM_SET_OUT (out_length, 0);
|
|
return NULL;
|
|
}
|
|
|
|
arr = g_new (NMSetting *, size + 1);
|
|
|
|
g_hash_table_iter_init (&iter, priv->settings);
|
|
for (i = 0; g_hash_table_iter_next (&iter, NULL, (gpointer *) &setting); i++)
|
|
arr[i] = setting;
|
|
nm_assert (i == size);
|
|
arr[size] = NULL;
|
|
|
|
/* sort the settings. This has an effect on the order in which keyfile
|
|
* prints them. */
|
|
if (size > 1)
|
|
g_qsort_with_data (arr, size, sizeof (NMSetting *), (GCompareDataFunc) _for_each_sort, NULL);
|
|
|
|
NM_SET_OUT (out_length, size);
|
|
return arr;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_for_each_setting_value:
|
|
* @connection: the #NMConnection
|
|
* @func: (scope call): user-supplied function called for each setting's property
|
|
* @user_data: user data passed to @func at each invocation
|
|
*
|
|
* Iterates over the properties of each #NMSetting object in the #NMConnection,
|
|
* calling the supplied user function for each property.
|
|
**/
|
|
void
|
|
nm_connection_for_each_setting_value (NMConnection *connection,
|
|
NMSettingValueIterFn func,
|
|
gpointer user_data)
|
|
{
|
|
gs_free NMSetting **settings = NULL;
|
|
guint i, length = 0;
|
|
|
|
g_return_if_fail (NM_IS_CONNECTION (connection));
|
|
g_return_if_fail (func);
|
|
|
|
settings = nm_connection_get_settings (connection, &length);
|
|
for (i = 0; i < length; i++)
|
|
nm_setting_enumerate_values (settings[i], func, user_data);
|
|
}
|
|
|
|
/**
|
|
* _nm_connection_aggregate:
|
|
* @connecition: the #NMConnection for which values are to be aggregated.
|
|
* @type: one of the supported aggrate types.
|
|
* @arg: the input/output argument that depends on @type.
|
|
*
|
|
* For example, with %NM_CONNECTION_AGGREGATE_ANY_SECRETS and
|
|
* %NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS @arg is a boolean
|
|
* output argument. It is either %NULL or a pointer to an gboolean
|
|
* out-argument. The function will always set @arg if given.
|
|
* Also, the return value of the function is likewise the result
|
|
* that is set to @arg.
|
|
*
|
|
* Returns: a boolean result with the meaning depending on the aggregation
|
|
* type @type.
|
|
*/
|
|
gboolean
|
|
_nm_connection_aggregate (NMConnection *connection,
|
|
NMConnectionAggregateType type,
|
|
gpointer arg)
|
|
{
|
|
NMConnectionPrivate *priv;
|
|
GHashTableIter iter;
|
|
NMSetting *setting;
|
|
gboolean arg_boolean;
|
|
gboolean completed_early;
|
|
gpointer my_arg;
|
|
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), FALSE);
|
|
|
|
switch (type) {
|
|
case NM_CONNECTION_AGGREGATE_ANY_SECRETS:
|
|
arg_boolean = FALSE;
|
|
my_arg = &arg_boolean;
|
|
goto good;
|
|
case NM_CONNECTION_AGGREGATE_ANY_SYSTEM_SECRET_FLAGS:
|
|
arg_boolean = FALSE;
|
|
my_arg = &arg_boolean;
|
|
goto good;
|
|
}
|
|
g_return_val_if_reached (FALSE);
|
|
|
|
good:
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
completed_early = FALSE;
|
|
g_hash_table_iter_init (&iter, priv->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
|
|
if (_nm_setting_aggregate (setting, type, my_arg)) {
|
|
completed_early = TRUE;
|
|
break;
|
|
}
|
|
nm_assert ( my_arg != &arg_boolean
|
|
|| !arg_boolean);
|
|
}
|
|
|
|
if (my_arg == &arg_boolean) {
|
|
nm_assert (completed_early == arg_boolean);
|
|
if (arg)
|
|
*((gboolean *) arg) = arg_boolean;
|
|
return arg_boolean;
|
|
}
|
|
|
|
nm_assert_not_reached ();
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_dump:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* Print the connection (including secrets!) to stdout. For debugging
|
|
* purposes ONLY, should NOT be used for serialization of the setting,
|
|
* or machine-parsed in any way. The output format is not guaranteed to
|
|
* be stable and may change at any time.
|
|
**/
|
|
void
|
|
nm_connection_dump (NMConnection *connection)
|
|
{
|
|
GHashTableIter iter;
|
|
NMSetting *setting;
|
|
char *str;
|
|
|
|
if (!connection)
|
|
return;
|
|
|
|
g_hash_table_iter_init (&iter, NM_CONNECTION_GET_PRIVATE (connection)->settings);
|
|
while (g_hash_table_iter_next (&iter, NULL, (gpointer) &setting)) {
|
|
str = nm_setting_to_string (setting);
|
|
g_print ("%s\n", str);
|
|
g_free (str);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* nm_connection_set_path:
|
|
* @connection: the #NMConnection
|
|
* @path: the D-Bus path of the connection as given by the settings service
|
|
* which provides the connection
|
|
*
|
|
* Sets the D-Bus path of the connection. This property is not serialized, and
|
|
* is only for the reference of the caller. Sets the #NMConnection:path
|
|
* property.
|
|
**/
|
|
void
|
|
nm_connection_set_path (NMConnection *connection, const char *path)
|
|
{
|
|
NMConnectionPrivate *priv;
|
|
|
|
g_return_if_fail (NM_IS_CONNECTION (connection));
|
|
|
|
priv = NM_CONNECTION_GET_PRIVATE (connection);
|
|
|
|
g_free (priv->path);
|
|
priv->path = g_strdup (path);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_path:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* Returns the connection's D-Bus path.
|
|
*
|
|
* Returns: the D-Bus path of the connection, previously set by a call to
|
|
* nm_connection_set_path().
|
|
**/
|
|
const char *
|
|
nm_connection_get_path (NMConnection *connection)
|
|
{
|
|
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
|
|
|
|
return NM_CONNECTION_GET_PRIVATE (connection)->path;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_interface_name:
|
|
* @connection: The #NMConnection
|
|
*
|
|
* Returns the interface name as stored in NMSettingConnection:interface_name.
|
|
* If the connection contains no NMSettingConnection, it will return %NULL.
|
|
*
|
|
* For hardware devices and software devices created outside of NetworkManager,
|
|
* this name is used to match the device. for software devices created by
|
|
* NetworkManager, this is the name of the created interface.
|
|
*
|
|
* Returns: Name of the kernel interface or %NULL
|
|
*/
|
|
const char *
|
|
nm_connection_get_interface_name (NMConnection *connection)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
return s_con ? nm_setting_connection_get_interface_name (s_con) : NULL;
|
|
}
|
|
|
|
NMConnectionMultiConnect
|
|
_nm_connection_get_multi_connect (NMConnection *connection)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
NMConnectionMultiConnect multi_connect;
|
|
const NMConnectionMultiConnect DEFAULT = NM_CONNECTION_MULTI_CONNECT_SINGLE;
|
|
|
|
/* connection.multi_connect property cannot be specified via regular
|
|
* connection defaults in NetworkManager.conf, because those are per-device,
|
|
* and we need to determine the multi_connect independent of a particular
|
|
* device.
|
|
*
|
|
* There is however still a default-value, so theoretically, the default
|
|
* value could be specified in NetworkManager.conf. Just not as [connection*]
|
|
* and indepdented of a device. */
|
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
if (!s_con)
|
|
return DEFAULT;
|
|
|
|
multi_connect = nm_setting_connection_get_multi_connect (s_con);
|
|
return multi_connect == NM_CONNECTION_MULTI_CONNECT_DEFAULT
|
|
? DEFAULT
|
|
: multi_connect;
|
|
}
|
|
|
|
gboolean
|
|
_nm_connection_verify_required_interface_name (NMConnection *connection,
|
|
GError **error)
|
|
{
|
|
const char *interface_name;
|
|
|
|
if (!connection)
|
|
return TRUE;
|
|
|
|
interface_name = nm_connection_get_interface_name (connection);
|
|
if (interface_name)
|
|
return TRUE;
|
|
|
|
g_set_error_literal (error,
|
|
NM_CONNECTION_ERROR,
|
|
NM_CONNECTION_ERROR_MISSING_PROPERTY,
|
|
_("property is missing"));
|
|
g_prefix_error (error, "%s.%s: ", NM_SETTING_CONNECTION_SETTING_NAME, NM_SETTING_CONNECTION_INTERFACE_NAME);
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_uuid:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return the UUID from the connection's #NMSettingConnection.
|
|
*
|
|
* Returns: the UUID from the connection's 'connection' setting
|
|
**/
|
|
const char *
|
|
nm_connection_get_uuid (NMConnection *connection)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
return s_con ? nm_setting_connection_get_uuid (s_con) : NULL;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_id:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return the ID from the connection's #NMSettingConnection.
|
|
*
|
|
* Returns: the ID from the connection's 'connection' setting
|
|
**/
|
|
const char *
|
|
nm_connection_get_id (NMConnection *connection)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
return s_con ? nm_setting_connection_get_id (s_con) : NULL;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_connection_type:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return the type from the connection's #NMSettingConnection.
|
|
*
|
|
* Returns: the type from the connection's 'connection' setting
|
|
**/
|
|
const char *
|
|
nm_connection_get_connection_type (NMConnection *connection)
|
|
{
|
|
NMSettingConnection *s_con;
|
|
|
|
s_con = nm_connection_get_setting_connection (connection);
|
|
return s_con ? nm_setting_connection_get_connection_type (s_con) : NULL;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_is_virtual:
|
|
* @connection: an #NMConnection
|
|
*
|
|
* Checks if @connection refers to a virtual device (and thus can potentially be
|
|
* activated even if the device it refers to doesn't exist).
|
|
*
|
|
* Returns: whether @connection refers to a virtual device
|
|
*/
|
|
gboolean
|
|
nm_connection_is_virtual (NMConnection *connection)
|
|
{
|
|
const char *type;
|
|
|
|
type = nm_connection_get_connection_type (connection);
|
|
if (!type)
|
|
return FALSE;
|
|
|
|
if (NM_IN_STRSET (type, NM_SETTING_6LOWPAN_SETTING_NAME,
|
|
NM_SETTING_BOND_SETTING_NAME,
|
|
NM_SETTING_BRIDGE_SETTING_NAME,
|
|
NM_SETTING_DUMMY_SETTING_NAME,
|
|
NM_SETTING_IP_TUNNEL_SETTING_NAME,
|
|
NM_SETTING_MACSEC_SETTING_NAME,
|
|
NM_SETTING_MACVLAN_SETTING_NAME,
|
|
NM_SETTING_OVS_BRIDGE_SETTING_NAME,
|
|
NM_SETTING_OVS_INTERFACE_SETTING_NAME,
|
|
NM_SETTING_OVS_PORT_SETTING_NAME,
|
|
NM_SETTING_TEAM_SETTING_NAME,
|
|
NM_SETTING_TUN_SETTING_NAME,
|
|
NM_SETTING_VLAN_SETTING_NAME,
|
|
NM_SETTING_VRF_SETTING_NAME,
|
|
NM_SETTING_VXLAN_SETTING_NAME,
|
|
NM_SETTING_WIREGUARD_SETTING_NAME))
|
|
return TRUE;
|
|
|
|
if (nm_streq (type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
|
|
NMSettingInfiniband *s_ib;
|
|
|
|
s_ib = nm_connection_get_setting_infiniband (connection);
|
|
return s_ib && nm_setting_infiniband_get_virtual_interface_name (s_ib);
|
|
}
|
|
|
|
if (nm_streq (type, NM_SETTING_BLUETOOTH_SETTING_NAME))
|
|
return !!_nm_connection_get_setting_bluetooth_for_nap (connection);
|
|
|
|
if (nm_streq (type, NM_SETTING_PPPOE_SETTING_NAME)) {
|
|
NMSettingPppoe *s_pppoe;
|
|
|
|
s_pppoe = nm_connection_get_setting_pppoe (connection);
|
|
return !!nm_setting_pppoe_get_parent (s_pppoe);
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_virtual_device_description:
|
|
* @connection: an #NMConnection for a virtual device type
|
|
*
|
|
* Returns the name that nm_device_disambiguate_names() would
|
|
* return for the virtual device that would be created for @connection.
|
|
* Eg, "VLAN (eth1.1)".
|
|
*
|
|
* Returns: (transfer full): the name of @connection's device,
|
|
* or %NULL if @connection is not a virtual connection type
|
|
*/
|
|
char *
|
|
nm_connection_get_virtual_device_description (NMConnection *connection)
|
|
{
|
|
const char *type;
|
|
const char *iface = NULL, *display_type = NULL;
|
|
|
|
type = nm_connection_get_connection_type (connection);
|
|
if (!type)
|
|
return NULL;
|
|
|
|
iface = nm_connection_get_interface_name (connection);
|
|
|
|
if (!strcmp (type, NM_SETTING_BOND_SETTING_NAME))
|
|
display_type = _("Bond");
|
|
else if (!strcmp (type, NM_SETTING_TEAM_SETTING_NAME))
|
|
display_type = _("Team");
|
|
else if (!strcmp (type, NM_SETTING_BRIDGE_SETTING_NAME))
|
|
display_type = _("Bridge");
|
|
else if (!strcmp (type, NM_SETTING_VLAN_SETTING_NAME))
|
|
display_type = _("VLAN");
|
|
else if (!strcmp (type, NM_SETTING_INFINIBAND_SETTING_NAME)) {
|
|
display_type = _("InfiniBand");
|
|
iface = nm_setting_infiniband_get_virtual_interface_name (nm_connection_get_setting_infiniband (connection));
|
|
} else if (!strcmp (type, NM_SETTING_IP_TUNNEL_SETTING_NAME))
|
|
display_type = _("IP Tunnel");
|
|
|
|
if (!iface || !display_type)
|
|
return NULL;
|
|
|
|
return g_strdup_printf ("%s (%s)", display_type, iface);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/**
|
|
* nm_connection_get_setting_802_1x:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSetting8021x the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSetting8021x if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSetting8021x *
|
|
nm_connection_get_setting_802_1x (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_802_1X);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_bluetooth:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingBluetooth the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingBluetooth if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingBluetooth *
|
|
nm_connection_get_setting_bluetooth (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_BLUETOOTH);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_bond:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingBond the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingBond if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingBond *
|
|
nm_connection_get_setting_bond (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_BOND);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_team:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingTeam the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingTeam if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingTeam *
|
|
nm_connection_get_setting_team (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_TEAM);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_team_port:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingTeamPort the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingTeamPort if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingTeamPort *
|
|
nm_connection_get_setting_team_port (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_TEAM_PORT);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_bridge:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingBridge the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingBridge if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingBridge *
|
|
nm_connection_get_setting_bridge (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_BRIDGE);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_cdma:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingCdma the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingCdma if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingCdma *
|
|
nm_connection_get_setting_cdma (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_CDMA);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_connection:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingConnection the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingConnection if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingConnection *
|
|
nm_connection_get_setting_connection (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_CONNECTION);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_dcb:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingDcb the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingDcb if the connection contains one, otherwise NULL
|
|
**/
|
|
NMSettingDcb *
|
|
nm_connection_get_setting_dcb (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_DCB);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_dummy:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingDummy the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingDummy if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.8
|
|
**/
|
|
NMSettingDummy *
|
|
nm_connection_get_setting_dummy (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_DUMMY);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_generic:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingGeneric the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingGeneric if the connection contains one, otherwise NULL
|
|
**/
|
|
NMSettingGeneric *
|
|
nm_connection_get_setting_generic (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_GENERIC);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_gsm:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingGsm the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingGsm if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingGsm *
|
|
nm_connection_get_setting_gsm (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_GSM);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_infiniband:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingInfiniband the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingInfiniband if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingInfiniband *
|
|
nm_connection_get_setting_infiniband (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_INFINIBAND);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_ip4_config:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingIP4Config the connection might contain.
|
|
*
|
|
* Note that it returns the value as type #NMSettingIPConfig, since the vast
|
|
* majority of IPv4-setting-related methods are on that type, not
|
|
* #NMSettingIP4Config.
|
|
*
|
|
* Returns: (type NMSettingIP4Config) (transfer none): an #NMSettingIP4Config if the
|
|
* connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingIPConfig *
|
|
nm_connection_get_setting_ip4_config (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_IP4_CONFIG);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_ip_tunnel:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingIPTunnel the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingIPTunnel if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
NMSettingIPTunnel *
|
|
nm_connection_get_setting_ip_tunnel (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_IP_TUNNEL);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_ip6_config:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingIP6Config the connection might contain.
|
|
*
|
|
* Note that it returns the value as type #NMSettingIPConfig, since the vast
|
|
* majority of IPv6-setting-related methods are on that type, not
|
|
* #NMSettingIP6Config.
|
|
*
|
|
* Returns: (type NMSettingIP6Config) (transfer none): an #NMSettingIP6Config if the
|
|
* connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingIPConfig *
|
|
nm_connection_get_setting_ip6_config (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_IP6_CONFIG);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_macsec:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingMacsec the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingMacsec if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.6
|
|
**/
|
|
NMSettingMacsec *
|
|
nm_connection_get_setting_macsec (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_MACSEC);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_macvlan:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingMacvlan the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingMacvlan if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
NMSettingMacvlan *
|
|
nm_connection_get_setting_macvlan (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_MACVLAN);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_olpc_mesh:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingOlpcMesh the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingOlpcMesh if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingOlpcMesh *
|
|
nm_connection_get_setting_olpc_mesh (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_OLPC_MESH);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_ovs_bridge:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingOvsBridge the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingOvsBridge if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.10
|
|
**/
|
|
NMSettingOvsBridge *
|
|
nm_connection_get_setting_ovs_bridge (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_BRIDGE);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_ovs_interface:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingOvsInterface the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingOvsInterface if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.10
|
|
**/
|
|
NMSettingOvsInterface *
|
|
nm_connection_get_setting_ovs_interface (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_INTERFACE);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_ovs_patch:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingOvsPatch the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingOvsPatch if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.10
|
|
**/
|
|
NMSettingOvsPatch *
|
|
nm_connection_get_setting_ovs_patch (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_PATCH);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_ovs_port:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingOvsPort the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingOvsPort if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.10
|
|
**/
|
|
NMSettingOvsPort *
|
|
nm_connection_get_setting_ovs_port (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_OVS_PORT);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_ppp:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingPpp the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingPpp if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingPpp *
|
|
nm_connection_get_setting_ppp (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_PPP);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_pppoe:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingPppoe the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingPppoe if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingPppoe *
|
|
nm_connection_get_setting_pppoe (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_PPPOE);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_proxy:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingProxy the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingProxy if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.6
|
|
**/
|
|
NMSettingProxy *
|
|
nm_connection_get_setting_proxy (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_PROXY);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_serial:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingSerial the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingSerial if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingSerial *
|
|
nm_connection_get_setting_serial (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_SERIAL);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_tc_config:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingTCConfig the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingTCConfig if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.12
|
|
**/
|
|
NMSettingTCConfig *
|
|
nm_connection_get_setting_tc_config (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_TC_CONFIG);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_tun:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingTun the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingTun if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
NMSettingTun *
|
|
nm_connection_get_setting_tun (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_TUN);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_vpn:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingVpn the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingVpn if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingVpn *
|
|
nm_connection_get_setting_vpn (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_VPN);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_vxlan:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingVxlan the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingVxlan if the connection contains one, otherwise %NULL
|
|
*
|
|
* Since: 1.2
|
|
**/
|
|
NMSettingVxlan *
|
|
nm_connection_get_setting_vxlan (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_VXLAN);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_wimax:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingWimax the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingWimax if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingWimax *
|
|
nm_connection_get_setting_wimax (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_WIMAX);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_wired:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingWired the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingWired if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingWired *
|
|
nm_connection_get_setting_wired (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_WIRED);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_adsl:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingAdsl the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingAdsl if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingAdsl *
|
|
nm_connection_get_setting_adsl (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_ADSL);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_wireless:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingWireless the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingWireless if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingWireless *
|
|
nm_connection_get_setting_wireless (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_WIRELESS);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_wireless_security:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingWirelessSecurity the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingWirelessSecurity if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingWirelessSecurity *
|
|
nm_connection_get_setting_wireless_security (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_bridge_port:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingBridgePort the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingBridgePort if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingBridgePort *
|
|
nm_connection_get_setting_bridge_port (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_BRIDGE_PORT);
|
|
}
|
|
|
|
/**
|
|
* nm_connection_get_setting_vlan:
|
|
* @connection: the #NMConnection
|
|
*
|
|
* A shortcut to return any #NMSettingVlan the connection might contain.
|
|
*
|
|
* Returns: (transfer none): an #NMSettingVlan if the connection contains one, otherwise %NULL
|
|
**/
|
|
NMSettingVlan *
|
|
nm_connection_get_setting_vlan (NMConnection *connection)
|
|
{
|
|
return _connection_get_setting_check (connection, NM_TYPE_SETTING_VLAN);
|
|
}
|
|
|
|
NMSettingBluetooth *
|
|
_nm_connection_get_setting_bluetooth_for_nap (NMConnection *connection)
|
|
{
|
|
NMSettingBluetooth *s_bt = nm_connection_get_setting_bluetooth (connection);
|
|
|
|
if ( s_bt
|
|
&& nm_streq0 (nm_setting_bluetooth_get_connection_type (s_bt), NM_SETTING_BLUETOOTH_TYPE_NAP))
|
|
return s_bt;
|
|
return NULL;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
nm_connection_private_free (NMConnectionPrivate *priv)
|
|
{
|
|
NMConnection *self = priv->self;
|
|
|
|
g_hash_table_foreach_remove (priv->settings, _setting_release_hfr, self);
|
|
g_hash_table_destroy (priv->settings);
|
|
g_free (priv->path);
|
|
|
|
g_slice_free (NMConnectionPrivate, priv);
|
|
}
|
|
|
|
static NMConnectionPrivate *
|
|
nm_connection_get_private (NMConnection *connection)
|
|
{
|
|
GQuark key;
|
|
NMConnectionPrivate *priv;
|
|
|
|
nm_assert (NM_IS_CONNECTION (connection));
|
|
|
|
key = NM_CACHED_QUARK ("NMConnectionPrivate");
|
|
|
|
priv = g_object_get_qdata ((GObject *) connection, key);
|
|
if (G_UNLIKELY (!priv)) {
|
|
priv = g_slice_new0 (NMConnectionPrivate);
|
|
g_object_set_qdata_full ((GObject *) connection, key,
|
|
priv, (GDestroyNotify) nm_connection_private_free);
|
|
|
|
priv->self = connection;
|
|
priv->settings = g_hash_table_new_full (nm_direct_hash,
|
|
NULL,
|
|
NULL,
|
|
g_object_unref);
|
|
}
|
|
|
|
return priv;
|
|
}
|
|
|
|
static void
|
|
nm_connection_default_init (NMConnectionInterface *iface)
|
|
{
|
|
/**
|
|
* NMConnection::secrets-updated:
|
|
* @connection: the object on which the signal is emitted
|
|
* @setting_name: the setting name of the #NMSetting for which secrets were
|
|
* updated
|
|
*
|
|
* The ::secrets-updated signal is emitted when the secrets of a setting
|
|
* have been changed.
|
|
*/
|
|
signals[SECRETS_UPDATED] =
|
|
g_signal_new (NM_CONNECTION_SECRETS_UPDATED,
|
|
NM_TYPE_CONNECTION,
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (NMConnectionInterface, secrets_updated),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__STRING,
|
|
G_TYPE_NONE, 1,
|
|
G_TYPE_STRING);
|
|
|
|
/**
|
|
* NMConnection::secrets-cleared:
|
|
* @connection: the object on which the signal is emitted
|
|
*
|
|
* The ::secrets-cleared signal is emitted when the secrets of a connection
|
|
* are cleared.
|
|
*/
|
|
signals[SECRETS_CLEARED] =
|
|
g_signal_new (NM_CONNECTION_SECRETS_CLEARED,
|
|
NM_TYPE_CONNECTION,
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (NMConnectionInterface, secrets_cleared),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
|
|
/**
|
|
* NMConnection::changed:
|
|
* @connection: the object on which the signal is emitted
|
|
*
|
|
* The ::changed signal is emitted when any property of any property
|
|
* (including secrets) of any setting of the connection is modified,
|
|
* or when settings are added or removed.
|
|
*/
|
|
signals[CHANGED] =
|
|
g_signal_new (NM_CONNECTION_CHANGED,
|
|
NM_TYPE_CONNECTION,
|
|
G_SIGNAL_RUN_FIRST,
|
|
G_STRUCT_OFFSET (NMConnectionInterface, changed),
|
|
NULL, NULL,
|
|
g_cclosure_marshal_VOID__VOID,
|
|
G_TYPE_NONE, 0);
|
|
}
|