diff options
| -rw-r--r-- | interface-definitions/include/interface/vif-s.xml.i | 22 | ||||
| -rw-r--r-- | interface-definitions/include/interface/vlan-protocol.xml.i | 23 | ||||
| -rw-r--r-- | interface-definitions/include/qos/mtu.xml.i | 14 | ||||
| -rw-r--r-- | interface-definitions/interfaces_bridge.xml.in | 4 | ||||
| -rw-r--r-- | interface-definitions/qos.xml.in | 2 | ||||
| -rw-r--r-- | interface-definitions/service_config-sync.xml.in | 4 | ||||
| -rw-r--r-- | python/vyos/configtree.py | 24 | ||||
| -rw-r--r-- | python/vyos/ifconfig/bridge.py | 34 | ||||
| -rw-r--r-- | python/vyos/qos/base.py | 9 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_backslash_escape.py | 68 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_firewall.py | 31 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_interfaces_bridge.py | 18 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_qos.py | 41 | ||||
| -rwxr-xr-x | src/conf_mode/system_conntrack.py | 16 | ||||
| -rwxr-xr-x | src/op_mode/conntrack.py | 3 | 
15 files changed, 264 insertions, 49 deletions
| diff --git a/interface-definitions/include/interface/vif-s.xml.i b/interface-definitions/include/interface/vif-s.xml.i index fdd62b63d..02e7ab057 100644 --- a/interface-definitions/include/interface/vif-s.xml.i +++ b/interface-definitions/include/interface/vif-s.xml.i @@ -18,27 +18,7 @@      #include <include/interface/dhcpv6-options.xml.i>      #include <include/interface/disable-link-detect.xml.i>      #include <include/interface/disable.xml.i> -    <leafNode name="protocol"> -      <properties> -        <help>Protocol used for service VLAN (default: 802.1ad)</help> -        <completionHelp> -          <list>802.1ad 802.1q</list> -        </completionHelp> -        <valueHelp> -          <format>802.1ad</format> -          <description>Provider Bridging (IEEE 802.1ad, Q-inQ), ethertype 0x88a8</description> -        </valueHelp> -        <valueHelp> -          <format>802.1q</format> -          <description>VLAN-tagged frame (IEEE 802.1q), ethertype 0x8100</description> -        </valueHelp> -        <constraint> -          <regex>(802.1q|802.1ad)</regex> -        </constraint> -        <constraintErrorMessage>Ethertype must be 802.1ad or 802.1q</constraintErrorMessage> -      </properties> -      <defaultValue>802.1ad</defaultValue> -    </leafNode> +    #include <include/interface/vlan-protocol.xml.i>      #include <include/interface/ipv4-options.xml.i>      #include <include/interface/ipv6-options.xml.i>      #include <include/interface/mac.xml.i> diff --git a/interface-definitions/include/interface/vlan-protocol.xml.i b/interface-definitions/include/interface/vlan-protocol.xml.i new file mode 100644 index 000000000..2fe8d65d7 --- /dev/null +++ b/interface-definitions/include/interface/vlan-protocol.xml.i @@ -0,0 +1,23 @@ +<!-- include start from interface/vif.xml.i --> +<leafNode name="protocol"> +  <properties> +    <help>Protocol used for service VLAN (default: 802.1ad)</help> +    <completionHelp> +      <list>802.1ad 802.1q</list> +    </completionHelp> +    <valueHelp> +      <format>802.1ad</format> +      <description>Provider Bridging (IEEE 802.1ad, Q-inQ), ethertype 0x88a8</description> +    </valueHelp> +    <valueHelp> +      <format>802.1q</format> +      <description>VLAN-tagged frame (IEEE 802.1q), ethertype 0x8100</description> +    </valueHelp> +    <constraint> +      <regex>(802.1q|802.1ad)</regex> +    </constraint> +    <constraintErrorMessage>Ethertype must be 802.1ad or 802.1q</constraintErrorMessage> +  </properties> +  <defaultValue>802.1ad</defaultValue> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/include/qos/mtu.xml.i b/interface-definitions/include/qos/mtu.xml.i new file mode 100644 index 000000000..161d4c27f --- /dev/null +++ b/interface-definitions/include/qos/mtu.xml.i @@ -0,0 +1,14 @@ +<!-- include start from qos/mtu.xml.i --> +<leafNode name="mtu"> +  <properties> +    <help>MTU size for this class</help> +    <valueHelp> +      <format>u32:256-65535</format> +      <description>Bytes</description> +    </valueHelp> +    <constraint> +      <validator name="numeric" argument="--range 256-65535"/> +    </constraint> +  </properties> +</leafNode> +<!-- include end --> diff --git a/interface-definitions/interfaces_bridge.xml.in b/interface-definitions/interfaces_bridge.xml.in index d4d277cfc..7fb5f121a 100644 --- a/interface-definitions/interfaces_bridge.xml.in +++ b/interface-definitions/interfaces_bridge.xml.in @@ -98,6 +98,10 @@                <valueless/>              </properties>            </leafNode> +          #include <include/interface/vlan-protocol.xml.i> +          <leafNode name="protocol"> +            <defaultValue>802.1q</defaultValue> +          </leafNode>            <leafNode name="max-age">              <properties>                <help>Interval at which neighbor bridges are removed</help> diff --git a/interface-definitions/qos.xml.in b/interface-definitions/qos.xml.in index 31b9a7d21..7618c3027 100644 --- a/interface-definitions/qos.xml.in +++ b/interface-definitions/qos.xml.in @@ -278,6 +278,7 @@                    #include <include/generic-description.xml.i>                    #include <include/qos/bandwidth.xml.i>                    #include <include/qos/burst.xml.i> +                  #include <include/qos/mtu.xml.i>                    #include <include/qos/class-police-exceed.xml.i>                    #include <include/qos/class-match.xml.i>                    #include <include/qos/class-priority.xml.i> @@ -293,6 +294,7 @@                  <children>                    #include <include/qos/bandwidth.xml.i>                    #include <include/qos/burst.xml.i> +                  #include <include/qos/mtu.xml.i>                    #include <include/qos/class-police-exceed.xml.i>                  </children>                </node> diff --git a/interface-definitions/service_config-sync.xml.in b/interface-definitions/service_config-sync.xml.in index 9e9dcdb69..17f59340d 100644 --- a/interface-definitions/service_config-sync.xml.in +++ b/interface-definitions/service_config-sync.xml.in @@ -38,11 +38,11 @@                  <properties>                    <help>Connection API timeout</help>                    <valueHelp> -                    <format>u32:1-300</format> +                    <format>u32:1-3600</format>                      <description>Connection API timeout</description>                    </valueHelp>                    <constraint> -                    <validator name="numeric" argument="--range 1-300"/> +                    <validator name="numeric" argument="--range 1-3600"/>                    </constraint>                  </properties>                  <defaultValue>60</defaultValue> diff --git a/python/vyos/configtree.py b/python/vyos/configtree.py index d048901f0..423fe01ed 100644 --- a/python/vyos/configtree.py +++ b/python/vyos/configtree.py @@ -20,10 +20,22 @@ from ctypes import cdll, c_char_p, c_void_p, c_int, c_bool  LIBPATH = '/usr/lib/libvyosconfig.so.0' +def replace_backslash(s, search, replace): +    """Modify quoted strings containing backslashes not of escape sequences""" +    def replace_method(match): +        result = match.group().replace(search, replace) +        return result +    p = re.compile(r'("[^"]*[\\][^"]*"\n|\'[^\']*[\\][^\']*\'\n)') +    return p.sub(replace_method, s) +  def escape_backslash(string: str) -> str: -    """Escape single backslashes in string that are not in escape sequence""" -    p = re.compile(r'(?<!\\)[\\](?!b|f|n|r|t|\\[^bfnrt])') -    result = p.sub(r'\\\\', string) +    """Escape single backslashes in quoted strings""" +    result = replace_backslash(string, '\\', '\\\\') +    return result + +def unescape_backslash(string: str) -> str: +    """Unescape backslashes in quoted strings""" +    result = replace_backslash(string, '\\\\', '\\')      return result  def extract_version(s): @@ -165,11 +177,14 @@ class ConfigTree(object):      def to_string(self, ordered_values=False):          config_string = self.__to_string(self.__config, ordered_values).decode() +        config_string = unescape_backslash(config_string)          config_string = "{0}\n{1}".format(config_string, self.__version)          return config_string      def to_commands(self, op="set"): -        return self.__to_commands(self.__config, op.encode()).decode() +        commands = self.__to_commands(self.__config, op.encode()).decode() +        commands = unescape_backslash(commands) +        return commands      def to_json(self):          return self.__to_json(self.__config).decode() @@ -362,6 +377,7 @@ def show_diff(left, right, path=[], commands=False, libpath=LIBPATH):          msg = __get_error().decode()          raise ConfigTreeError(msg) +    res = unescape_backslash(res)      return res  def union(left, right, libpath=LIBPATH): diff --git a/python/vyos/ifconfig/bridge.py b/python/vyos/ifconfig/bridge.py index b29e71394..7936e3da5 100644 --- a/python/vyos/ifconfig/bridge.py +++ b/python/vyos/ifconfig/bridge.py @@ -1,4 +1,4 @@ -# Copyright 2019-2021 VyOS maintainers and contributors <maintainers@vyos.io> +# Copyright 2019-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 @@ -14,12 +14,11 @@  # License along with this library.  If not, see <http://www.gnu.org/licenses/>.  from netifaces import interfaces -import json  from vyos.ifconfig.interface import Interface  from vyos.utils.assertion import assert_boolean +from vyos.utils.assertion import assert_list  from vyos.utils.assertion import assert_positive -from vyos.utils.process import cmd  from vyos.utils.dict import dict_search  from vyos.configdict import get_vlan_ids  from vyos.configdict import list_diff @@ -86,6 +85,10 @@ class BridgeIf(Interface):              'validate': assert_boolean,              'location': '/sys/class/net/{ifname}/bridge/vlan_filtering',          }, +        'vlan_protocol': { +            'validate': lambda v: assert_list(v, ['0x88a8', '0x8100']), +            'location': '/sys/class/net/{ifname}/bridge/vlan_protocol', +        },          'multicast_querier': {              'validate': assert_boolean,              'location': '/sys/class/net/{ifname}/bridge/multicast_querier', @@ -248,6 +251,26 @@ class BridgeIf(Interface):          """          return self.set_interface('del_port', interface) +    def set_vlan_protocol(self, protocol): +        """ +        Set protocol used for VLAN filtering. +        The valid values are 0x8100(802.1q) or 0x88A8(802.1ad). + +        Example: +        >>> from vyos.ifconfig import Interface +        >>> BridgeIf('br0').del_port('eth1') +        """ + +        if protocol not in ['802.1q', '802.1ad']: +            raise ValueError() + +        map = { +            '802.1ad': '0x88a8', +            '802.1q' : '0x8100' +        } + +        return self.set_interface('vlan_protocol', map[protocol]) +      def update(self, config):          """ General helper function which works on a dictionary retrived by          get_config_dict(). It's main intention is to consolidate the scattered @@ -294,10 +317,13 @@ class BridgeIf(Interface):              if member in interfaces():                  self.del_port(member) -        # enable/disable Vlan Filter +        # enable/disable VLAN Filter          tmp = '1' if 'enable_vlan' in config else '0'          self.set_vlan_filter(tmp) +        tmp = config.get('protocol') +        self.set_vlan_protocol(tmp) +          # add VLAN interfaces to local 'parent' bridge to allow forwarding          if 'enable_vlan' in config:              for vlan in config.get('vif_remove', {}): diff --git a/python/vyos/qos/base.py b/python/vyos/qos/base.py index 47318122b..c8e881ee2 100644 --- a/python/vyos/qos/base.py +++ b/python/vyos/qos/base.py @@ -324,6 +324,11 @@ class QoSBase:                              if 'burst' in cls_config:                                  burst = cls_config['burst']                                  filter_cmd += f' burst {burst}' + +                            if 'mtu' in cls_config: +                                mtu = cls_config['mtu'] +                                filter_cmd += f' mtu {mtu}' +                          cls = int(cls)                          filter_cmd += f' flowid {self._parent:x}:{cls:x}'                          self._cmd(filter_cmd) @@ -387,6 +392,10 @@ class QoSBase:                      burst = config['default']['burst']                      filter_cmd += f' burst {burst}' +                if 'mtu' in config['default']: +                    mtu = config['default']['mtu'] +                    filter_cmd += f' mtu {mtu}' +                  if 'class' in config:                      filter_cmd += f' flowid {self._parent:x}:{default_cls_id:x}' diff --git a/smoketest/scripts/cli/test_backslash_escape.py b/smoketest/scripts/cli/test_backslash_escape.py new file mode 100755 index 000000000..e94e9ab0a --- /dev/null +++ b/smoketest/scripts/cli/test_backslash_escape.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 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 unittest + +from base_vyostest_shim import VyOSUnitTestSHIM +from vyos.configtree import ConfigTree + +base_path = ['interfaces', 'ethernet', 'eth0', 'description'] + +cases_word = [r'fo\o', r'fo\\o', r'foço\o', r'foço\\o'] +# legacy CLI output quotes only if whitespace present; this is a notable +# difference that confounds the translation legacy -> modern, hence +# determines the regex used in function replace_backslash +cases_phrase = [r'some fo\o', r'some fo\\o', r'some foço\o', r'some foço\\o'] + +case_save_config = '/tmp/smoketest-case-save' + +class TestBackslashEscape(VyOSUnitTestSHIM.TestCase): +    def test_backslash_escape_word(self): +        for case in cases_word: +            self.cli_set(base_path + [case]) +            self.cli_commit() +            # save_config tests translation though subsystems: +            # legacy output -> config -> configtree -> file +            self._session.save_config(case_save_config) +            # reload to configtree and confirm: +            with open(case_save_config) as f: +                config_string = f.read() +            ct = ConfigTree(config_string) +            res = ct.return_value(base_path) +            self.assertEqual(case, res, msg=res) +            print(f'description: {res}') +            self.cli_delete(base_path) +            self.cli_commit() + +    def test_backslash_escape_phrase(self): +        for case in cases_phrase: +            self.cli_set(base_path + [case]) +            self.cli_commit() +            # save_config tests translation though subsystems: +            # legacy output -> config -> configtree -> file +            self._session.save_config(case_save_config) +            # reload to configtree and confirm: +            with open(case_save_config) as f: +                config_string = f.read() +            ct = ConfigTree(config_string) +            res = ct.return_value(base_path) +            self.assertEqual(case, res, msg=res) +            print(f'description: {res}') +            self.cli_delete(base_path) +            self.cli_commit() + +if __name__ == '__main__': +    unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 9e8473fa4..fe6977252 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -598,14 +598,30 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):          self.verify_nftables(nftables_search, 'ip6 vyos_filter') -    def test_ipv4_state_and_status_rules(self): -        name = 'smoketest-state' -        interface = 'eth0' - +    def test_ipv4_global_state(self):          self.cli_set(['firewall', 'global-options', 'state-policy', 'established', 'action', 'accept'])          self.cli_set(['firewall', 'global-options', 'state-policy', 'related', 'action', 'accept'])          self.cli_set(['firewall', 'global-options', 'state-policy', 'invalid', 'action', 'drop']) +        self.cli_commit() + +        nftables_search = [ +            ['jump VYOS_STATE_POLICY'], +            ['chain VYOS_STATE_POLICY'], +            ['ct state established', 'accept'], +            ['ct state invalid', 'drop'], +            ['ct state related', 'accept'] +        ] + +        self.verify_nftables(nftables_search, 'ip vyos_filter') + +        # Check conntrack is enabled from state-policy +        self.verify_nftables_chain([['accept']], 'ip vyos_conntrack', 'FW_CONNTRACK') +        self.verify_nftables_chain([['accept']], 'ip6 vyos_conntrack', 'FW_CONNTRACK') + +    def test_ipv4_state_and_status_rules(self): +        name = 'smoketest-state' +          self.cli_set(['firewall', 'ipv4', 'name', name, 'default-action', 'drop'])          self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'action', 'accept'])          self.cli_set(['firewall', 'ipv4', 'name', name, 'rule', '1', 'state', 'established']) @@ -632,12 +648,7 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):              ['ct state new', 'ct status dnat', 'accept'],              ['ct state { established, new }', 'ct status snat', 'accept'],              ['ct state related', 'ct helper { "ftp", "pptp" }', 'accept'], -            ['drop', f'comment "{name} default-action drop"'], -            ['jump VYOS_STATE_POLICY'], -            ['chain VYOS_STATE_POLICY'], -            ['ct state established', 'accept'], -            ['ct state invalid', 'drop'], -            ['ct state related', 'accept'] +            ['drop', f'comment "{name} default-action drop"']          ]          self.verify_nftables(nftables_search, 'ip vyos_filter') diff --git a/smoketest/scripts/cli/test_interfaces_bridge.py b/smoketest/scripts/cli/test_interfaces_bridge.py index 3500e97d6..124c1fbcb 100755 --- a/smoketest/scripts/cli/test_interfaces_bridge.py +++ b/smoketest/scripts/cli/test_interfaces_bridge.py @@ -182,6 +182,10 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase):          for interface in self._interfaces:              cost = 1000              priority = 10 + +            tmp = get_interface_config(interface) +            self.assertEqual('802.1Q',  tmp['linkinfo']['info_data']['vlan_protocol']) # default VLAN protocol +              for member in self._members:                  tmp = get_interface_config(member)                  self.assertEqual(interface, tmp['master']) @@ -442,5 +446,19 @@ class BridgeInterfaceTest(BasicInterfaceTest.TestCase):          self.cli_delete(['interfaces', 'tunnel', tunnel_if])          self.cli_delete(['interfaces', 'ethernet', 'eth0', 'address', eth0_addr]) +    def test_bridge_vlan_protocol(self): +        protocol = '802.1ad' + +        # Add member interface to bridge and set VLAN filter +        for interface in self._interfaces: +            self.cli_set(self._base_path + [interface, 'protocol', protocol]) + +        # commit config +        self.cli_commit() + +        for interface in self._interfaces: +            tmp = get_interface_config(interface) +            self.assertEqual(protocol, tmp['linkinfo']['info_data']['vlan_protocol']) +  if __name__ == '__main__':      unittest.main(verbosity=2) diff --git a/smoketest/scripts/cli/test_qos.py b/smoketest/scripts/cli/test_qos.py index 81e7326f8..46ef68b1d 100755 --- a/smoketest/scripts/cli/test_qos.py +++ b/smoketest/scripts/cli/test_qos.py @@ -38,6 +38,13 @@ def get_tc_filter_json(interface, direction) -> list:      tmp = loads(tmp)      return tmp +def get_tc_filter_details(interface, direction) -> list: +    # json doesn't contain all params, such as mtu +    if direction not in ['ingress', 'egress']: +        raise ValueError() +    tmp = cmd(f'tc -details filter show dev {interface} {direction}') +    return tmp +  class TestQoS(VyOSUnitTestSHIM.TestCase):      @classmethod      def setUpClass(cls): @@ -234,7 +241,12 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):      def test_05_limiter(self):          qos_config = {              '1' : { -                'bandwidth' : '1000000', +                'bandwidth' : '3000000', +                'exceed' : 'pipe', +                'burst' : '100Kb', +                'mtu' : '1600', +                'not-exceed' : 'continue', +                'priority': '15',                  'match4' : {                      'ssh'   : { 'dport' : '22', },                      }, @@ -262,6 +274,10 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):              self.cli_set(base_path + ['interface', interface, 'ingress', policy_name])              # set default bandwidth parameter for all remaining connections              self.cli_set(base_path + ['policy', 'limiter', policy_name, 'default', 'bandwidth', '500000']) +            self.cli_set(base_path + ['policy', 'limiter', policy_name, 'default', 'burst', '200kb']) +            self.cli_set(base_path + ['policy', 'limiter', policy_name, 'default', 'exceed', 'drop']) +            self.cli_set(base_path + ['policy', 'limiter', policy_name, 'default', 'mtu', '3000']) +            self.cli_set(base_path + ['policy', 'limiter', policy_name, 'default', 'not-exceed', 'ok'])              for qos_class, qos_class_config in qos_config.items():                  qos_class_base = base_path + ['policy', 'limiter', policy_name, 'class', qos_class] @@ -279,6 +295,21 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):                  if 'bandwidth' in qos_class_config:                      self.cli_set(qos_class_base + ['bandwidth', qos_class_config['bandwidth']]) +                if 'exceed' in qos_class_config: +                    self.cli_set(qos_class_base + ['exceed', qos_class_config['exceed']]) + +                if 'not-exceed' in qos_class_config: +                    self.cli_set(qos_class_base + ['not-exceed', qos_class_config['not-exceed']]) + +                if 'burst' in qos_class_config: +                    self.cli_set(qos_class_base + ['burst', qos_class_config['burst']]) + +                if 'mtu' in qos_class_config: +                    self.cli_set(qos_class_base + ['mtu', qos_class_config['mtu']]) + +                if 'priority' in qos_class_config: +                    self.cli_set(qos_class_base + ['priority', qos_class_config['priority']]) +          # commit changes          self.cli_commit() @@ -303,6 +334,14 @@ class TestQoS(VyOSUnitTestSHIM.TestCase):                          dport = int(match_config['dport'])                          self.assertEqual(f'{dport:x}', filter['options']['match']['value']) +            tc_details = get_tc_filter_details(interface, 'ingress') +            self.assertTrue('filter parent ffff: protocol all pref 20 u32 chain 0' in tc_details) +            self.assertTrue('rate 1Gbit burst 15125b mtu 2Kb action drop overhead 0b linklayer ethernet' in tc_details) +            self.assertTrue('filter parent ffff: protocol all pref 15 u32 chain 0' in tc_details) +            self.assertTrue('rate 3Gbit burst 102000b mtu 1600b action pipe/continue overhead 0b linklayer ethernet' in tc_details) +            self.assertTrue('rate 500Mbit burst 204687b mtu 3000b action drop overhead 0b linklayer ethernet' in tc_details) +            self.assertTrue('filter parent ffff: protocol all pref 255 basic chain 0' in tc_details) +      def test_06_network_emulator(self):          policy_type = 'network-emulator' diff --git a/src/conf_mode/system_conntrack.py b/src/conf_mode/system_conntrack.py index a1472aaaa..3d42389f6 100755 --- a/src/conf_mode/system_conntrack.py +++ b/src/conf_mode/system_conntrack.py @@ -185,12 +185,16 @@ def generate(conntrack):      conntrack['ipv4_firewall_action'] = 'return'      conntrack['ipv6_firewall_action'] = 'return' -    for rules, path in dict_search_recursive(conntrack['firewall'], 'rule'): -        if any(('state' in rule_conf or 'connection_status' in rule_conf or 'offload_target' in rule_conf) for rule_conf in rules.values()): -            if path[0] == 'ipv4': -                conntrack['ipv4_firewall_action'] = 'accept' -            elif path[0] == 'ipv6': -                conntrack['ipv6_firewall_action'] = 'accept' +    if dict_search_args(conntrack['firewall'], 'global_options', 'state_policy') != None: +        conntrack['ipv4_firewall_action'] = 'accept' +        conntrack['ipv6_firewall_action'] = 'accept' +    else: +        for rules, path in dict_search_recursive(conntrack['firewall'], 'rule'): +            if any(('state' in rule_conf or 'connection_status' in rule_conf or 'offload_target' in rule_conf) for rule_conf in rules.values()): +                if path[0] == 'ipv4': +                    conntrack['ipv4_firewall_action'] = 'accept' +                elif path[0] == 'ipv6': +                    conntrack['ipv6_firewall_action'] = 'accept'      render(conntrack_config, 'conntrack/vyos_nf_conntrack.conf.j2', conntrack)      render(sysctl_file, 'conntrack/sysctl.conf.j2', conntrack) diff --git a/src/op_mode/conntrack.py b/src/op_mode/conntrack.py index cf8adf795..6ea213bec 100755 --- a/src/op_mode/conntrack.py +++ b/src/op_mode/conntrack.py @@ -112,7 +112,8 @@ def get_formatted_output(dict_data):                      proto = meta['layer4']['protoname']              if direction == 'independent':                  conn_id = meta['id'] -                timeout = meta['timeout'] +                # T6138 flowtable offload conntrack entries without 'timeout' +                timeout = meta.get('timeout', 'n/a')                  orig_src = f'{orig_src}:{orig_sport}' if orig_sport else orig_src                  orig_dst = f'{orig_dst}:{orig_dport}' if orig_dport else orig_dst                  reply_src = f'{reply_src}:{reply_sport}' if reply_sport else reply_src | 
