Skip to content

Commit cf56080

Browse files
fix: Refine MAC validation using interface name
When a user provides both an interface name and a MAC address, the current validation process retrieves sysfs link info separately using the interface name and the MAC address, then compares the results. If the information doesn't match, an error is raised. However, this approach may trigger false alarms because retrieving the link info by MAC might return data that only matches the current MAC instead of the permanent MAC. Since the interface name is unique within the kernel, a more robust validation method is to fetch the MAC address using the interface name and then compare it directly with the user-provided MAC address. Signed-off-by: Wen Liang <[email protected]>
1 parent 52c2f77 commit cf56080

File tree

5 files changed

+129
-8
lines changed

5 files changed

+129
-8
lines changed

library/network_connections.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,20 @@ def _link_read_address(ifname):
167167
c = SysUtil._sysctl_read("/sys/class/net/" + ifname + "/address")
168168
return Util.mac_norm(c.strip())
169169

170+
@staticmethod
171+
def _link_read_bond_port_perm_hwaddr(ifname):
172+
filename = os.path.join(
173+
"/sys/class/net",
174+
ifname,
175+
# wokeignore:rule=slave
176+
"bonding_slave",
177+
"perm_hwaddr",
178+
)
179+
if not os.path.exists(filename):
180+
return None
181+
c = SysUtil._sysctl_read(filename)
182+
return Util.mac_norm(c.strip())
183+
170184
@staticmethod
171185
def _link_read_permaddress(ifname):
172186
return ethtool.get_perm_addr(ifname)
@@ -187,6 +201,13 @@ def _link_infos_fetch():
187201
"ifname": ifname,
188202
"address": SysUtil._link_read_address(ifname),
189203
"perm-address": SysUtil._link_read_permaddress(ifname),
204+
# When an interface is added as a port of a bonding device, its MAC
205+
# address might change, we need to retrieve and preserve the original
206+
# MAC address to ensure the user-provided interface name and MAC match
207+
# correctly.
208+
"bond-port-perm-hwaddr": SysUtil._link_read_bond_port_perm_hwaddr(
209+
ifname
210+
),
190211
}
191212
return links
192213

@@ -232,6 +253,7 @@ def link_info_find(cls, mac=None, ifname=None):
232253
for linkinfo in cls.link_infos().values():
233254
perm_address = linkinfo.get("perm-address", NULL_MAC)
234255
current_address = linkinfo.get("address", NULL_MAC)
256+
bond_port_perm_hwaddr = linkinfo.get("bond-port-perm-hwaddr", NULL_MAC)
235257

236258
if ifname and ifname == linkinfo["ifname"]:
237259
result = linkinfo
@@ -241,7 +263,7 @@ def link_info_find(cls, mac=None, ifname=None):
241263
if mac == perm_address:
242264
result = linkinfo
243265
break
244-
elif mac == current_address:
266+
elif mac in (current_address, bond_port_perm_hwaddr):
245267
result = linkinfo
246268

247269
return result
@@ -2180,13 +2202,23 @@ def run_prepare(self):
21802202
"infiniband interface exists"
21812203
% (connection["interface_name"]),
21822204
)
2183-
if li_mac and li_ifname and li_mac != li_ifname:
2184-
self.log_fatal(
2185-
idx,
2186-
"profile specifies interface_name '%s' and mac '%s' but no "
2187-
"such interface exists"
2188-
% (connection["interface_name"], connection["mac"]),
2189-
)
2205+
elif connection["mac"]:
2206+
perm_address = li_ifname.get("perm-address", NULL_MAC)
2207+
current_address = li_ifname.get("address", NULL_MAC)
2208+
bond_port_perm_hwaddr = li_ifname.get(
2209+
"bond-port-perm-hwaddr", NULL_MAC
2210+
)
2211+
if (perm_address not in (NULL_MAC, connection["mac"])) or (
2212+
perm_address == NULL_MAC
2213+
and connection["mac"]
2214+
not in (current_address, bond_port_perm_hwaddr)
2215+
):
2216+
self.log_fatal(
2217+
idx,
2218+
"profile specifies interface_name '%s' and mac '%s' "
2219+
"but no such interface exists"
2220+
% (connection["interface_name"], connection["mac"]),
2221+
)
21902222

21912223
def start_transaction(self):
21922224
"""Hook before making changes"""

tests/ensure_provider_tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
},
7575
"playbooks/tests_ignore_auto_dns.yml": {},
7676
"playbooks/tests_bond_options.yml": {},
77+
"playbooks/tests_bond_port_match_by_mac.yml": {},
7778
"playbooks/tests_eth_dns_support.yml": {},
7879
"playbooks/tests_dummy.yml": {}, # wokeignore:rule=dummy
7980
"playbooks/tests_infiniband.yml": {},
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
---
3+
- name: Play for creating the connection to match the port device
4+
based on the perm_hwaddr
5+
hosts: all
6+
vars:
7+
controller_profile: bond0
8+
controller_device: nm-bond
9+
port1_profile: bond0.0
10+
dhcp_interface1: test1
11+
port2_profile: bond0.1
12+
dhcp_interface2: test2
13+
profile: test2conn
14+
interface: test2
15+
tasks:
16+
- name: Test creating the connection to match the port device
17+
based on the perm_hwaddr
18+
tags:
19+
- tests::bond:create
20+
block:
21+
- name: Include the task 'run_test.yml'
22+
include_tasks: tasks/run_test.yml
23+
vars:
24+
lsr_description: Given two DHCP-enabled network interfaces,
25+
when creating a bond profile with them,
26+
then we can still create the connection to match the port device
27+
based on the perm_hwaddr
28+
lsr_setup:
29+
- tasks/create_test_interfaces_with_dhcp.yml
30+
- tasks/assert_dhcp_device_present.yml
31+
lsr_test:
32+
- tasks/create_bond_profile.yml
33+
- tasks/create_bond_port_match_by_mac.yml
34+
lsr_assert:
35+
- tasks/assert_controller_device_present.yml
36+
- tasks/assert_profile_present.yml
37+
lsr_cleanup:
38+
- tasks/cleanup_bond_profile+device.yml
39+
- tasks/remove_test_interfaces_with_dhcp.yml
40+
- tasks/check_network_dns.yml
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
---
3+
- name: Retrieve perm_hwaddr using ethtool
4+
# wokeignore:rule=slave
5+
command: cat /sys/class/net/{{ interface }}/bonding_slave/perm_hwaddr
6+
register: mac_address_result
7+
changed_when: false
8+
failed_when: mac_address_result.rc != 0
9+
- name: Set the MAC address variable
10+
set_fact:
11+
mac: "{{ mac_address_result.stdout_lines[-1].split(' ')[-1] }}"
12+
- name: Display the retrieved MAC address
13+
debug:
14+
msg: "Retrieved MAC address for {{ interface }}: {{ mac }}"
15+
- name: Test matching the port device based on the perm_hwaddr
16+
import_role:
17+
name: linux-system-roles.network
18+
vars:
19+
network_connections:
20+
- name: "{{ profile }}"
21+
state: up
22+
type: ethernet
23+
interface_name: "{{ interface }}"
24+
mac: "{{ mac }}"
25+
...
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# SPDX-License-Identifier: BSD-3-Clause
2+
# This file was generated by ensure_provider_tests.py
3+
---
4+
# set network provider and gather facts
5+
# yamllint disable rule:line-length
6+
- name: Run playbook 'playbooks/tests_bond_port_match_by_mac.yml' with nm as provider
7+
hosts: all
8+
tasks:
9+
- name: Include the task 'el_repo_setup.yml'
10+
include_tasks: tasks/el_repo_setup.yml
11+
- name: Set network provider to 'nm'
12+
set_fact:
13+
network_provider: nm
14+
tags:
15+
- always
16+
17+
18+
# The test requires or should run with NetworkManager, therefore it cannot run
19+
# on RHEL/CentOS 6
20+
- name: Import the playbook 'playbooks/tests_bond_port_match_by_mac.yml'
21+
import_playbook: playbooks/tests_bond_port_match_by_mac.yml
22+
when:
23+
- ansible_distribution_major_version != '6'

0 commit comments

Comments
 (0)