Merge "netutils: Use ethtool ioctl to get permanent mac address"

This commit is contained in:
Zuul 2025-05-07 21:53:20 +00:00 committed by Gerrit Code Review
commit b51cc75ff3
3 changed files with 118 additions and 14 deletions

View File

@ -34,6 +34,13 @@ LLDP_ETHERTYPE = 0x88cc
IFF_PROMISC = 0x100
SIOCGIFFLAGS = 0x8913
SIOCSIFFLAGS = 0x8914
# SIOCETHTOOL from linux/sockios.h
SIOCETHTOOL = 0x8946
# ETHTOOL_GPERMADDR from linux/ethtool.h
ETHTOOL_GPERMADDR = 0x00000020
# MAX_ADDR_LEN from linux/netdevice.h
MAX_ADDR_LEN = 32
INFINIBAND_ADDR_LEN = 59
# LLDP definitions needed to extract vlan information
@ -45,10 +52,25 @@ dot1_VLAN_NAME = "03"
VLAN_ID_LEN = len(LLDP_802dot1_OUI + dot1_VLAN_NAME)
class ethtoolPermAddr(ctypes.Structure):
"""Class for getting interface permanent MAC address"""
_fields_ = [("cmd", ctypes.c_uint32),
("size", ctypes.c_uint32),
("data", ctypes.c_uint8 * MAX_ADDR_LEN)]
class ifreq_data(ctypes.Union):
_fields_ = [("ifr_flags", ctypes.c_short),
(
"ifr_data_ethtool_perm_addr",
ctypes.POINTER(ethtoolPermAddr))]
class ifreq(ctypes.Structure):
"""Class for setting flags on a socket."""
"""Class for ioctl on socket."""
_anonymous_ = ("ifr_data",)
_fields_ = [("ifr_ifrn", ctypes.c_char * 16),
("ifr_flags", ctypes.c_short)]
("ifr_data", ifreq_data)]
class RawPromiscuousSockets(object):
@ -236,6 +258,23 @@ def get_ipv6_addr(interface_id):
def get_mac_addr(interface_id):
"""Retrieve permanent mac address, if unable to fallback to default one"""
try:
data = ethtoolPermAddr(cmd=ETHTOOL_GPERMADDR, size=MAX_ADDR_LEN)
ifr = ifreq(ifr_ifrn=interface_id.encode())
ifr.ifr_data_ethtool_perm_addr = ctypes.pointer(data)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
fcntl.ioctl(sock.fileno(), SIOCETHTOOL, ifr)
# if not full of zeros
if any(data.data[:data.size]):
# kernel updates size to actual address size during ioctl call
permaddr = [f'{b:02x}' for b in data.data[:data.size]]
return ':'.join(permaddr)
except OSError:
pass
LOG.warning("Failed to get permanent mac address for interface %s, "
"falling back to default mac address",
interface_id)
return get_default_ip_addr(socket.AF_PACKET, interface_id)

View File

@ -6279,6 +6279,7 @@ class TestCollectSystemLogs(base.IronicAgentTest):
FakeAddr = namedtuple('FakeAddr', ('family', 'address'))
@mock.patch.object(netutils, 'get_mac_addr', autospec=True)
@mock.patch.object(hardware.GenericHardwareManager, '_get_system_lshw_dict',
autospec=True, return_value={'id': 'host'})
@mock.patch.object(hardware, 'get_managers', autospec=True,
@ -6303,7 +6304,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
mocked_lshw.return_value = json.loads(hws.LSHW_JSON_OUTPUT_V2[0])
mocked_listdir.return_value = ['lo', 'eth0', 'foobar']
mocked_exists.side_effect = [False, False, True, True]
@ -6327,6 +6329,10 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_INET6, 'fd00:1000::101')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'eth0': '00:0c:29:8c:11:b1',
}.get(iface)
mocked_execute.return_value = ('em0\n', '')
mock_has_carrier.return_value = True
interfaces = self.hardware.list_network_interfaces()
@ -6348,7 +6354,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
mocked_listdir.return_value = ['lo', 'eth0']
mocked_exists.side_effect = [False, False, True]
mocked_open.return_value.__enter__ = lambda s: s
@ -6367,6 +6374,10 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:b1')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'eth0': '00:0c:29:8c:11:b1',
}.get(iface)
mocked_execute.return_value = ('em0\n', '')
mock_has_carrier.return_value = True
interfaces = self.hardware.list_network_interfaces()
@ -6390,7 +6401,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
CONF.set_override('collect_lldp', True)
mocked_listdir.return_value = ['lo', 'eth0']
mocked_exists.side_effect = [False, False, True]
@ -6410,6 +6422,10 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:b1')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'eth0': '00:0c:29:8c:11:b1',
}.get(iface)
mocked_lldp_info.return_value = {'eth0': [
(0, b''),
(1, b'\x04\x88Z\x92\xecTY'),
@ -6444,7 +6460,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
CONF.set_override('collect_lldp', True)
mocked_listdir.return_value = ['lo', 'eth0']
mocked_exists.side_effect = [False, False, True]
@ -6464,6 +6481,10 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:b1')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'eth0': '00:0c:29:8c:11:b1',
}.get(iface)
mocked_lldp_info.side_effect = Exception('Boom!')
mocked_execute.return_value = ('em0\n', '')
mock_has_carrier.return_value = True
@ -6485,7 +6506,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
mockedget_managers.return_value = [hardware.GenericHardwareManager()]
mocked_listdir.return_value = ['lo', 'eth0']
@ -6506,6 +6528,10 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:b1')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'eth0': '00:0c:29:8c:11:b1',
}.get(iface)
mocked_execute.return_value = ('em0\n', '')
mock_has_carrier.return_value = False
interfaces = self.hardware.list_network_interfaces()
@ -6526,7 +6552,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
mocked_listdir.return_value = ['lo', 'eth0']
mocked_exists.side_effect = [False, False, True]
mocked_open.return_value.__enter__ = lambda s: s
@ -6546,6 +6573,10 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:b1')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'eth0': '00:0c:29:8c:11:b1',
}.get(iface)
mocked_execute.return_value = ('em0\n', '')
mock_has_carrier.return_value = True
interfaces = self.hardware.list_network_interfaces()
@ -6567,7 +6598,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
mocked_listdir.return_value = ['lo', 'bond0']
mocked_exists.side_effect = [False, False, True]
mocked_open.return_value.__enter__ = lambda s: s
@ -6586,6 +6618,10 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:b1')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'bond0': '00:0c:29:8c:11:b1',
}.get(iface)
mocked_execute.return_value = ('\n', '')
mock_has_carrier.return_value = True
interfaces = self.hardware.list_network_interfaces()
@ -6610,7 +6646,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
mocked_listdir.return_value = ['lo', 'eth0']
mocked_exists.side_effect = [False, False, True]
mocked_open.return_value.__enter__ = lambda s: s
@ -6629,6 +6666,10 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:b1')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'eth0': '00:0c:29:8c:11:b1',
}.get(iface)
mocked_execute.return_value = ('em0\n', '')
mock_has_carrier.return_value = True
mock_get_pci.return_value = '0000:02:00.0'
@ -6654,7 +6695,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
CONF.set_override('enable_vlan_interfaces', 'eth0.100')
mocked_listdir.return_value = ['lo', 'eth0']
mocked_exists.side_effect = [False, False, True]
@ -6679,6 +6721,11 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:b1')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'eth0': '00:0c:29:8c:11:b1',
'eth0.100': '00:0c:29:8c:11:b1',
}.get(iface)
mocked_execute.return_value = ('em0\n', '')
mock_has_carrier.return_value = True
interfaces = self.hardware.list_network_interfaces()
@ -6702,7 +6749,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
CONF.set_override('collect_lldp', True)
CONF.set_override('enable_vlan_interfaces', 'eth0')
mocked_listdir.return_value = ['lo', 'eth0']
@ -6734,6 +6782,12 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
FakeAddr(socket.AF_PACKET, '00:0c:29:8c:11:c2')
]
}
mocked_get_mac_addr.side_effect = lambda iface: {
'lo': '00:00:00:00:00:00',
'eth0': '00:0c:29:8c:11:b1',
'eth0.100': '00:0c:29:8c:11:c1',
'eth0.101': '00:0c:29:8c:11:c2',
}.get(iface)
mocked_lldp_info.return_value = {'eth0': [
(0, b''),
(127, b'\x00\x80\xc2\x03\x00d\x08vlan-100'),
@ -6767,7 +6821,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
CONF.set_override('collect_lldp', True)
CONF.set_override('enable_vlan_interfaces', 'enp0s1')
mocked_listdir.return_value = ['lo', 'eth0']
@ -6805,7 +6860,8 @@ class TestListNetworkInterfaces(base.IronicAgentTest):
mocked_listdir,
mocked_net_if_addrs,
mockedget_managers,
mocked_lshw):
mocked_lshw,
mocked_get_mac_addr):
CONF.set_override('collect_lldp', True)
CONF.set_override('enable_vlan_interfaces', 'all')
mocked_listdir.return_value = ['lo', 'eth0', 'eth1']

View File

@ -0,0 +1,9 @@
---
fixes:
- |
Fixes IPA collecting the effective MAC address of NICs instead of the
pesistent MAC address. In case it fails to fetch the persistent address
falls back to effective MAC address.
See https://bugs.launchpad.net/ironic-python-agent/+bug/2103450 for
details.