Skip to content

Commit

Permalink
networkmanager: add support for stable-ssid MAC option
Browse files Browse the repository at this point in the history
802-11-wireless.cloned-mac-address = stable-ssid was added in Network
Manager 1.46 and is available through the GUI applet.

tests: add integration test for stable-ssid

In order to create a predictable MAC address we need to set a fixed
/etc/machine-id and /var/lib/NetworkManager/secret_key.
Note that this test is currently unstable and skipped by default.
  • Loading branch information
daniloegea committed Oct 14, 2024
1 parent cfac5eb commit c53dfcd
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 7 deletions.
2 changes: 1 addition & 1 deletion doc/netplan-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ Match devices by MAC when setting options like: `wakeonlan` or `*-offload`.
> "XX:XX:XX:XX:XX:XX". The following special options are also accepted:
> `permanent` and `random`.
> In addition to these options, the NetworkManager renderer also accepts
> `stable` and `preserve`.
> `stable`, `stable-ssid` (Wi-Fi only) and `preserve`.
>
> **Note:** This will not work reliably for devices matched by name
> only and rendered by networkd, due to interactions with device
Expand Down
4 changes: 2 additions & 2 deletions src/parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ handle_special_macaddress_option(NetplanParser* npp, yaml_node_t* node, void* en
g_assert(entryptr != NULL);
g_assert(node->type == YAML_SCALAR_NODE);

if (!_is_macaddress_special_nm_option(scalar(node)) &&
if (!_is_macaddress_special_nm_option(npp->current.netdef, scalar(node)) &&
!_is_macaddress_special_nd_option(scalar(node)))
return FALSE;

Expand Down Expand Up @@ -695,7 +695,7 @@ handle_netdef_set_mac(NetplanParser* npp, yaml_node_t* node, const void* data, G
if (!handle_special_macaddress_option(npp, node, npp->current.netdef, data, NULL)) {
return yaml_error(npp, node, error,
"Invalid MAC address '%s', must be XX:XX:XX:XX:XX:XX, XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
" or one of 'permanent', 'random', 'stable', 'preserve'.",
" or one of 'permanent', 'random', 'stable', 'preserve', 'stable-ssid' (Wi-Fi only).",
scalar(node));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/util-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ gboolean
_is_auth_key_management_psk(const NetplanAuthenticationSettings* auth);

gboolean
_is_macaddress_special_nm_option(const char* value);
_is_macaddress_special_nm_option(const NetplanNetDefinition* netdef, const char* value);

gboolean
_is_macaddress_special_nd_option(const char* value);
Expand Down
5 changes: 3 additions & 2 deletions src/util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1239,12 +1239,13 @@ _is_auth_key_management_psk(const NetplanAuthenticationSettings* auth)
}

gboolean
_is_macaddress_special_nm_option(const char* value)
_is_macaddress_special_nm_option(const NetplanNetDefinition* netdef, const char* value)
{
return ( !g_strcmp0(value, "preserve")
|| !g_strcmp0(value, "permanent")
|| !g_strcmp0(value, "random")
|| !g_strcmp0(value, "stable"));
|| !g_strcmp0(value, "stable")
|| (!g_strcmp0(value, "stable-ssid") && netdef->type == NETPLAN_DEF_TYPE_WIFI));
}

gboolean
Expand Down
16 changes: 15 additions & 1 deletion tests/generator/test_ethernets.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,7 +504,21 @@ def test_eth_set_mac_special_values_error(self):

error = ("Invalid MAC address 'preservetypo', must be XX:XX:XX:XX:XX:XX, "
"XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX or "
"one of 'permanent', 'random', 'stable', 'preserve'")
"one of 'permanent', 'random', 'stable', 'preserve', 'stable-ssid' (Wi-Fi only)")
self.assertIn(error, res)

def test_eth_set_mac_special_values_ethernet_stable_ssid(self):
res = self.generate('''network:
version: 2
renderer: NetworkManager
ethernets:
eth0:
macaddress: stable-ssid
dhcp4: true''', expect_fail=True)

error = ("Invalid MAC address 'stable-ssid', must be XX:XX:XX:XX:XX:XX, "
"XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX or "
"one of 'permanent', 'random', 'stable', 'preserve', 'stable-ssid' (Wi-Fi only)")
self.assertIn(error, res)

def test_eth_match_by_driver(self):
Expand Down
36 changes: 36 additions & 0 deletions tests/generator/test_wifis.py
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,42 @@ def test_wifi_regdom(self):
new_config = f.read()
self.assertIn('ExecStart=/usr/sbin/iw reg set DE\n', new_config)

def test_wlan_set_mac_special_values(self):
self.generate('''network:
version: 2
renderer: NetworkManager
wifis:
wlan0:
macaddress: stable-ssid
dhcp4: true
access-points:
"mynetwork":
password: mypassword''')

self.assert_networkd(None)

self.assert_nm({'wlan0-mynetwork': '''[connection]
id=netplan-wlan0-mynetwork
type=wifi
interface-name=wlan0
[wifi]
cloned-mac-address=stable-ssid
ssid=mynetwork
mode=infrastructure
[ipv4]
method=auto
[ipv6]
method=ignore
[wifi-security]
key-mgmt=wpa-psk
pmf=2
psk=mypassword
'''})


class TestConfigErrors(TestBase):

Expand Down
40 changes: 40 additions & 0 deletions tests/integration/wifi.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,45 @@ def test_wifi_ap_open(self):
self.assertIn('ssid fake net', out)
self.assert_iface_up(self.dev_w_ap, ['inet 10.'])

@unittest.skip("Test if flaky. NM might generate a different MAC address.")
def test_wifi_cloned_macaddress_stable_ssid(self):
self.setup_ap('''hw_mode=g
channel=1
ssid=fake net
wpa=1
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
wpa_passphrase=12345678
''', None)

with open(self.config, 'w') as f:
f.write('''network:
renderer: NetworkManager
wifis:
%(wc)s:
addresses: ["192.168.1.42/24"]
dhcp4: false
dhcp6: false
macaddress: stable-ssid
access-points:
"fake net":
password: 12345678''' % {'wc': self.dev_w_client})

subprocess.check_call(['systemctl', 'start', 'NetworkManager'])

# Make the generated MAC address predictable
# See nm_utils_hw_addr_gen_stable_eth() in NM for details
# TODO: save and restore these files to avoid any impact on the
# entire test suite.
with open('/etc/machine-id', 'w') as f:
f.write('ee7ac3602b6306061bd984a41eb1c045\n')
with open('/var/lib/NetworkManager/secret_key', 'w') as f:
f.write('nm-v2:hnIHoHp4p9kaEWU5/+dO+gFREirN1AsMoO1MPaoYxCc=')

subprocess.check_call(['systemctl', 'restart', 'NetworkManager'])

self.generate_and_settle([self.state_up(self.dev_w_client)])
self.assert_iface_up(self.dev_w_client, ['ether 5e:ba:fe:fd:89:03'])


unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))

0 comments on commit c53dfcd

Please sign in to comment.