#
# Copyright (c) 2018-2019 Red Hat, Inc.
#
# This file is part of nmstate
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 2.1 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
""" Minimal wrapper around the SIOCETHTOOL IOCTL to provide data that Network
Manager is not yet providing.
https://bugzilla.redhat.com/show_bug.cgi?id=1621188
"""
import array
import struct
import fcntl
import socket
ETHTOOL_GSET = 0x00000001 # Get settings
SIOCETHTOOL = 0x8946
def minimal_ethtool(interface):
"""
Return dictionary with speed, duplex and auto-negotiation settings for the
specified interface using the ETHTOOL_GSET command. The speed is returned n
MBit/s, 0 means that the speed could not be determined. The duplex setting
is 'unknown', 'full' or 'half. The auto-negotiation setting True or False
or None if it could not be determined.
Based on:
https://github.com/rlisagor/pynetlinux/blob/master/pynetlinux/ifconfig.py
https://elixir.bootlin.com/linux/v4.19-rc1/source/include/uapi/linux/ethtool.h
:param interface str: Name of interface
:returns dict: Dictionary with the keys speed, duplex, auto-negotiation
"""
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockfd = sock.fileno()
ecmd = array.array(
"B", struct.pack("I39s", ETHTOOL_GSET, b"\x00" * 39)
)
interface = interface.encode("utf-8")
ifreq = struct.pack("16sP", interface, ecmd.buffer_info()[0])
fcntl.ioctl(sockfd, SIOCETHTOOL, ifreq)
res = ecmd.tobytes()
speed, duplex, auto = struct.unpack("12xHB3xB24x", res)
except IOError:
speed, duplex, auto = 65535, 255, 255
finally:
sock.close()
if speed == 65535:
speed = 0
if duplex == 255:
duplex = "unknown"
else:
duplex = "full" if bool(duplex) else "half"
if auto == 255:
auto = None
else:
auto = bool(auto)
return {"speed": speed, "duplex": duplex, "auto-negotiation": auto}