diff options
| -rw-r--r-- | interface-definitions/firewall.xml.in | 2 | ||||
| -rw-r--r-- | interface-definitions/include/firewall/packet-length.xml.i | 18 | ||||
| -rw-r--r-- | python/vyos/firewall.py | 23 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_firewall.py | 22 | ||||
| -rwxr-xr-x | src/validators/packet-length | 29 | 
5 files changed, 93 insertions, 1 deletions
| diff --git a/interface-definitions/firewall.xml.in b/interface-definitions/firewall.xml.in index 9488ddcdc..cca3c0f7d 100644 --- a/interface-definitions/firewall.xml.in +++ b/interface-definitions/firewall.xml.in @@ -383,6 +383,7 @@                  </children>                </node>                #include <include/firewall/common-rule.xml.i> +              #include <include/firewall/packet-length.xml.i>                <node name="hop-limit">                  <properties>                    <help>Hop Limit</help> @@ -571,6 +572,7 @@                  </children>                </node>                #include <include/firewall/common-rule.xml.i> +              #include <include/firewall/packet-length.xml.i>                <node name="icmp">                  <properties>                    <help>ICMP type and code information</help> diff --git a/interface-definitions/include/firewall/packet-length.xml.i b/interface-definitions/include/firewall/packet-length.xml.i new file mode 100644 index 000000000..866a76bbb --- /dev/null +++ b/interface-definitions/include/firewall/packet-length.xml.i @@ -0,0 +1,18 @@ +<!-- include start from firewall/packet-length.xml.i --> +<leafNode name="packet-length"> +  <properties> +    <help>Payload size in bytes, including header and data</help> +    <valueHelp> +      <format>u32:1-65535</format> +      <description>Packet length value. Multiple values can be specified as a comma-separated list. Inverted match is also supported</description> +    </valueHelp> +    <valueHelp> +      <format><start-end></format> +      <description>Packet length range. Inverted match is also supported (e.g. 1001-1005 or !1001-1005)</description> +    </valueHelp> +    <constraint> +      <validator name="packet-length"/> +    </constraint> +  </properties> +</leafNode> +<!-- include end --> diff --git a/python/vyos/firewall.py b/python/vyos/firewall.py index 663c4394a..ea28aa91d 100644 --- a/python/vyos/firewall.py +++ b/python/vyos/firewall.py @@ -265,6 +265,29 @@ def parse_rule(rule_conf, fw_name, rule_id, ip_name):                  if 'type' in rule_conf[icmp]:                      output.append(icmp + ' type ' + rule_conf[icmp]['type']) + +    if 'packet_length' in rule_conf: +        #proto = rule_conf['protocol'] +        length = rule_conf['packet_length'].split(',') + +        lengths = [] +        negated_lengths = [] + +        for p in length: +            if p[0] == '!': +                negated_lengths.append(p[1:]) +            else: +                lengths.append(p) + +        if lengths: +            lengths_str = ','.join(lengths) +            output.append(f'ip{def_suffix} length {{{lengths_str}}}') + +        if negated_lengths: +            negated_lengths_str = ','.join(negated_lengths) +            output.append(f'ip{def_suffix} length != {{{negated_lengths_str}}}') + +      if 'ipsec' in rule_conf:          if 'match_ipsec' in rule_conf['ipsec']:              output.append('meta ipsec == 1') diff --git a/smoketest/scripts/cli/test_firewall.py b/smoketest/scripts/cli/test_firewall.py index 684a07681..8b6c221e3 100755 --- a/smoketest/scripts/cli/test_firewall.py +++ b/smoketest/scripts/cli/test_firewall.py @@ -209,6 +209,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):          self.cli_set(['firewall', 'name', 'smoketest', 'rule', '5', 'tcp', 'flags', 'syn'])          self.cli_set(['firewall', 'name', 'smoketest', 'rule', '5', 'tcp', 'mss', mss_range]) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '6', 'action', 'accept']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '6', 'packet-length', '64,512,1024']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '7', 'action', 'accept']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '7', 'packet-length', '0-30000']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '8', 'action', 'accept']) +        self.cli_set(['firewall', 'name', 'smoketest', 'rule', '8', 'packet-length', '!60000-65535']) +          self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'name', 'smoketest'])          self.cli_commit() @@ -220,7 +227,10 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):              ['tcp dport { 22 }', 'limit rate 5/minute', 'return'],              ['log prefix "[smoketest-default-D]"','smoketest default-action', 'drop'],              ['tcp dport { 22 }', 'add @RECENT_smoketest_4 { ip saddr limit rate over 10/minute burst 10 packets }', 'drop'], -            [f'tcp flags & syn == syn tcp option maxseg size {mss_range}'] +            [f'tcp flags & syn == syn tcp option maxseg size {mss_range}'], +            ['ip length { 64, 512, 1024 }', 'return'], +            ['ip length { 0-30000 }', 'return'], +            ['ip length != { 60000-65535 }', 'return']          ]          self.verify_nftables(nftables_search, 'ip filter') @@ -239,6 +249,13 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):          self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '2', 'protocol', 'tcp_udp'])          self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '2', 'destination', 'port', '8888']) +        self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '3', 'action', 'accept']) +        self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '3', 'packet-length', '64,512,1024']) +        self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '4', 'action', 'accept']) +        self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '4', 'packet-length', '0-30000']) +        self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '5', 'action', 'accept']) +        self.cli_set(['firewall', 'ipv6-name', 'v6-smoketest', 'rule', '5', 'packet-length', '!60000-65535']) +          self.cli_set(['interfaces', 'ethernet', 'eth0', 'firewall', 'in', 'ipv6-name', 'v6-smoketest'])          self.cli_commit() @@ -247,6 +264,9 @@ class TestFirewall(VyOSUnitTestSHIM.TestCase):              ['iifname "eth0"', 'jump NAME6_v6-smoketest'],              ['saddr 2002::1', 'daddr 2002::1:1', 'log prefix "[v6-smoketest-1-A]" level crit', 'return'],              ['meta l4proto { tcp, udp }', 'th dport { 8888 }', 'reject'], +            ['ip6 length { 64, 512, 1024 }', 'return'], +            ['ip6 length { 0-30000 }', 'return'], +            ['ip6 length != { 60000-65535 }', 'return'],              ['smoketest default-action', 'log prefix "[v6-smoketest-default-D]"', 'drop']          ] diff --git a/src/validators/packet-length b/src/validators/packet-length new file mode 100755 index 000000000..d96093849 --- /dev/null +++ b/src/validators/packet-length @@ -0,0 +1,29 @@ +#!/usr/bin/python3 + +from sys import argv +from sys import exit +import re + +if __name__ == '__main__': +    if len(argv)>1: +        lengths = argv[1].split(",") + +        for length in lengths: +            if length and length[0] == '!': +                length = length[1:] +            if re.match('^[0-9]{1,5}-[0-9]{1,5}$', length): +                length_1, length_2 = length.split('-') +                if int(length_1) not in range(0, 65536) or int(length_2) not in range(0, 65536): +                    print(f'Error: {length} is not a valid length range') +                    exit(1) +                if int(length_1) > int(length_2): +                    print(f'Error: {length} is not a valid length range') +                    exit(1) +            elif length.isnumeric(): +                if int(length) not in range(0, 65536): +                    print(f'Error: {length} is not a valid length value') +                    exit(1) +    else: +        exit(2) + +    exit(0)
\ No newline at end of file | 
