merge: branch 'bg/rh1934122'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/862

(cherry picked from commit 2d879c1ac5)
This commit is contained in:
Beniamino Galvani 2021-07-27 17:47:38 +02:00
commit 2188c00ed4
14 changed files with 398 additions and 43 deletions

View file

@ -1150,6 +1150,63 @@ managed=1
</para>
</listitem>
</varlistentry>
<varlistentry id="keep-configuration">
<term><varname>keep-configuration</varname></term>
<listitem>
<para>
On startup, NetworkManager tries to not interfere with
interfaces that are already configured. It does so by
generating a in-memory connection based on the interface
current configuration.
</para>
<para>
If this generated connection matches one of the existing
persistent connections, the persistent connection gets
activated. If there is no match, the generated
connection gets activated as "external", which means
that the connection is considered as active, but
NetworkManager doesn't actually touch the interface.
</para>
<para>
It is possible to disable this behavior by setting
<literal>keep-configuration</literal> to
<literal>no</literal>. In this way, on startup
NetworkManager always tries to activate the most
suitable persistent connection (the one with highest
autoconnect-priority or, in case of a tie, the one
activated most recently).
</para>
<para>
Note that when NetworkManager gets restarted, it stores
the previous state in
<filename>/run/NetworkManager</filename>; in particular
it saves the UUID of the connection that was previously
active so that it can be activated again after the
restart. Therefore,
<literal>keep-configuration</literal> does not have
any effect on service restart.
</para>
</listitem>
</varlistentry>
<varlistentry id="allowed-connections">
<term><varname>allowed-connections</varname></term>
<listitem>
<para>
A list of connections that can be activated on the
device. See <xref linkend="connection-spec"/> for the
syntax to specify a connection. If this option is not
specified, all connections can be potentially activated
on the device, provided that the connection type and
other settings match.
</para>
<para>
A notable use case for this is to filter which
connections can be activated based on how they were
created; see the <literal>origin</literal> keyword in
<xref linkend="connection-spec"/>.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>wifi.scan-rand-mac-address</varname></term>
<listitem>
@ -1647,6 +1704,55 @@ interface-name:vboxnet*,except:interface-name:vboxnet2
</programlisting>
</para>
</refsect2>
<refsect2 id="connection-spec">
<title>Connection List Format</title>
<para>
Connections can be specified using the following format:
</para>
<para>
<variablelist>
<varlistentry>
<term>*</term>
<listitem><para>Matches every connection.</para></listitem>
</varlistentry>
<varlistentry>
<term>uuid:UUID</term>
<listitem><para>Match the connection by UUID, for example
<literal>"uuid:83037490-1d17-4986-a397-01f1db3a7fc2"</literal></para></listitem>
</varlistentry>
<varlistentry>
<term>id=ID</term>
<listitem><para>Match the connection by name.</para></listitem>
</varlistentry>
<varlistentry>
<term>origin:ORIGIN</term>
<listitem><para>Match the connection by origin, stored in the
<literal>org.freedesktop.NetworkManager.origin</literal> tag of the user setting. For example, use
<literal>"except:origin:nm-initrd-generator"</literal> to forbid activation of connections created by the
initrd generator.</para></listitem>
</varlistentry>
<varlistentry>
<term>except:SPEC</term>
<listitem><para>Negative match of a connection. A negative match has higher priority then the positive
matches above.</para>
<para>If there is a list consisting only of negative matches, the behavior is the same as if there is also
match-all. That means, if none of all the negative matches is satisfied, the overall result is still a
positive match.</para></listitem>
</varlistentry>
<varlistentry>
<term>SPEC[,;]SPEC</term>
<listitem><para>Multiple specs can be concatenated with commas or semicolons. The order does not matter as
matches are either inclusive or negative (<literal>except:</literal>), with negative matches having higher
priority.</para>
<para>Backslash is supported to escape the separators ';' and ',', and to express special characters such as
newline ('\n'), tabulator ('\t'), whitespace ('\s') and backslash ('\\'). Whitespace is not a separator but
will be trimmed between two specs (unless escaped as '\s').</para></listitem>
</varlistentry>
</variablelist>
</para>
</refsect2>
</refsect1>
<refsect1>

View file

@ -7239,6 +7239,16 @@ _get_maybe_ipv6_disabled(NMDevice *self)
return (nm_platform_sysctl_get_int32(platform, NMP_SYSCTL_PATHID_ABSOLUTE(path), 0) == 0);
}
/*
* nm_device_generate_connection:
*
* Generates a connection from an existing interface.
*
* If the device doesn't have an IP configuration and it's not a port or a
* controller, then no connection gets generated and the function returns
* %NULL. In such case, @maybe_later is set to %TRUE if a connection can be
* generated later when an IP address is assigned to the interface.
*/
NMConnection *
nm_device_generate_connection(NMDevice *self,
NMDevice *master,
@ -7495,6 +7505,8 @@ check_connection_compatible(NMDevice *self, NMConnection *connection, GError **e
gs_free char * conn_iface = NULL;
NMDeviceClass * klass;
NMSettingMatch * s_match;
const GSList * specs;
gboolean has_match = FALSE;
klass = NM_DEVICE_GET_CLASS(self);
if (klass->connection_type_check_compatible) {
@ -7571,6 +7583,15 @@ check_connection_compatible(NMDevice *self, NMConnection *connection, GError **e
}
}
specs =
nm_config_data_get_device_allowed_connections_specs(NM_CONFIG_GET_DATA, self, &has_match);
if (has_match && !nm_utils_connection_match_spec_list(connection, specs, FALSE)) {
nm_utils_error_set_literal(error,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_DISALLOWED,
"device configuration doesn't allow this connection");
return FALSE;
}
return TRUE;
}
@ -7637,7 +7658,7 @@ nm_device_check_slave_connection_compatible(NMDevice *self, NMConnection *slave)
*
* Returns: %TRUE if the device is capable of assuming connections, %FALSE if not
*/
static gboolean
gboolean
nm_device_can_assume_connections(NMDevice *self)
{
return !!NM_DEVICE_GET_CLASS(self)->update_connection;

View file

@ -527,6 +527,7 @@ nm_device_check_connection_compatible(NMDevice *device, NMConnection *connection
gboolean nm_device_check_slave_connection_compatible(NMDevice *device, NMConnection *connection);
gboolean nm_device_can_assume_connections(NMDevice *self);
gboolean nm_device_unmanage_on_quit(NMDevice *self);
gboolean nm_device_spec_match_list(NMDevice *device, const GSList *specs);

View file

@ -26,6 +26,15 @@ typedef struct {
gboolean has;
GSList * spec;
} match_device;
union {
struct {
GSList * allowed_connections;
gboolean allowed_connections_has;
} device;
};
gboolean is_device;
/* List of key/value pairs in the section, sorted by key */
gsize lookup_len;
const NMUtilsNamedValue *lookup_idx;
} MatchSectionInfo;
@ -1436,13 +1445,13 @@ _match_section_infos_lookup(const MatchSectionInfo *match_section_infos,
match = TRUE;
if (match) {
*out_value = value;
NM_SET_OUT(out_value, value);
return match_section_infos;
}
}
out:
*out_value = NULL;
NM_SET_OUT(out_value, NULL);
return NULL;
}
@ -1538,6 +1547,37 @@ nm_config_data_get_device_config_int64(const NMConfigData *self,
return _nm_utils_ascii_str_to_int64(value, base, min, max, val_invalid);
}
const GSList *
nm_config_data_get_device_allowed_connections_specs(const NMConfigData *self,
NMDevice * device,
gboolean * has_match)
{
const NMConfigDataPrivate *priv;
const MatchSectionInfo * connection_info;
const GSList * ret = NULL;
g_return_val_if_fail(self, NULL);
priv = NM_CONFIG_DATA_GET_PRIVATE(self);
connection_info = _match_section_infos_lookup(&priv->device_infos[0],
priv->keyfile,
NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS,
device,
NULL,
NULL,
NULL);
if (connection_info) {
nm_assert(connection_info->device.allowed_connections_has);
ret = connection_info->device.allowed_connections;
NM_SET_OUT(has_match, TRUE);
} else
NM_SET_OUT(has_match, FALSE);
return ret;
}
const char *
nm_config_data_get_connection_default(const NMConfigData *self,
const char * property,
@ -1610,7 +1650,10 @@ _match_section_info_get_str(const MatchSectionInfo *m, GKeyFile *keyfile, const
}
static void
_match_section_info_init(MatchSectionInfo *connection_info, GKeyFile *keyfile, char *group)
_match_section_info_init(MatchSectionInfo *connection_info,
GKeyFile * keyfile,
char * group,
gboolean is_device)
{
char ** keys = NULL;
gsize n_keys;
@ -1629,6 +1672,14 @@ _match_section_info_init(MatchSectionInfo *connection_info, GKeyFile *keyfile, c
connection_info->stop_match =
nm_config_keyfile_get_boolean(keyfile, group, NM_CONFIG_KEYFILE_KEY_STOP_MATCH, FALSE);
if (is_device) {
connection_info->device.allowed_connections =
nm_config_get_match_spec(keyfile,
group,
NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS,
&connection_info->device.allowed_connections_has);
}
keys = g_key_file_get_keys(keyfile, group, &n_keys, NULL);
nm_utils_strv_sort(keys, n_keys);
@ -1680,9 +1731,13 @@ _match_section_infos_free(MatchSectionInfo *match_section_infos)
if (!match_section_infos)
return;
for (m = match_section_infos; m->group_name; m++) {
g_free(m->group_name);
g_slist_free_full(m->match_device.spec, g_free);
if (m->is_device) {
g_slist_free_full(m->device.allowed_connections, g_free);
}
for (i = 0; i < m->lookup_len; i++) {
g_free(m->lookup_idx[i].name_mutable);
g_free(m->lookup_idx[i].value_str_mutable);
@ -1693,12 +1748,16 @@ _match_section_infos_free(MatchSectionInfo *match_section_infos)
}
static MatchSectionInfo *
_match_section_infos_construct(GKeyFile *keyfile, const char *prefix)
_match_section_infos_construct(GKeyFile *keyfile, gboolean is_device)
{
char ** groups;
gsize i, j, ngroups;
char * connection_tag = NULL;
MatchSectionInfo *match_section_infos = NULL;
const char * prefix;
prefix =
is_device ? NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE : NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION;
/* get the list of existing [connection.\+]/[device.\+] sections.
*
@ -1730,13 +1789,17 @@ _match_section_infos_construct(GKeyFile *keyfile, const char *prefix)
}
match_section_infos = g_new0(MatchSectionInfo, ngroups + 1 + (connection_tag ? 1 : 0));
match_section_infos->is_device = is_device;
for (i = 0; i < ngroups; i++) {
/* pass ownership of @group on... */
_match_section_info_init(&match_section_infos[i], keyfile, groups[ngroups - i - 1]);
_match_section_info_init(&match_section_infos[i],
keyfile,
groups[ngroups - i - 1],
is_device);
}
if (connection_tag) {
/* pass ownership of @connection_tag on... */
_match_section_info_init(&match_section_infos[i], keyfile, connection_tag);
_match_section_info_init(&match_section_infos[i], keyfile, connection_tag, is_device);
}
g_free(groups);
@ -1950,10 +2013,8 @@ constructed(GObject *object)
priv->keyfile = _merge_keyfiles(priv->keyfile_user, priv->keyfile_intern);
priv->connection_infos =
_match_section_infos_construct(priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_CONNECTION);
priv->device_infos =
_match_section_infos_construct(priv->keyfile, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE);
priv->connection_infos = _match_section_infos_construct(priv->keyfile, FALSE);
priv->device_infos = _match_section_infos_construct(priv->keyfile, TRUE);
priv->connectivity.enabled =
nm_config_keyfile_get_boolean(priv->keyfile,

View file

@ -243,6 +243,10 @@ gint64 nm_config_data_get_device_config_int64(const NMConfigData *self,
gint64 val_no_match,
gint64 val_invalid);
const GSList *nm_config_data_get_device_allowed_connections_specs(const NMConfigData *self,
NMDevice * device,
gboolean * has_match);
char ** nm_config_data_get_groups(const NMConfigData *self);
char ** nm_config_data_get_keys(const NMConfigData *self, const char *group);
gboolean nm_config_data_is_intern_atomic_group(const NMConfigData *self, const char *group);

View file

@ -796,6 +796,7 @@ static gboolean
_setting_is_device_spec(const char *group, const char *key)
{
#define _IS(group_v, key_v) (nm_streq(group, "" group_v "") && nm_streq(key, "" key_v ""))
return _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_NO_AUTO_DEFAULT)
|| _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_IGNORE_CARRIER)
|| _IS(NM_CONFIG_KEYFILE_GROUP_MAIN, NM_CONFIG_KEYFILE_KEY_MAIN_ASSUME_IPV6LL_ONLY)
@ -806,6 +807,13 @@ _setting_is_device_spec(const char *group, const char *key)
&& nm_streq(key, NM_CONFIG_KEYFILE_KEY_MATCH_DEVICE));
}
static gboolean
_setting_is_connection_spec(const char *group, const char *key)
{
return NM_STR_HAS_PREFIX(group, NM_CONFIG_KEYFILE_GROUPPREFIX_DEVICE)
&& nm_streq(key, NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS);
}
static gboolean
_setting_is_string_list(const char *group, const char *key)
{
@ -878,6 +886,8 @@ static const ConfigGroup config_groups[] = {
NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER,
NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED,
NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS,
NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION,
NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS,
NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND,
NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS,
NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_GENERATE_MAC_ADDRESS_MASK,
@ -1059,7 +1069,8 @@ read_config(GKeyFile * keyfile,
is_string_list = _setting_is_string_list(group, base_key);
if (is_string_list || _setting_is_device_spec(group, base_key)) {
if (is_string_list || _setting_is_device_spec(group, base_key)
|| _setting_is_connection_spec(group, base_key)) {
gs_unref_ptrarray GPtrArray *new = g_ptr_array_new_with_free_func(g_free);
char ** iter_val;
gs_strfreev char **old_val = NULL;

View file

@ -1484,6 +1484,112 @@ nm_match_spec_device(const GSList *specs,
return _match_result(has_except, has_not_except, has_match, has_match_except);
}
typedef struct {
const char *uuid;
const char *id;
const char *origin;
} MatchConnectionData;
static gboolean
match_connection_eval(const char *spec_str, const MatchConnectionData *match_data)
{
if (spec_str[0] == '*' && spec_str[1] == '\0')
return TRUE;
if (_MATCH_CHECK(spec_str, "id:"))
return nm_streq0(spec_str, match_data->id);
if (_MATCH_CHECK(spec_str, "uuid:"))
return nm_streq0(spec_str, match_data->uuid);
if (_MATCH_CHECK(spec_str, "origin:"))
return nm_streq0(spec_str, match_data->origin);
return FALSE;
}
static NMMatchSpecMatchType
match_spec_connection(const GSList *specs, const char *id, const char *uuid, const char *origin)
{
const GSList * iter;
gboolean has_match = FALSE;
gboolean has_match_except = FALSE;
gboolean has_except = FALSE;
gboolean has_not_except = FALSE;
const char * spec_str;
const MatchConnectionData match_data = {
.id = nm_str_not_empty(id),
.uuid = nm_str_not_empty(uuid),
.origin = nm_str_not_empty(origin),
};
if (!specs)
return NM_MATCH_SPEC_NO_MATCH;
for (iter = specs; iter; iter = iter->next) {
gboolean except;
spec_str = iter->data;
if (!spec_str || !*spec_str)
continue;
spec_str = match_except(spec_str, &except);
if (except)
has_except = TRUE;
else
has_not_except = TRUE;
if ((except && has_match_except) || (!except && has_match)) {
/* evaluating the match does not give new information. Skip it. */
continue;
}
if (!match_connection_eval(spec_str, &match_data))
continue;
if (except)
has_match_except = TRUE;
else
has_match = TRUE;
}
return _match_result(has_except, has_not_except, has_match, has_match_except);
}
int
nm_utils_connection_match_spec_list(NMConnection *connection,
const GSList *specs,
int no_match_value)
{
NMMatchSpecMatchType m;
NMSettingUser * s_user;
const char * origin = NULL;
if (!specs)
return no_match_value;
s_user = _nm_connection_get_setting(connection, NM_TYPE_SETTING_USER);
if (s_user)
origin = nm_setting_user_get_data(s_user, NM_USER_TAG_ORIGIN);
m = match_spec_connection(specs,
nm_connection_get_id(connection),
nm_connection_get_uuid(connection),
origin);
switch (m) {
case NM_MATCH_SPEC_MATCH:
return TRUE;
case NM_MATCH_SPEC_NEG_MATCH:
return FALSE;
case NM_MATCH_SPEC_NO_MATCH:
return no_match_value;
}
nm_assert_not_reached();
return no_match_value;
}
static gboolean
match_config_eval(const char *str, const char *tag, guint cur_nm_version)
{

View file

@ -211,6 +211,10 @@ gboolean nm_utils_kernel_cmdline_match_check(const char *const *proc_cmdline,
guint num_patterns,
GError ** error);
int nm_utils_connection_match_spec_list(NMConnection *connection,
const GSList *specs,
int no_match_value);
/*****************************************************************************/
gboolean nm_utils_connection_has_default_route(NMConnection *connection,

View file

@ -2595,6 +2595,12 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat
nm_device_capture_initial_config(device);
if (!nm_device_can_assume_connections(device)) {
nm_device_assume_state_reset(device);
_LOG2D(LOGD_DEVICE, device, "assume: device cannot assume connection");
return NULL;
}
if (ifindex) {
int master_ifindex = nm_platform_link_get_master(priv->platform, ifindex);
@ -2626,39 +2632,47 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat
}
}
/* The core of the API is nm_device_generate_connection() function and
* update_connection() virtual method and the convenient connection_type
* class attribute. Subclasses supporting the new API must have
* update_connection() implemented, otherwise nm_device_generate_connection()
* returns NULL.
*/
connection = nm_device_generate_connection(device, master, &maybe_later, &gen_error);
if (!connection) {
if (maybe_later) {
/* The device can generate a connection, but it failed for now.
* Give it a chance to match a connection from the state file. */
only_by_uuid = TRUE;
} else {
nm_device_assume_state_reset(device);
_LOG2D(LOGD_DEVICE,
device,
"assume: cannot generate connection: %s",
gen_error->message);
return NULL;
if (nm_config_data_get_device_config_boolean(NM_CONFIG_GET_DATA,
NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION,
device,
TRUE,
TRUE)) {
/* The core of the API is nm_device_generate_connection() function, based on
* update_connection() virtual method and the @connection_type_supported
* class attribute. Devices that support assuming existing connections must
* have update_connection() implemented, otherwise
* nm_device_generate_connection() returns NULL. */
connection = nm_device_generate_connection(device, master, &maybe_later, &gen_error);
if (!connection) {
if (maybe_later) {
/* The device can potentially assume connections, but at this
* time we can't generate a connection because no address is
* configured. Allow the device to assume a connection indicated
* in the state file by UUID. */
only_by_uuid = TRUE;
} else {
nm_device_assume_state_reset(device);
_LOG2D(LOGD_DEVICE,
device,
"assume: cannot generate connection: %s",
gen_error->message);
return NULL;
}
}
} else {
connection = NULL;
only_by_uuid = TRUE;
g_set_error(&gen_error,
NM_DEVICE_ERROR,
NM_DEVICE_ERROR_FAILED,
"device %s has 'keep-configuration=no'",
nm_device_get_iface(device));
}
nm_device_assume_state_get(device, &assume_state_guess_assume, &assume_state_connection_uuid);
/* Now we need to compare the generated connection to each configured
* connection. The comparison function is the heart of the connection
* assumption implementation and it must compare the connections very
* carefully to sort out various corner cases. Also, the comparison is
* not entirely symmetric.
*
* When no configured connection matches the generated connection, we keep
* the generated connection instead.
*/
/* If the device state file indicates a connection that was active before NM
* restarted, perform basic sanity checks on it. */
if (assume_state_connection_uuid
&& (connection_checked =
nm_settings_get_connection_by_uuid(priv->settings, assume_state_connection_uuid))
@ -2692,8 +2706,9 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat
gs_free NMSettingsConnection **sett_conns = NULL;
guint len, i, j;
/* the state file doesn't indicate a connection UUID to assume. Search the
* persistent connections for a matching candidate. */
/* @assume_state_guess_assume=TRUE means this is the first start of NM
* and the state file contains no UUID. Search persistent connections
* for a matching candidate. */
sett_conns = nm_manager_get_activatable_connections(self, FALSE, FALSE, &len);
if (len > 0) {
for (i = 0, j = 0; i < len; i++) {
@ -2766,6 +2781,8 @@ get_existing_connection(NMManager *self, NMDevice *device, gboolean *out_generat
return matched;
}
/* When no configured connection matches the generated connection, we keep
* the generated connection instead. */
_LOG2D(LOGD_DEVICE,
device,
"assume: generated connection '%s' (%s)",

View file

@ -498,6 +498,8 @@ _startup_complete_check_is_ready(NMSettings * self,
conn = nm_settings_connection_get_connection(sett_conn);
nm_manager_for_each_device (priv->manager, device, tmp_lst) {
gs_free_error GError *error = NULL;
if (!nm_device_is_real(device))
continue;
@ -508,7 +510,13 @@ _startup_complete_check_is_ready(NMSettings * self,
continue;
}
if (!nm_device_check_connection_compatible(device, conn, NULL))
/* Check that device is compatible with the device. We are also happy
* with a device compatible but for which the connection is disallowed
* by NM configuration. */
if (!nm_device_check_connection_compatible(device, conn, &error)
&& !g_error_matches(error,
NM_UTILS_ERROR,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_DISALLOWED))
continue;
return TRUE;

View file

@ -62,6 +62,8 @@
#define NM_CONFIG_KEYFILE_KEY_DEVICE_MANAGED "managed"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_IGNORE_CARRIER "ignore-carrier"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_SRIOV_NUM_VFS "sriov-num-vfs"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_KEEP_CONFIGURATION "keep-configuration"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_ALLOWED_CONNECTIONS "allowed-connections"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_BACKEND "wifi.backend"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_RAND_MAC_ADDRESS "wifi.scan-rand-mac-address"
#define NM_CONFIG_KEYFILE_KEY_DEVICE_WIFI_SCAN_GENERATE_MAC_ADDRESS_MASK \

View file

@ -60,6 +60,7 @@
#include "nm-setting-team-port.h"
#include "nm-setting-team.h"
#include "nm-setting-tun.h"
#include "nm-setting-user.h"
#include "nm-setting-veth.h"
#include "nm-setting-vlan.h"
#include "nm-setting-vpn.h"
@ -79,6 +80,10 @@
#include "nm-vpn-editor-plugin.h"
#include "libnm-core-aux-intern/nm-libnm-core-utils.h"
#define NM_USER_TAG_ORIGIN "org.freedesktop.NetworkManager.origin"
/*****************************************************************************/
/* NM_SETTING_COMPARE_FLAG_INFERRABLE: check whether a device-generated
* connection can be replaced by a already-defined connection. This flag only
* takes into account properties marked with the %NM_SETTING_PARAM_INFERRABLE

View file

@ -1258,6 +1258,7 @@ typedef enum {
NM_UTILS_ERROR_CONNECTION_AVAILABLE_INCOMPATIBLE,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_UNMANAGED_DEVICE,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_TEMPORARY,
NM_UTILS_ERROR_CONNECTION_AVAILABLE_DISALLOWED,
NM_UTILS_ERROR_SETTING_MISSING,

View file

@ -35,6 +35,14 @@ output_conn(gpointer key, gpointer value, gpointer user_data)
gs_free char * data = NULL;
gs_free_error GError *error = NULL;
gsize len;
NMSetting * setting;
setting = nm_setting_user_new();
nm_connection_add_setting(connection, setting);
nm_setting_user_set_data(NM_SETTING_USER(setting),
NM_USER_TAG_ORIGIN,
"nm-initrd-generator",
NULL);
if (!nm_connection_normalize(connection, NULL, NULL, &error))
goto err_out;