From c8551d473b12c44b5bbb9d9e9d7921410eb55a5b Mon Sep 17 00:00:00 2001
From: Christian Poessinger <christian@poessinger.com>
Date: Sun, 20 Dec 2020 15:22:05 +0100
Subject: ethernet: T3140: relax "ethernet offload-options" CLI definition

Migrate from
 ethernet eth1 {
     offload-options {
         generic-receive on
         generic-segmentation on
         scatter-gather on
         tcp-segmentation on
         udp-fragmentation on
     }
 }

to

  ethernet eth1 {
      offload {
          ufo
          tso
          sg
          gso
          gro
      }
  }
---
 interface-definitions/interfaces-ethernet.xml.in | 102 ++++-------------------
 python/vyos/ifconfig/ethernet.py                 |  82 +++++++++++-------
 src/migration-scripts/interfaces/17-to-18        |  36 ++++++--
 3 files changed, 96 insertions(+), 124 deletions(-)

diff --git a/interface-definitions/interfaces-ethernet.xml.in b/interface-definitions/interfaces-ethernet.xml.in
index 8bd9b7010..d3d94cf8d 100644
--- a/interface-definitions/interfaces-ethernet.xml.in
+++ b/interface-definitions/interfaces-ethernet.xml.in
@@ -60,109 +60,39 @@
           #include <include/interface-mac.xml.i>
           #include <include/interface-mtu-68-16000.xml.i>
           #include <include/interface-mirror.xml.i>
-          <node name="offload-options">
+          <node name="offload">
             <properties>
               <help>Configurable offload options</help>
             </properties>
             <children>
-              <leafNode name="generic-receive">
+              <leafNode name="gro">
                 <properties>
-                  <help>Configure GRO (generic receive offload)</help>
-                  <completionHelp>
-                    <list>on off</list>
-                  </completionHelp>
-                  <valueHelp>
-                    <format>on</format>
-                    <description>Enable GRO (generic receive offload)</description>
-                  </valueHelp>
-                  <valueHelp>
-                    <format>off</format>
-                    <description>Disable GRO (generic receive offload)</description>
-                  </valueHelp>
-                  <constraint>
-                    <regex>(on|off)</regex>
-                  </constraint>
-                  <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+                  <help>Enable Generic Receive Offload</help>
+                  <valueless/>
                 </properties>
               </leafNode>
-              <leafNode name="generic-segmentation">
+              <leafNode name="gso">
                 <properties>
-                  <help>Configure GSO (generic segmentation offload)</help>
-                  <completionHelp>
-                    <list>on off</list>
-                  </completionHelp>
-                  <valueHelp>
-                    <format>on</format>
-                    <description>Enable GSO (generic segmentation offload)</description>
-                  </valueHelp>
-                  <valueHelp>
-                    <format>off</format>
-                    <description>Disable GSO (generic segmentation offload)</description>
-                  </valueHelp>
-                  <constraint>
-                    <regex>(on|off)</regex>
-                  </constraint>
-                  <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+                  <help>Enable Generic Segmentation Offload</help>
+                  <valueless/>
                 </properties>
               </leafNode>
-              <leafNode name="scatter-gather">
+              <leafNode name="sg">
                 <properties>
-                  <help>Configure scatter-gather option</help>
-                  <completionHelp>
-                    <list>on off</list>
-                  </completionHelp>
-                  <valueHelp>
-                    <format>on</format>
-                    <description>Enable scatter-gather</description>
-                  </valueHelp>
-                  <valueHelp>
-                    <format>off</format>
-                    <description>Disable scatter-gather</description>
-                  </valueHelp>
-                  <constraint>
-                    <regex>(on|off)</regex>
-                  </constraint>
-                  <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+                  <help>Enable Scatter-Gather</help>
+                  <valueless/>
                 </properties>
               </leafNode>
-              <leafNode name="tcp-segmentation">
+              <leafNode name="tso">
                 <properties>
-                  <help>Configure TSO (TCP segmentation offloading)</help>
-                  <completionHelp>
-                    <list>on off</list>
-                  </completionHelp>
-                  <valueHelp>
-                    <format>on</format>
-                    <description>Enable TSO (TCP segmentation offloading)</description>
-                  </valueHelp>
-                  <valueHelp>
-                    <format>off</format>
-                    <description>Disable TSO (TCP segmentation offloading)</description>
-                  </valueHelp>
-                  <constraint>
-                    <regex>(on|off)</regex>
-                  </constraint>
-                  <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+                  <help>Enable TCP Segmentation Offloading</help>
+                  <valueless/>
                 </properties>
               </leafNode>
-              <leafNode name="udp-fragmentation">
+              <leafNode name="ufo">
                 <properties>
-                  <help>Configure UDP fragmentation offloading</help>
-                  <completionHelp>
-                    <list>on off</list>
-                  </completionHelp>
-                  <valueHelp>
-                    <format>on</format>
-                    <description>Enable UDP fragmentation offloading</description>
-                  </valueHelp>
-                  <valueHelp>
-                    <format>off</format>
-                    <description>Disable UDP fragmentation offloading</description>
-                  </valueHelp>
-                  <constraint>
-                    <regex>(on|off)</regex>
-                  </constraint>
-                  <constraintErrorMessage>Must be either 'on' or 'off'</constraintErrorMessage>
+                  <help>Enable UDP Fragmentation Offloading</help>
+                  <valueless/>
                 </properties>
               </leafNode>
               <leafNode name="xdp">
diff --git a/python/vyos/ifconfig/ethernet.py b/python/vyos/ifconfig/ethernet.py
index b2dd54587..3ef415248 100644
--- a/python/vyos/ifconfig/ethernet.py
+++ b/python/vyos/ifconfig/ethernet.py
@@ -208,55 +208,83 @@ class EthernetIf(Interface):
 
     def set_gro(self, state):
         """
+        Enable Generic Receive Offload. State can be either True or False.
+
         Example:
         >>> from vyos.ifconfig import EthernetIf
         >>> i = EthernetIf('eth0')
-        >>> i.set_gro('on')
+        >>> i.set_gro(True)
         """
-        return self.set_interface('gro', state)
+        if not isinstance(state, bool):
+            raise ValueError("Value out of range")
+        return self.set_interface('gro', 'on' if state else 'off')
 
     def set_gso(self, state):
         """
+        Enable Generic Segmentation offload. State can be either True or False.
         Example:
         >>> from vyos.ifconfig import EthernetIf
         >>> i = EthernetIf('eth0')
-        >>> i.set_gso('on')
+        >>> i.set_gso(True)
         """
-        return self.set_interface('gso', state)
+        if not isinstance(state, bool):
+            raise ValueError("Value out of range")
+        return self.set_interface('gso', 'on' if state else 'off')
 
     def set_sg(self, state):
         """
+        Enable Scatter-Gather support. State can be either True or False.
+
         Example:
         >>> from vyos.ifconfig import EthernetIf
         >>> i = EthernetIf('eth0')
-        >>> i.set_sg('on')
+        >>> i.set_sg(True)
         """
-        return self.set_interface('sg', state)
+        if not isinstance(state, bool):
+            raise ValueError("Value out of range")
+        return self.set_interface('sg', 'on' if state else 'off')
 
     def set_tso(self, state):
         """
+        Enable TCP segmentation offloading. State can be either True or False.
+
         Example:
         >>> from vyos.ifconfig import EthernetIf
         >>> i = EthernetIf('eth0')
-        >>> i.set_tso('on')
+        >>> i.set_tso(False)
         """
-        return self.set_interface('tso', state)
+        if not isinstance(state, bool):
+            raise ValueError("Value out of range")
+        return self.set_interface('tso', 'on' if state else 'off')
 
     def set_ufo(self, state):
         """
+        Enable UDP fragmentation offloading. State can be either True or False.
+
         Example:
         >>> from vyos.ifconfig import EthernetIf
         >>> i = EthernetIf('eth0')
-        >>> i.set_udp_offload('on')
+        >>> i.set_udp_offload(True)
         """
-        return self.set_interface('ufo', state)
+        if not isinstance(state, bool):
+            raise ValueError("Value out of range")
+        return self.set_interface('ufo', 'on' if state else 'off')
 
-    def set_xdp(self, enabled):
+    def set_xdp(self, state):
         """
+        Enable Kernel XDP support. State can be either True or False.
+
+        Example:
+        >>> from vyos.ifconfig import EthernetIf
+        >>> i = EthernetIf('eth0')
+        >>> i.set_xdp(True)
         """
+        if not isinstance(state, bool):
+            raise ValueError("Value out of range")
+
         ifname = self.config['ifname']
         cmd = f'xdp_loader -d {ifname} -U --auto-mode'
-        if enabled:
+        if state:
             # Using 'xdp' will automatically decide if the driver supports
             # 'xdpdrv' or only 'xdpgeneric'. A user later sees which driver is
             # actually in use by calling 'ip a' or 'show interfaces ethernet'
@@ -293,38 +321,30 @@ class EthernetIf(Interface):
         # call base class first
         super().update(config)
 
+        import pprint
+        pprint.pprint(config)
+
         # disable ethernet flow control (pause frames)
-        value = 'off' if 'disable_flow_control' in config.keys() else 'on'
+        value = 'off' if 'disable_flow_control' in config else 'on'
         self.set_flow_control(value)
 
         # GRO (generic receive offload)
-        tmp = dict_search('offload_options.generic_receive', config)
-        value = tmp if (tmp != None) else 'off'
-        self.set_gro(value)
+        self.set_gro(dict_search('offload.gro', config) != None)
 
         # GSO (generic segmentation offload)
-        tmp = dict_search('offload_options.generic_segmentation', config)
-        value = tmp if (tmp != None) else 'off'
-        self.set_gso(value)
+        self.set_gso(dict_search('offload.gso', config) != None)
 
         # scatter-gather option
-        tmp = dict_search('offload_options.scatter_gather', config)
-        value = tmp if (tmp != None) else 'off'
-        self.set_sg(value)
+        self.set_sg(dict_search('offload.sg', config) != None)
 
         # TSO (TCP segmentation offloading)
-        tmp = dict_search('offload_options.udp_fragmentation', config)
-        value = tmp if (tmp != None) else 'off'
-        self.set_tso(value)
+        self.set_tso(dict_search('offload.tso', config) != None)
 
         # UDP fragmentation offloading
-        tmp = dict_search('offload_options.udp_fragmentation', config)
-        value = tmp if (tmp != None) else 'off'
-        self.set_ufo(value)
+        self.set_ufo(dict_search('offload.ufo', config) != None)
 
-        # UDP fragmentation offloading
-        tmp = dict_search('offload_options.xdp', config)
-        self.set_xdp(tmp != None) # enable or disable
+        # eXpress Data Path - highly experimental
+        self.set_xdp(dict_search('offload.xdp', config) != None)
 
         # Set physical interface speed and duplex
         if {'speed', 'duplex'} <= set(config):
diff --git a/src/migration-scripts/interfaces/17-to-18 b/src/migration-scripts/interfaces/17-to-18
index c382a7e85..b8cb8c119 100755
--- a/src/migration-scripts/interfaces/17-to-18
+++ b/src/migration-scripts/interfaces/17-to-18
@@ -16,6 +16,7 @@
 
 # T3043: Move "system wifi-regulatory-domain" to indicidual wireless interface.
 #        Country Code will be migratred from upper to lower case.
+# T3140: Relax ethernet interface offload-options
 
 from sys import exit, argv
 from vyos.configtree import ConfigTree
@@ -31,15 +32,36 @@ if __name__ == '__main__':
 
     config = ConfigTree(config_file)
 
-    # T3043: WIFI country-code should be lower-case
-    wifi_base = ['interfaces', 'wireless']
-    for wifi in config.list_nodes(wifi_base):
-        ccode = wifi_base + [wifi, 'country-code']
-        if config.exists(ccode):
-            tmp = config.return_value(ccode)
-            config.set(ccode, value=tmp.lower(), replace=True)
+    # T3140: Cleanup ethernet offload-options, remove on/off value and use
+    # valueless nodes instead.
+    eth_base = ['interfaces', 'ethernet']
+    if config.exists(eth_base):
+        for eth in config.list_nodes(eth_base):
+           offload = eth_base + [eth, 'offload-options']
+           if config.exists(offload):
+                mapping = {
+                    'generic-receive'      : 'gro',
+                    'generic-segmentation' : 'gso',
+                    'scatter-gather'       : 'sg',
+                    'tcp-segmentation'     : 'tso',
+                    'udp-fragmentation'    : 'ufo',
+                }
+                for k, v in mapping.items():
+                    if config.exists(offload + [k]):
+                        tmp = config.return_value(offload + [k])
+                        if tmp == 'on':
+                            config.set(eth_base + [eth, 'offload', v])
 
+                config.delete(offload)
 
+    # T3043: WIFI country-code should be lower-case
+    wifi_base = ['interfaces', 'wireless']
+    if config.exists(wifi_base):
+        for wifi in config.list_nodes(wifi_base):
+            ccode = wifi_base + [wifi, 'country-code']
+            if config.exists(ccode):
+                tmp = config.return_value(ccode)
+                config.set(ccode, value=tmp.lower(), replace=True)
 
     try:
         with open(file_name, 'w') as f:
-- 
cgit v1.2.3