ethtool: use GPERMADDR instead the ethtool command line tool

This patch implements the ETHTOOL_GPERMADDR command in order to retrieve
the permanent address from ethtool instead using command line tool.

Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
This commit is contained in:
Fernando Fernandez Mancera 2020-05-13 18:03:36 +02:00 committed by Till Maas
parent 024ba709f6
commit 2e5dd50852
6 changed files with 72 additions and 44 deletions

View file

@ -34,7 +34,7 @@ _network_packages_default_gobject_packages: ["python{{
ansible_python['version']['major'] | replace('2', '')}}-gobject-base"]
network_service_name_default_nm: NetworkManager
network_packages_default_nm: "{{ ['ethtool', 'NetworkManager']
network_packages_default_nm: "{{['NetworkManager']
+ _network_packages_default_gobject_packages|select()|list()
+ _network_packages_default_802_1x|select()|list()}}"
@ -56,8 +56,8 @@ if ansible_distribution in ['RedHat', 'CentOS', 'OracleLinux'] and
# |select() filters the list to include only values that evaluate to true
# (the empty string is false)
# |list() converts the generator that |select() creates to a list
network_packages_default_initscripts: "{{ ['ethtool']
+ _network_packages_default_initscripts_bridge|select()|list()
network_packages_default_initscripts: "{{
_network_packages_default_initscripts_bridge|select()|list()
+ _network_packages_default_initscripts_network_scripts|select()|list()
}}"

View file

@ -13,19 +13,21 @@ import traceback
# pylint: disable=import-error, no-name-in-module
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network_lsr import ethtool
from ansible.module_utils.network_lsr import MyError
# pylint: disable=import-error
from ansible.module_utils.network_lsr.argument_validator import (
ArgUtil,
ArgValidator_ListConnections,
ValidationError,
)
# pylint: disable=import-error
from ansible.module_utils.network_lsr.utils import Util
from ansible.module_utils.network_lsr import nm_provider
# pylint: enable=import-error, no-name-in-module
DOCUMENTATION = """
---
module: network_connections
@ -107,15 +109,7 @@ class SysUtil:
@staticmethod
def _link_read_permaddress(ifname):
try:
out = Util.check_output(["ethtool", "-P", ifname])
except MyError:
return None
m = re.match("^Permanent address: ([0-9A-Fa-f:]*)\n$", out)
if not m:
return None
return Util.mac_norm(m.group(1))
return ethtool.get_perm_addr(ifname)
@staticmethod
def _link_infos_fetch():

View file

@ -0,0 +1,56 @@
# SPDX-License-Identifier: BSD-3-Clause
import array
import struct
import fcntl
import socket
from .utils import Util
ETHTOOL_GPERMADDR = 0x00000020
SIOCETHTOOL = 0x8946
MAX_ADDR_LEN = 32
IFNAMESIZ = 16
def get_perm_addr(ifname):
"""
Return the Permanent address value for the specified interface using the
ETHTOOL_GPERMADDR ioctl command.
Please for further documentation, see:
https://github.com/torvalds/linux/blob/master/include/uapi/linux/ethtool.h#L734
https://github.com/torvalds/linux/blob/master/include/uapi/linux/ethtool.h#L1388
https://git.kernel.org/pub/scm/network/ethtool/ethtool.git/tree/ethtool.c#n4172
"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockfd = sock.fileno()
ifname = ifname.encode("utf-8")
if len(ifname) > IFNAMESIZ:
return None
ecmd = array.array(
"B",
struct.pack(
"II%is" % MAX_ADDR_LEN,
ETHTOOL_GPERMADDR,
MAX_ADDR_LEN,
b"\x00" * MAX_ADDR_LEN,
),
)
ifreq = struct.pack("%isP" % IFNAMESIZ, ifname, ecmd.buffer_info()[0])
fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
try:
res = ecmd.tobytes()
except AttributeError: # tobytes() is not available in python2
res = ecmd.tostring()
_, size, perm_addr = struct.unpack("II%is" % MAX_ADDR_LEN, res)
perm_addr = Util.mac_ntoa(perm_addr[:size])
except IOError:
perm_addr = None
finally:
sock.close()
return perm_addr

View file

@ -2,9 +2,7 @@
# SPDX-License-Identifier: BSD-3-Clause
# vim: fileencoding=utf8
import os
import socket
import subprocess
import sys
import uuid
@ -25,19 +23,6 @@ class Util:
return v
return default
@staticmethod
def check_output(argv):
# subprocess.check_output is python 2.7.
with open("/dev/null", "wb") as DEVNULL:
env = os.environ.copy()
env["LANG"] = "C"
p = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=DEVNULL, env=env)
# FIXME: Can we assume this to always be UTF-8?
out = p.communicate()[0].decode("UTF-8")
if p.returncode != 0:
raise MyError("failure calling %s: exit with %s" % (argv, p.returncode))
return out
@staticmethod
def path_to_glib_bytes(path):
"""
@ -275,7 +260,8 @@ class Util:
def mac_ntoa(mac):
if mac is None:
return None
return ":".join(["%02x" % c for c in mac])
# bytearray() is needed for python2 compatibility
return ":".join(["%02x" % c for c in bytearray(mac)])
@staticmethod
def mac_norm(mac_str, force_len=None):

View file

@ -1,6 +0,0 @@
#! /bin/bash
if [ "${1}" == "-P" ] && [ "${2}" != "" ]
then
echo "Permanent address: 23:00:00:00:00:00"
fi

View file

@ -2503,10 +2503,9 @@ class TestNM(unittest.TestCase):
class TestUtils(unittest.TestCase):
def test_check_output(self):
res = Util.check_output(["echo", "test"])
self.assertEqual(res, "test\n")
self.assertRaises(n.MyError, Util.check_output, ["false"])
def test_mac_ntoa(self):
mac_bytes = b"\xaa\xbb\xcc\xdd\xee\xff"
self.assertEqual(Util.mac_ntoa(mac_bytes), "aa:bb:cc:dd:ee:ff")
def test_convert_passwd_flags_nm(self):
test_cases = [
@ -2528,10 +2527,9 @@ class TestUtils(unittest.TestCase):
class TestSysUtils(unittest.TestCase):
def test_link_read_permaddress(self):
# Manipulate PATH to use ethtool mock script to avoid hard dependency on
# ethtool
os.environ["PATH"] = TESTS_BASEDIR + "/helpers:" + os.environ["PATH"]
self.assertEqual(SysUtil._link_read_permaddress("lo"), "23:00:00:00:00:00")
self.assertEqual(SysUtil._link_read_permaddress("lo"), "00:00:00:00:00:00")
self.assertEqual(SysUtil._link_read_permaddress("fakeiface"), None)
self.assertEqual(SysUtil._link_read_permaddress("morethansixteenchars"), None)
if __name__ == "__main__":