device: add spec "driver:" to match devices

Changing the MAC address of devices is known to fail with
certain drivers. Add a device-spec to allow disabling it
for for such devices.

Related: https://bugzilla.gnome.org/show_bug.cgi?id=777523
This commit is contained in:
Thomas Haller 2017-03-17 16:18:48 +01:00
parent 4c6edb22b7
commit b869d9cc0d
7 changed files with 93 additions and 2 deletions

View file

@ -138,6 +138,7 @@ EXTRA_DIST += \
examples/lua/lgi/deactivate-all.lua \
\
examples/nm-conf.d/30-anon.conf \
examples/nm-conf.d/31-mac-addr-change.conf \
\
examples/python/dbus/nm-state.py \
examples/python/dbus/add-connection.py \

View file

@ -0,0 +1,11 @@
# Certain drivers are known not to support changing the MAC address.
# Disable touching the MAC address on such devices.
#
# See man NetworkManager.conf
#
# https://bugzilla.gnome.org/show_bug.cgi?id=777523
[device-mac-addr-change-wifi]
match-device=driver:rtl8723bs,driver:rtl8189es,driver:r8188eu,driver:eagle_sdio
wifi.scan-rand-mac-address=no
wifi.cloned-mac-address=preserve
ethernet.cloned-mac-address=preserve

View file

@ -1172,6 +1172,13 @@ enable=nm-version-min:1.3,nm-version-min:1.2.6,nm-version-min:1.0.16
<listitem><para>Match the device type. Valid type names are as reported by "<literal>nmcli -f GENERAL.TYPE device show</literal>".
Globbing is not supported.</para></listitem>
</varlistentry>
<varlistentry>
<term>driver:DRIVER</term>
<listitem><para>Match the device driver as reported by "<literal>nmcli -f GENERAL.DRIVER,GENERAL.DRIVER-VERSION device show</literal>".
"<literal>DRIVER</literal>" must match the driver name exactly and does not support globbing.
Optionally, a driver version may be specified separated by '/'. Globbing is supported for the version.
</para></listitem>
</varlistentry>
<varlistentry>
<term>except:SPEC</term>
<listitem><para>Negative match of a device. <literal>SPEC</literal> must be explicitly qualified with

View file

@ -13193,6 +13193,8 @@ nm_device_spec_match_list (NMDevice *self, const GSList *specs)
m = nm_match_spec_device (specs,
nm_device_get_iface (self),
nm_device_get_type_description (self),
nm_device_get_driver (self),
nm_device_get_driver_version (self),
nm_device_get_permanent_hw_address (self),
klass->get_s390_subchannels ? klass->get_s390_subchannels (self) : NULL);
return m == NM_MATCH_SPEC_MATCH;

View file

@ -1182,6 +1182,7 @@ nm_utils_read_link_absolute (const char *link_file, GError **error)
#define MAC_TAG "mac:"
#define INTERFACE_NAME_TAG "interface-name:"
#define DEVICE_TYPE_TAG "type:"
#define DRIVER_TAG "driver:"
#define SUBCHAN_TAG "s390-subchannels:"
#define EXCEPT_TAG "except:"
#define MATCH_TAG_CONFIG_NM_VERSION "nm-version:"
@ -1192,6 +1193,8 @@ nm_utils_read_link_absolute (const char *link_file, GError **error)
typedef struct {
const char *interface_name;
const char *device_type;
const char *driver;
const char *driver_version;
struct {
const char *value;
gboolean is_parsed;
@ -1374,6 +1377,38 @@ match_device_eval (const char *spec_str,
return FALSE;
}
if (_MATCH_CHECK (spec_str, DRIVER_TAG)) {
const char *t;
if (!match_data->driver)
return FALSE;
/* support:
* 1) "${DRIVER}"
* In this case, DRIVER may not contain a '/' character.
* It matches any driver version.
* 2) "${DRIVER}/${DRIVER_VERSION}"
* In this case, DRIVER may contains '/' but DRIVER_VERSION
* may not. A '/' in DRIVER_VERSION may be replaced by '?'.
*
* It follows, that "${DRIVER}/""*" is like 1), but allows
* '/' inside DRIVER.
*
* The fields match to what `nmcli -f GENERAL.DRIVER,GENERAL.DRIVER-VERSION device show`
* gives. However, DRIVER matches literally, while DRIVER_VERSION is a glob
* supporting ? and *.
*/
t = strrchr (spec_str, '/');
if (!t)
return nm_streq (spec_str, match_data->driver);
return (strncmp (spec_str, match_data->driver, t - spec_str) == 0)
&& g_pattern_match_simple (&t[1],
match_data->driver_version ?: "");
}
if (_MATCH_CHECK (spec_str, SUBCHAN_TAG))
return match_data_s390_subchannels_eval (spec_str, match_data);
@ -1392,6 +1427,8 @@ NMMatchSpecMatchType
nm_match_spec_device (const GSList *specs,
const char *interface_name,
const char *device_type,
const char *driver,
const char *driver_version,
const char *hwaddr,
const char *s390_subchannels)
{
@ -1402,6 +1439,8 @@ nm_match_spec_device (const GSList *specs,
MatchDeviceData match_data = {
.interface_name = interface_name,
.device_type = nm_str_not_empty (device_type),
.driver = nm_str_not_empty (driver),
.driver_version = nm_str_not_empty (driver_version),
.hwaddr = {
.value = hwaddr,
},

View file

@ -142,6 +142,8 @@ typedef enum {
NMMatchSpecMatchType nm_match_spec_device (const GSList *specs,
const char *interface_name,
const char *driver,
const char *driver_version,
const char *device_type,
const char *hwaddr,
const char *s390_subchannels);

View file

@ -986,13 +986,25 @@ test_connection_sort_autoconnect_priority (void)
/*****************************************************************************/
#define MATCH_S390 "S390:"
#define MATCH_DRIVER "DRIVER:"
static NMMatchSpecMatchType
_test_match_spec_device (const GSList *specs, const char *match_str)
{
if (match_str && g_str_has_prefix (match_str, MATCH_S390))
return nm_match_spec_device (specs, NULL, NULL, NULL, &match_str[NM_STRLEN (MATCH_S390)]);
return nm_match_spec_device (specs, match_str, NULL, NULL, NULL);
return nm_match_spec_device (specs, NULL, NULL, NULL, NULL, NULL, &match_str[NM_STRLEN (MATCH_S390)]);
if (match_str && g_str_has_prefix (match_str, MATCH_DRIVER)) {
gs_free char *s = g_strdup (&match_str[NM_STRLEN (MATCH_DRIVER)]);
char *t;
t = strchr (s, '|');
if (t) {
t[0] = '\0';
t++;
}
return nm_match_spec_device (specs, NULL, NULL, s, t, NULL, NULL);
}
return nm_match_spec_device (specs, match_str, NULL, NULL, NULL, NULL, NULL);
}
static void
@ -1142,6 +1154,23 @@ test_match_spec_device (void)
NULL,
S (NULL),
S (MATCH_S390"0.0.1000", MATCH_S390"0.0.1000,deadbeef", MATCH_S390"0.0.1000,0.0.1001", MATCH_S390"0.0.1000,0.0.1002"));
_do_test_match_spec_device ("driver:DRV",
S (MATCH_DRIVER"DRV", MATCH_DRIVER"DRV|1.6"),
S (MATCH_DRIVER"DR", MATCH_DRIVER"DR*"),
NULL);
_do_test_match_spec_device ("driver:DRV//",
S (MATCH_DRIVER"DRV/"),
S (MATCH_DRIVER"DRV/|1.6", MATCH_DRIVER"DR", MATCH_DRIVER"DR*"),
NULL);
_do_test_match_spec_device ("driver:DRV//*",
S (MATCH_DRIVER"DRV/", MATCH_DRIVER"DRV/|1.6"),
S (MATCH_DRIVER"DR", MATCH_DRIVER"DR*"),
NULL);
_do_test_match_spec_device ("driver:DRV//1.5*",
S (MATCH_DRIVER"DRV/|1.5", MATCH_DRIVER"DRV/|1.5.2"),
S (MATCH_DRIVER"DRV/", MATCH_DRIVER"DRV/|1.6", MATCH_DRIVER"DR", MATCH_DRIVER"DR*"),
NULL);
#undef S
}