Skip to content

Commit f26a0ca

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 the link info 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 7cf95af commit f26a0ca

File tree

5 files changed

+127
-8
lines changed

5 files changed

+127
-8
lines changed

library/network_connections.py

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

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

@@ -2137,7 +2158,6 @@ def run_prepare(self):
21372158
# existing interface has the right device type. Also, there is
21382159
# some ambiguity between the current MAC address and the
21392160
# permanent MAC address.
2140-
li_mac = None
21412161
li_ifname = None
21422162
if connection["interface_name"]:
21432163
li_ifname = SysUtil.link_info_find(
@@ -2158,13 +2178,23 @@ def run_prepare(self):
21582178
"infiniband interface exists"
21592179
% (connection["interface_name"]),
21602180
)
2161-
if li_mac and li_ifname and li_mac != li_ifname:
2162-
self.log_fatal(
2163-
idx,
2164-
"profile specifies interface_name '%s' and mac '%s' but no "
2165-
"such interface exists"
2166-
% (connection["interface_name"], connection["mac"]),
2167-
)
2181+
elif connection["mac"]:
2182+
perm_address = li_ifname.get("perm-address", NULL_MAC)
2183+
current_address = li_ifname.get("address", NULL_MAC)
2184+
bond_port_perm_hwaddr = li_ifname.get(
2185+
"bond-port-perm-hwaddr", NULL_MAC
2186+
)
2187+
if (perm_address not in (NULL_MAC, connection["mac"])) or (
2188+
perm_address == NULL_MAC
2189+
and connection["mac"]
2190+
not in (current_address, bond_port_perm_hwaddr)
2191+
):
2192+
self.log_fatal(
2193+
idx,
2194+
"profile specifies interface_name '%s' and mac '%s' "
2195+
"but no such interface exists"
2196+
% (connection["interface_name"], connection["mac"]),
2197+
)
21682198

21692199
def start_transaction(self):
21702200
"""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)