core: introduce device 'allowed-connections' property

Configuration can have [device*] and [connection*] settings and both
can include a 'match-device=' key, which is a list of device-specs.

Introduce a new 'allowed-connections' key for [device*] sections,
which specifies a list of connection-specs to indicate which
connections can be activated on the device.

With this, it becomes possible to have a device configuration like:

  [device-enp1s0]
  match-device=interface-name:enp1s0
  allowed-connections=except:origin:nm-initrd-generator

so that NM in the real root ignores connections created by the
nm-initrd-generator, and starts activating a persistent
connection. This requires also setting 'keep-configuration=no' to not
generate an assumed connection.
This commit is contained in:
Beniamino Galvani
2021-06-03 09:01:23 +02:00
parent 604c611cd0
commit bace14fe1f
8 changed files with 127 additions and 12 deletions

View File

@@ -1188,6 +1188,25 @@ managed=1
</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>

View File

@@ -7507,6 +7507,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) {
@@ -7583,6 +7585,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;
}

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)
{
@@ -879,6 +887,7 @@ static const ConfigGroup config_groups[] = {
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,
@@ -1060,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

@@ -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

@@ -63,6 +63,7 @@
#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

@@ -1274,6 +1274,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,