diff --git a/python/vyos/ethtool.py b/python/vyos/ethtool.py
index 18d66b84b..473c98d0c 100644
--- a/python/vyos/ethtool.py
+++ b/python/vyos/ethtool.py
@@ -1,233 +1,225 @@
 # Copyright 2021-2024 VyOS maintainers and contributors <maintainers@vyos.io>
 #
 # This library 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 library 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 library.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
 import re
 
 from json import loads
 from vyos.utils.process import popen
 
 # These drivers do not support using ethtool to change the speed, duplex, or
 # flow control settings
 _drivers_without_speed_duplex_flow = ['vmxnet3', 'virtio_net', 'xen_netfront',
                                       'iavf', 'ice', 'i40e', 'hv_netvsc', 'veth', 'ixgbevf',
                                       'tun']
 _drivers_without_eee = ['vmxnet3', 'virtio_net', 'xen_netfront', 'hv_netvsc']
 
 class Ethtool:
     """
     Class is used to retrive and cache information about an ethernet adapter
     """
     # dictionary containing driver featurs, it will be populated on demand and
     # the content will look like:
-    # {
-    #   'tls-hw-tx-offload': {'fixed': True, 'enabled': False},
-    #   'tx-checksum-fcoe-crc': {'fixed': True, 'enabled': False},
-    #   'tx-checksum-ip-generic': {'fixed': False, 'enabled': True},
-    #   'tx-checksum-ipv4': {'fixed': True, 'enabled': False},
-    #   'tx-checksum-ipv6': {'fixed': True, 'enabled': False},
-    #   'tx-checksum-sctp': {'fixed': True, 'enabled': False},
-    #   'tx-checksumming': {'fixed': False, 'enabled': True},
-    #   'tx-esp-segmentation': {'fixed': True, 'enabled': False},
-    # }
+    # [{'esp-hw-offload': {'active': False, 'fixed': True, 'requested': False},
+    #   'esp-tx-csum-hw-offload': {'active': False,
+    #                              'fixed': True,
+    #                              'requested': False},
+    #   'fcoe-mtu': {'active': False, 'fixed': True, 'requested': False},
+    #   'generic-receive-offload': {'active': True,
+    #                               'fixed': False,
+    #                               'requested': True},
+    #   'generic-segmentation-offload': {'active': True,
+    #                                    'fixed': False,
+    #                                    'requested': True},
+    #   'highdma': {'active': True, 'fixed': False, 'requested': True},
+    #   'ifname': 'eth0',
+    #   'l2-fwd-offload': {'active': False, 'fixed': True, 'requested': False},
+    #   'large-receive-offload': {'active': False,
+    #                             'fixed': False,
+    #                             'requested': False},
+    # ...
     _features = { }
     # dictionary containing available interface speed and duplex settings
     # {
     #   '10'  : {'full': '', 'half': ''},
     #   '100' : {'full': '', 'half': ''},
     #   '1000': {'full': ''}
     #  }
     _speed_duplex = {'auto': {'auto': ''}}
     _ring_buffer = None
     _driver_name = None
     _auto_negotiation = False
     _auto_negotiation_supported = None
-    _flow_control = False
-    _flow_control_enabled = None
+    _flow_control = None
     _eee = False
     _eee_enabled = None
 
     def __init__(self, ifname):
         # Get driver used for interface
         out, _ = popen(f'ethtool --driver {ifname}')
         driver = re.search(r'driver:\s(\w+)', out)
         if driver:
             self._driver_name = driver.group(1)
 
         # Build a dictinary of supported link-speed and dupley settings.
         out, _ = popen(f'ethtool {ifname}')
         reading = False
         pattern = re.compile(r'\d+base.*')
         for line in out.splitlines()[1:]:
             line = line.lstrip()
             if 'Supported link modes:' in line:
                 reading = True
             if 'Supported pause frame use:' in line:
                 reading = False
             if reading:
                 for block in line.split():
                     if pattern.search(block):
                         speed = block.split('base')[0]
                         duplex = block.split('/')[-1].lower()
                         if speed not in self._speed_duplex:
                             self._speed_duplex.update({ speed : {}})
                         if duplex not in self._speed_duplex[speed]:
                             self._speed_duplex[speed].update({ duplex : ''})
             if 'Supports auto-negotiation:' in line:
                 # Split the following string: Auto-negotiation: off
                 # we are only interested in off or on
                 tmp = line.split()[-1]
                 self._auto_negotiation_supported = bool(tmp == 'Yes')
             # Only read in if Auto-negotiation is supported
             if self._auto_negotiation_supported and 'Auto-negotiation:' in line:
                 # Split the following string: Auto-negotiation: off
                 # we are only interested in off or on
                 tmp = line.split()[-1]
                 self._auto_negotiation = bool(tmp == 'on')
 
-        # Now populate features dictionaty
-        out, _ = popen(f'ethtool --show-features {ifname}')
-        # skip the first line, it only says: "Features for eth0":
-        for line in out.splitlines()[1:]:
-            if ":" in line:
-                key, value = [s.strip() for s in line.strip().split(":", 1)]
-                fixed = bool('fixed' in value)
-                if fixed:
-                    value = value.split()[0].strip()
-                self._features[key.strip()] = {
-                    'enabled' : bool(value == 'on'),
-                    'fixed' : fixed
-                }
+        # Now populate driver features
+        out, _ = popen(f'ethtool --json --show-features {ifname}')
+        self._features = loads(out)
 
+        # Get information about NIC ring buffers
         out, _ = popen(f'ethtool --json --show-ring {ifname}')
         self._ring_buffer = loads(out)
 
         # Get current flow control settings, but this is not supported by
         # all NICs (e.g. vmxnet3 does not support is)
-        out, _ = popen(f'ethtool --show-pause {ifname}')
-        if len(out.splitlines()) > 1:
-            self._flow_control = True
-            # read current flow control setting, this returns:
-            # ['Autonegotiate:', 'on']
-            self._flow_control_enabled = out.splitlines()[1].split()[-1]
+        out, err = popen(f'ethtool --json --show-pause {ifname}')
+        if not bool(err):
+            self._flow_control = loads(out)
 
         # Get current Energy Efficient Ethernet (EEE) settings, but this is
         # not supported by all NICs (e.g. vmxnet3 does not support is)
         out, _ = popen(f'ethtool --show-eee {ifname}')
         if len(out.splitlines()) > 1:
             self._eee = True
             # read current EEE setting, this returns:
             # EEE status: disabled || EEE status: enabled - inactive || EEE status: enabled - active
             self._eee_enabled = bool('enabled' in out.splitlines()[1])
 
     def check_auto_negotiation_supported(self):
         """ Check if the NIC supports changing auto-negotiation """
         return self._auto_negotiation_supported
 
     def get_auto_negotiation(self):
         return self._auto_negotiation_supported and self._auto_negotiation
 
     def get_driver_name(self):
         return self._driver_name
 
     def _get_generic(self, feature):
         """
         Generic method to read self._features and return a tuple for feature
         enabled and feature is fixed.
 
         In case of a missing key, return "fixed = True and enabled = False"
         """
+        active = False
         fixed = True
-        enabled = False
-        if feature in self._features:
-            if 'enabled' in self._features[feature]:
-                enabled = self._features[feature]['enabled']
-            if 'fixed' in self._features[feature]:
-                fixed = self._features[feature]['fixed']
-        return enabled, fixed
+        if feature in self._features[0]:
+            active = bool(self._features[0][feature]['active'])
+            fixed = bool(self._features[0][feature]['fixed'])
+        return active, fixed
 
     def get_generic_receive_offload(self):
         return self._get_generic('generic-receive-offload')
 
     def get_generic_segmentation_offload(self):
         return self._get_generic('generic-segmentation-offload')
 
     def get_hw_tc_offload(self):
         return self._get_generic('hw-tc-offload')
 
     def get_large_receive_offload(self):
         return self._get_generic('large-receive-offload')
 
     def get_scatter_gather(self):
         return self._get_generic('scatter-gather')
 
     def get_tcp_segmentation_offload(self):
         return self._get_generic('tcp-segmentation-offload')
 
     def get_ring_buffer_max(self, rx_tx):
         # Configuration of RX/TX ring-buffers is not supported on every device,
         # thus when it's impossible return None
         if rx_tx not in ['rx', 'tx']:
             ValueError('Ring-buffer type must be either "rx" or "tx"')
         return str(self._ring_buffer[0].get(f'{rx_tx}-max', None))
 
     def get_ring_buffer(self, rx_tx):
         # Configuration of RX/TX ring-buffers is not supported on every device,
         # thus when it's impossible return None
         if rx_tx not in ['rx', 'tx']:
             ValueError('Ring-buffer type must be either "rx" or "tx"')
         return str(self._ring_buffer[0].get(rx_tx, None))
 
     def check_speed_duplex(self, speed, duplex):
         """ Check if the passed speed and duplex combination is supported by
         the underlaying network adapter. """
         if isinstance(speed, int):
             speed = str(speed)
         if speed != 'auto' and not speed.isdigit():
             raise ValueError(f'Value "{speed}" for speed is invalid!')
         if duplex not in ['auto', 'full', 'half']:
             raise ValueError(f'Value "{duplex}" for duplex is invalid!')
 
         if self.get_driver_name() in _drivers_without_speed_duplex_flow:
             return False
 
         if speed in self._speed_duplex:
             if duplex in self._speed_duplex[speed]:
                 return True
         return False
 
     def check_flow_control(self):
         """ Check if the NIC supports flow-control """
-        if self.get_driver_name() in _drivers_without_speed_duplex_flow:
-            return False
-        return self._flow_control
+        return bool(self._flow_control)
 
     def get_flow_control(self):
-        if self._flow_control_enabled == None:
+        if self._flow_control == None:
             raise ValueError('Interface does not support changing '\
                              'flow-control settings!')
-        return self._flow_control_enabled
+
+        return 'on' if bool(self._flow_control[0]['autonegotiate']) else 'off'
 
     def check_eee(self):
         """ Check if the NIC supports eee """
         if self.get_driver_name() in _drivers_without_eee:
             return False
         return self._eee
 
     def get_eee(self):
         if self._eee_enabled == None:
             raise ValueError('Interface does not support changing '\
                              'EEE settings!')
         return self._eee_enabled
diff --git a/smoketest/scripts/cli/test_interfaces_ethernet.py b/smoketest/scripts/cli/test_interfaces_ethernet.py
index 9bf6a1a61..8f387b23d 100755
--- a/smoketest/scripts/cli/test_interfaces_ethernet.py
+++ b/smoketest/scripts/cli/test_interfaces_ethernet.py
@@ -1,331 +1,358 @@
 #!/usr/bin/env python3
 #
 # Copyright (C) 2020-2024 VyOS maintainers and contributors
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License version 2 or later as
 # published by the Free Software Foundation.
 #
 # 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 General Public License for more details.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 import os
 import re
 import unittest
 
 from glob import glob
 from json import loads
 
 from netifaces import AF_INET
 from netifaces import AF_INET6
 from netifaces import ifaddresses
 
 from base_interfaces_test import BasicInterfaceTest
 from vyos.configsession import ConfigSessionError
 from vyos.ifconfig import Section
 from vyos.pki import CERT_BEGIN
 from vyos.utils.process import cmd
 from vyos.utils.process import process_named_running
+from vyos.utils.process import popen
 from vyos.utils.file import read_file
 from vyos.utils.network import is_ipv6_link_local
 
 server_ca_root_cert_data = """
 MIIBcTCCARagAwIBAgIUDcAf1oIQV+6WRaW7NPcSnECQ/lUwCgYIKoZIzj0EAwIw
 HjEcMBoGA1UEAwwTVnlPUyBzZXJ2ZXIgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjBa
 Fw0zMjAyMTUxOTQxMjBaMB4xHDAaBgNVBAMME1Z5T1Mgc2VydmVyIHJvb3QgQ0Ew
 WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ0y24GzKQf4aM2Ir12tI9yITOIzAUj
 ZXyJeCmYI6uAnyAMqc4Q4NKyfq3nBi4XP87cs1jlC1P2BZ8MsjL5MdGWozIwMDAP
 BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRwC/YaieMEnjhYa7K3Flw/o0SFuzAK
 BggqhkjOPQQDAgNJADBGAiEAh3qEj8vScsjAdBy5shXzXDVVOKWCPTdGrPKnu8UW
 a2cCIQDlDgkzWmn5ujc5ATKz1fj+Se/aeqwh4QyoWCVTFLIxhQ==
 """
 
 server_ca_intermediate_cert_data = """
 MIIBmTCCAT+gAwIBAgIUNzrtHzLmi3QpPK57tUgCnJZhXXQwCgYIKoZIzj0EAwIw
 HjEcMBoGA1UEAwwTVnlPUyBzZXJ2ZXIgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjFa
 Fw0zMjAyMTUxOTQxMjFaMCYxJDAiBgNVBAMMG1Z5T1Mgc2VydmVyIGludGVybWVk
 aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABEl2nJ1CzoqPV6hWII2m
 eGN/uieU6wDMECTk/LgG8CCCSYb488dibUiFN/1UFsmoLIdIhkx/6MUCYh62m8U2
 WNujUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMV3YwH88I5gFsFUibbQ
 kMR0ECPsMB8GA1UdIwQYMBaAFHAL9hqJ4wSeOFhrsrcWXD+jRIW7MAoGCCqGSM49
 BAMCA0gAMEUCIQC/ahujD9dp5pMMCd3SZddqGC9cXtOwMN0JR3e5CxP13AIgIMQm
 jMYrinFoInxmX64HfshYqnUY8608nK9D2BNPOHo=
 """
 
 client_ca_root_cert_data = """
 MIIBcDCCARagAwIBAgIUZmoW2xVdwkZSvglnkCq0AHKa6zIwCgYIKoZIzj0EAwIw
 HjEcMBoGA1UEAwwTVnlPUyBjbGllbnQgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjFa
 Fw0zMjAyMTUxOTQxMjFaMB4xHDAaBgNVBAMME1Z5T1MgY2xpZW50IHJvb3QgQ0Ew
 WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAATUpKXzQk2NOVKDN4VULk2yw4mOKPvn
 mg947+VY7lbpfOfAUD0QRg95qZWCw899eKnXp/U4TkAVrmEKhUb6OJTFozIwMDAP
 BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTXu6xGWUl25X3sBtrhm3BJSICIATAK
 BggqhkjOPQQDAgNIADBFAiEAnTzEwuTI9bz2Oae3LZbjP6f/f50KFJtjLZFDbQz7
 DpYCIDNRHV8zBUibC+zg5PqMpQBKd/oPfNU76nEv6xkp/ijO
 """
 
 client_ca_intermediate_cert_data = """
 MIIBmDCCAT+gAwIBAgIUJEMdotgqA7wU4XXJvEzDulUAGqgwCgYIKoZIzj0EAwIw
 HjEcMBoGA1UEAwwTVnlPUyBjbGllbnQgcm9vdCBDQTAeFw0yMjAyMTcxOTQxMjJa
 Fw0zMjAyMTUxOTQxMjJaMCYxJDAiBgNVBAMMG1Z5T1MgY2xpZW50IGludGVybWVk
 aWF0ZSBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGyIVIi217s9j3O+WQ2b
 6R65/Z0ZjQpELxPjBRc0CA0GFCo+pI5EvwI+jNFArvTAJ5+ZdEWUJ1DQhBKDDQdI
 avCjUzBRMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOUS8oNJjChB1Rb9Blcl
 ETvziHJ9MB8GA1UdIwQYMBaAFNe7rEZZSXblfewG2uGbcElIgIgBMAoGCCqGSM49
 BAMCA0cAMEQCIArhaxWgRsAUbEeNHD/ULtstLHxw/P97qPUSROLQld53AiBjgiiz
 9pDfISmpekZYz6bIDWRIR0cXUToZEMFNzNMrQg==
 """
 
 client_cert_data = """
 MIIBmTCCAUCgAwIBAgIUV5T77XdE/tV82Tk4Vzhp5BIFFm0wCgYIKoZIzj0EAwIw
 JjEkMCIGA1UEAwwbVnlPUyBjbGllbnQgaW50ZXJtZWRpYXRlIENBMB4XDTIyMDIx
 NzE5NDEyMloXDTMyMDIxNTE5NDEyMlowIjEgMB4GA1UEAwwXVnlPUyBjbGllbnQg
 Y2VydGlmaWNhdGUwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARuyynqfc/qJj5e
 KJ03oOH8X4Z8spDeAPO9WYckMM0ldPj+9kU607szFzPwjaPWzPdgyIWz3hcN8yAh
 CIhytmJao1AwTjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTIFKrxZ+PqOhYSUqnl
 TGCUmM7wTjAfBgNVHSMEGDAWgBTlEvKDSYwoQdUW/QZXJRE784hyfTAKBggqhkjO
 PQQDAgNHADBEAiAvO8/jvz05xqmP3OXD53XhfxDLMIxzN4KPoCkFqvjlhQIgIHq2
 /geVx3rAOtSps56q/jiDouN/aw01TdpmGKVAa9U=
 """
 
 client_key_data = """
 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxaxAQsJwjoOCByQE
 +qSYKtKtJzbdbOnTsKNSrfgkFH6hRANCAARuyynqfc/qJj5eKJ03oOH8X4Z8spDe
 APO9WYckMM0ldPj+9kU607szFzPwjaPWzPdgyIWz3hcN8yAhCIhytmJa
 """
 
 def get_wpa_supplicant_value(interface, key):
     tmp = read_file(f'/run/wpa_supplicant/{interface}.conf')
     tmp = re.findall(r'\n?{}=(.*)'.format(key), tmp)
     return tmp[0]
 
 def get_certificate_count(interface, cert_type):
     tmp = read_file(f'/run/wpa_supplicant/{interface}_{cert_type}.pem')
     return tmp.count(CERT_BEGIN)
 
 class EthernetInterfaceTest(BasicInterfaceTest.TestCase):
     @classmethod
     def setUpClass(cls):
         cls._base_path = ['interfaces', 'ethernet']
         cls._mirror_interfaces = ['dum21354']
 
         # We only test on physical interfaces and not VLAN (sub-)interfaces
         if 'TEST_ETH' in os.environ:
             tmp = os.environ['TEST_ETH'].split()
             cls._interfaces = tmp
         else:
             for tmp in Section.interfaces('ethernet', vlan=False):
                 cls._interfaces.append(tmp)
 
         cls._macs = {}
         for interface in cls._interfaces:
             cls._macs[interface] = read_file(f'/sys/class/net/{interface}/address')
 
         # call base-classes classmethod
         super(EthernetInterfaceTest, cls).setUpClass()
 
     def tearDown(self):
         for interface in self._interfaces:
             # when using a dedicated interface to test via TEST_ETH environment
             # variable only this one will be cleared in the end - usable to test
             # ethernet interfaces via SSH
             self.cli_delete(self._base_path + [interface])
             self.cli_set(self._base_path + [interface, 'duplex', 'auto'])
             self.cli_set(self._base_path + [interface, 'speed', 'auto'])
             self.cli_set(self._base_path + [interface, 'hw-id', self._macs[interface]])
 
         self.cli_commit()
 
         # Verify that no address remains on the system as this is an eternal
         # interface.
         for interface in self._interfaces:
             self.assertNotIn(AF_INET, ifaddresses(interface))
             # required for IPv6 link-local address
             self.assertIn(AF_INET6, ifaddresses(interface))
             for addr in ifaddresses(interface)[AF_INET6]:
                 # checking link local addresses makes no sense
                 if is_ipv6_link_local(addr['addr']):
                     continue
                 self.assertFalse(is_intf_addr_assigned(interface, addr['addr']))
             # Ensure no VLAN interfaces are left behind
             tmp = [x for x in Section.interfaces('ethernet') if x.startswith(f'{interface}.')]
             self.assertListEqual(tmp, [])
 
     def test_offloading_rps(self):
         # enable RPS on all available CPUs, RPS works with a CPU bitmask,
         # where each bit represents a CPU (core/thread). The formula below
         # expands to rps_cpus = 255 for a 8 core system
         rps_cpus = (1 << os.cpu_count()) -1
 
         # XXX: we should probably reserve one core when the system is under
         # high preasure so we can still have a core left for housekeeping.
         # This is done by masking out the lowst bit so CPU0 is spared from
         # receive packet steering.
         rps_cpus &= ~1
 
         for interface in self._interfaces:
             self.cli_set(self._base_path + [interface, 'offload', 'rps'])
 
         self.cli_commit()
 
         for interface in self._interfaces:
             cpus = read_file(f'/sys/class/net/{interface}/queues/rx-0/rps_cpus')
             # remove the nasty ',' separation on larger strings
             cpus = cpus.replace(',','')
             cpus = int(cpus, 16)
 
             self.assertEqual(f'{cpus:x}', f'{rps_cpus:x}')
 
     def test_offloading_rfs(self):
         global_rfs_flow = 32768
         rfs_flow = global_rfs_flow
 
         for interface in self._interfaces:
             self.cli_set(self._base_path + [interface, 'offload', 'rfs'])
 
         self.cli_commit()
 
         for interface in self._interfaces:
             queues = len(glob(f'/sys/class/net/{interface}/queues/rx-*'))
             rfs_flow = int(global_rfs_flow/queues)
             for i in range(0, queues):
                 tmp = read_file(f'/sys/class/net/{interface}/queues/rx-{i}/rps_flow_cnt')
                 self.assertEqual(int(tmp), rfs_flow)
 
         tmp = read_file(f'/proc/sys/net/core/rps_sock_flow_entries')
         self.assertEqual(int(tmp), global_rfs_flow)
 
         # delete configuration of RFS and check all values returned to default "0"
         for interface in self._interfaces:
             self.cli_delete(self._base_path + [interface, 'offload', 'rfs'])
 
         self.cli_commit()
 
         for interface in self._interfaces:
             queues = len(glob(f'/sys/class/net/{interface}/queues/rx-*'))
             rfs_flow = int(global_rfs_flow/queues)
             for i in range(0, queues):
                 tmp = read_file(f'/sys/class/net/{interface}/queues/rx-{i}/rps_flow_cnt')
                 self.assertEqual(int(tmp), 0)
 
 
     def test_non_existing_interface(self):
         unknonw_interface = self._base_path + ['eth667']
         self.cli_set(unknonw_interface)
 
         # check validate() - interface does not exist
         with self.assertRaises(ConfigSessionError):
             self.cli_commit()
 
         # we need to remove this wrong interface from the configuration
         # manually, else tearDown() will have problem in commit()
         self.cli_delete(unknonw_interface)
 
     def test_speed_duplex_verify(self):
         for interface in self._interfaces:
             self.cli_set(self._base_path + [interface, 'speed', '1000'])
 
             # check validate() - if either speed or duplex is not auto, the
             # other one must be manually configured, too
             with self.assertRaises(ConfigSessionError):
                 self.cli_commit()
             self.cli_set(self._base_path + [interface, 'speed', 'auto'])
             self.cli_commit()
 
     def test_eapol_support(self):
         ca_certs = {
             'eapol-server-ca-root': server_ca_root_cert_data,
             'eapol-server-ca-intermediate': server_ca_intermediate_cert_data,
             'eapol-client-ca-root': client_ca_root_cert_data,
             'eapol-client-ca-intermediate': client_ca_intermediate_cert_data,
         }
         cert_name = 'eapol-client'
 
         for name, data in ca_certs.items():
             self.cli_set(['pki', 'ca', name, 'certificate', data.replace('\n','')])
 
         self.cli_set(['pki', 'certificate', cert_name, 'certificate', client_cert_data.replace('\n','')])
         self.cli_set(['pki', 'certificate', cert_name, 'private', 'key', client_key_data.replace('\n','')])
 
         for interface in self._interfaces:
             # Enable EAPoL
             self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-server-ca-intermediate'])
             self.cli_set(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-client-ca-intermediate'])
             self.cli_set(self._base_path + [interface, 'eapol', 'certificate', cert_name])
 
         self.cli_commit()
 
         # Test multiple CA chains
         self.assertEqual(get_certificate_count(interface, 'ca'), 4)
 
         for interface in self._interfaces:
             self.cli_delete(self._base_path + [interface, 'eapol', 'ca-certificate', 'eapol-client-ca-intermediate'])
 
         self.cli_commit()
 
         # Check for running process
         self.assertTrue(process_named_running('wpa_supplicant'))
 
         # Validate interface config
         for interface in self._interfaces:
             tmp = get_wpa_supplicant_value(interface, 'key_mgmt')
             self.assertEqual('IEEE8021X', tmp)
 
             tmp = get_wpa_supplicant_value(interface, 'eap')
             self.assertEqual('TLS', tmp)
 
             tmp = get_wpa_supplicant_value(interface, 'eapol_flags')
             self.assertEqual('0', tmp)
 
             tmp = get_wpa_supplicant_value(interface, 'ca_cert')
             self.assertEqual(f'"/run/wpa_supplicant/{interface}_ca.pem"', tmp)
 
             tmp = get_wpa_supplicant_value(interface, 'client_cert')
             self.assertEqual(f'"/run/wpa_supplicant/{interface}_cert.pem"', tmp)
 
             tmp = get_wpa_supplicant_value(interface, 'private_key')
             self.assertEqual(f'"/run/wpa_supplicant/{interface}_cert.key"', tmp)
 
             mac = read_file(f'/sys/class/net/{interface}/address')
             tmp = get_wpa_supplicant_value(interface, 'identity')
             self.assertEqual(f'"{mac}"', tmp)
 
         # Check certificate files have the full chain
         self.assertEqual(get_certificate_count(interface, 'ca'), 2)
         self.assertEqual(get_certificate_count(interface, 'cert'), 3)
 
         for name in ca_certs:
             self.cli_delete(['pki', 'ca', name])
         self.cli_delete(['pki', 'certificate', cert_name])
 
     def test_ethtool_ring_buffer(self):
         for interface in self._interfaces:
+            # We do not use vyos.ethtool here to not have any chance
+            # for invalid testcases. Re-gain data by hand
             tmp = cmd(f'sudo ethtool --json --show-ring {interface}')
             tmp = loads(tmp)
             max_rx = str(tmp[0]['rx-max'])
             max_tx = str(tmp[0]['tx-max'])
 
             self.cli_set(self._base_path + [interface, 'ring-buffer', 'rx', max_rx])
             self.cli_set(self._base_path + [interface, 'ring-buffer', 'tx', max_tx])
 
         self.cli_commit()
 
         for interface in self._interfaces:
             tmp = cmd(f'sudo ethtool --json --show-ring {interface}')
             tmp = loads(tmp)
             max_rx = str(tmp[0]['rx-max'])
             max_tx = str(tmp[0]['tx-max'])
             rx = str(tmp[0]['rx'])
             tx = str(tmp[0]['tx'])
 
             # validate if the above change was carried out properly and the
             # ring-buffer size got increased
             self.assertEqual(max_rx, rx)
             self.assertEqual(max_tx, tx)
 
+    def test_ethtool_flow_control(self):
+        for interface in self._interfaces:
+            # Disable flow-control
+            self.cli_set(self._base_path + [interface, 'disable-flow-control'])
+            # Check current flow-control state on ethernet interface
+            out, err = popen(f'sudo ethtool --json --show-pause {interface}')
+            # Flow-control not supported - test if it bails out with a proper
+            # this is a dynamic path where err = 1 on VMware, but err = 0 on
+            # a physical box.
+            if bool(err):
+                with self.assertRaises(ConfigSessionError):
+                    self.cli_commit()
+            else:
+                out = loads(out)
+                # Flow control is on
+                self.assertTrue(out[0]['autonegotiate'])
+
+                # commit change on CLI to disable-flow-control and re-test
+                self.cli_commit()
+
+                out, err = popen(f'sudo ethtool --json --show-pause {interface}')
+                out = loads(out)
+                self.assertFalse(out[0]['autonegotiate'])
+
 if __name__ == '__main__':
     unittest.main(verbosity=2)