Skip to content

Commit 83a8d8c

Browse files
committed
networkmanager: add support for stable-ssid MAC option
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.
1 parent cfac5eb commit 83a8d8c

File tree

7 files changed

+98
-7
lines changed

7 files changed

+98
-7
lines changed

doc/netplan-yaml.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -499,7 +499,7 @@ Match devices by MAC when setting options like: `wakeonlan` or `*-offload`.
499499
> "XX:XX:XX:XX:XX:XX". The following special options are also accepted:
500500
> `permanent` and `random`.
501501
> In addition to these options, the NetworkManager renderer also accepts
502-
> `stable` and `preserve`.
502+
> `stable`, `stable-ssid` (Wi-Fi only) and `preserve`.
503503
>
504504
> **Note:** This will not work reliably for devices matched by name
505505
> only and rendered by networkd, due to interactions with device

src/parse.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ handle_special_macaddress_option(NetplanParser* npp, yaml_node_t* node, void* en
392392
g_assert(entryptr != NULL);
393393
g_assert(node->type == YAML_SCALAR_NODE);
394394

395-
if (!_is_macaddress_special_nm_option(scalar(node)) &&
395+
if (!_is_macaddress_special_nm_option(npp->current.netdef, scalar(node)) &&
396396
!_is_macaddress_special_nd_option(scalar(node)))
397397
return FALSE;
398398

@@ -695,7 +695,7 @@ handle_netdef_set_mac(NetplanParser* npp, yaml_node_t* node, const void* data, G
695695
if (!handle_special_macaddress_option(npp, node, npp->current.netdef, data, NULL)) {
696696
return yaml_error(npp, node, error,
697697
"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"
698-
" or one of 'permanent', 'random', 'stable', 'preserve'.",
698+
" or one of 'permanent', 'random', 'stable', 'preserve', 'stable-ssid' (Wi-Fi only).",
699699
scalar(node));
700700
}
701701
}

src/util-internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ gboolean
136136
_is_auth_key_management_psk(const NetplanAuthenticationSettings* auth);
137137

138138
gboolean
139-
_is_macaddress_special_nm_option(const char* value);
139+
_is_macaddress_special_nm_option(const NetplanNetDefinition* netdef, const char* value);
140140

141141
gboolean
142142
_is_macaddress_special_nd_option(const char* value);

src/util.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,12 +1239,13 @@ _is_auth_key_management_psk(const NetplanAuthenticationSettings* auth)
12391239
}
12401240

12411241
gboolean
1242-
_is_macaddress_special_nm_option(const char* value)
1242+
_is_macaddress_special_nm_option(const NetplanNetDefinition* netdef, const char* value)
12431243
{
12441244
return ( !g_strcmp0(value, "preserve")
12451245
|| !g_strcmp0(value, "permanent")
12461246
|| !g_strcmp0(value, "random")
1247-
|| !g_strcmp0(value, "stable"));
1247+
|| !g_strcmp0(value, "stable")
1248+
|| (!g_strcmp0(value, "stable-ssid") && netdef->type == NETPLAN_DEF_TYPE_WIFI));
12481249
}
12491250

12501251
gboolean

tests/generator/test_ethernets.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,21 @@ def test_eth_set_mac_special_values_error(self):
504504

505505
error = ("Invalid MAC address 'preservetypo', must be XX:XX:XX:XX:XX:XX, "
506506
"XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX or "
507-
"one of 'permanent', 'random', 'stable', 'preserve'")
507+
"one of 'permanent', 'random', 'stable', 'preserve', 'stable-ssid' (Wi-Fi only)")
508+
self.assertIn(error, res)
509+
510+
def test_eth_set_mac_special_values_ethernet_stable_ssid(self):
511+
res = self.generate('''network:
512+
version: 2
513+
renderer: NetworkManager
514+
ethernets:
515+
eth0:
516+
macaddress: stable-ssid
517+
dhcp4: true''', expect_fail=True)
518+
519+
error = ("Invalid MAC address 'stable-ssid', must be XX:XX:XX:XX:XX:XX, "
520+
"XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX or "
521+
"one of 'permanent', 'random', 'stable', 'preserve', 'stable-ssid' (Wi-Fi only)")
508522
self.assertIn(error, res)
509523

510524
def test_eth_match_by_driver(self):

tests/generator/test_wifis.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,42 @@ def test_wifi_regdom(self):
11141114
new_config = f.read()
11151115
self.assertIn('ExecStart=/usr/sbin/iw reg set DE\n', new_config)
11161116

1117+
def test_wlan_set_mac_special_values(self):
1118+
self.generate('''network:
1119+
version: 2
1120+
renderer: NetworkManager
1121+
wifis:
1122+
wlan0:
1123+
macaddress: stable-ssid
1124+
dhcp4: true
1125+
access-points:
1126+
"mynetwork":
1127+
password: mypassword''')
1128+
1129+
self.assert_networkd(None)
1130+
1131+
self.assert_nm({'wlan0-mynetwork': '''[connection]
1132+
id=netplan-wlan0-mynetwork
1133+
type=wifi
1134+
interface-name=wlan0
1135+
1136+
[wifi]
1137+
cloned-mac-address=stable-ssid
1138+
ssid=mynetwork
1139+
mode=infrastructure
1140+
1141+
[ipv4]
1142+
method=auto
1143+
1144+
[ipv6]
1145+
method=ignore
1146+
1147+
[wifi-security]
1148+
key-mgmt=wpa-psk
1149+
pmf=2
1150+
psk=mypassword
1151+
'''})
1152+
11171153

11181154
class TestConfigErrors(TestBase):
11191155

tests/integration/wifi.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,45 @@ def test_wifi_ap_open(self):
181181
self.assertIn('ssid fake net', out)
182182
self.assert_iface_up(self.dev_w_ap, ['inet 10.'])
183183

184+
@unittest.skip("Test if flaky. NM might generate a different MAC address.")
185+
def test_wifi_cloned_macaddress_stable_ssid(self):
186+
self.setup_ap('''hw_mode=g
187+
channel=1
188+
ssid=fake net
189+
wpa=1
190+
wpa_key_mgmt=WPA-PSK
191+
wpa_pairwise=TKIP
192+
wpa_passphrase=12345678
193+
''', None)
194+
195+
with open(self.config, 'w') as f:
196+
f.write('''network:
197+
renderer: NetworkManager
198+
wifis:
199+
%(wc)s:
200+
addresses: ["192.168.1.42/24"]
201+
dhcp4: false
202+
dhcp6: false
203+
macaddress: stable-ssid
204+
access-points:
205+
"fake net":
206+
password: 12345678''' % {'wc': self.dev_w_client})
207+
208+
subprocess.check_call(['systemctl', 'start', 'NetworkManager'])
209+
210+
# Make the generated MAC address predictable
211+
# See nm_utils_hw_addr_gen_stable_eth() in NM for details
212+
# TODO: save and restore these files to avoid any impact on the
213+
# entire test suite.
214+
with open('/etc/machine-id', 'w') as f:
215+
f.write('ee7ac3602b6306061bd984a41eb1c045\n')
216+
with open('/var/lib/NetworkManager/secret_key', 'w') as f:
217+
f.write('nm-v2:hnIHoHp4p9kaEWU5/+dO+gFREirN1AsMoO1MPaoYxCc=')
218+
219+
subprocess.check_call(['systemctl', 'restart', 'NetworkManager'])
220+
221+
self.generate_and_settle([self.state_up(self.dev_w_client)])
222+
self.assert_iface_up(self.dev_w_client, ['ether 5e:ba:fe:fd:89:03'])
223+
184224

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

0 commit comments

Comments
 (0)