Page MenuHomeVyOS Platform

Update IPv6 firewall rules to support matching of hop-limit
Closed, ResolvedPublicFEATURE REQUEST

Description

Patch for current 1.2 nightly build included. Ip6tables supports the "hl" module for matching hop-limit.

The ability to match IPv6 traffic by TTL greatly simplifies firewall rule creation for appropriately restricting link-local traffic (such as IPv6 neighbor discovery).

Example:

ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 134 -m hl --hl-eq 255 -j ACCEPT  # Router Advertisement [RFC4861]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 135 -m hl --hl-eq 255 -j ACCEPT  # Neighbor Solicitation [RFC4861]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 136 -m hl --hl-eq 255 -j ACCEPT  # Neighbor Advertisement [RFC4861]
ip6tables -A INPUT -p udp -m udp --sport 547 --dport 546 -m hl --hl-eq 255 -j ACCEPT    # DHCPv6

The following syntax is proposed:

firewall {
    ipv6-name EXAMPLE {
        rule 1000 {
            action accept
            hop-limit {
                eq 255
            }
            icmpv6 {
                type 134
            }
            protocol ipv6-icmp
        }
    }
}

Supporting eq, lt, and gt comparison operators for matching TTL 0-255.

The needed vyatta-cfg templates can be generated using the following:

mkdir /opt/vyatta/share/vyatta-cfg/templates/firewall/ipv6-name/node.tag/rule/node.tag/hop-limit
echo -en "help: Hop Limit\n" > /opt/vyatta/share/vyatta-cfg/templates/firewall/ipv6-name/node.tag/rule/node.tag/hop-limit/node.def
mkdir /opt/vyatta/share/vyatta-cfg/templates/firewall/ipv6-name/node.tag/rule/node.tag/hop-limit/eq
mkdir /opt/vyatta/share/vyatta-cfg/templates/firewall/ipv6-name/node.tag/rule/node.tag/hop-limit/lt
mkdir /opt/vyatta/share/vyatta-cfg/templates/firewall/ipv6-name/node.tag/rule/node.tag/hop-limit/gt
echo -en "type: u32\nhelp: Value to match a hop limit equal to it\nval_help: u32:0-255; Hop limit equal to value\nsyntax:expression: \$VAR(@) >= 0 && \$VAR(@) <= 255; \"eq must be between 0 and 255\"\ncommit:expression: (\$VAR(../lt/) == \"\") && (\$VAR(../gt/) == \"\"); \"you may only define one comparison (eq|lt|gt)\"\n" > /opt/vyatta/share/vyatta-cfg/templates/firewall/ipv6-name/node.tag/rule/node.tag/hop-limit/eq/node.def
echo -en "type: u32\nhelp: Value to match a hop limit less than or equal to it\nval_help: u32:0-255; Hop limit less than value\nsyntax:expression: \$VAR(@) >= 0 && \$VAR(@) <= 255; \"lt must be between 0 and 255\"\ncommit:expression: (\$VAR(../eq/) == \"\") && (\$VAR(../gt/) == \"\"); \"you may only define one comparison (eq|lt|gt)\"\n" > /opt/vyatta/share/vyatta-cfg/templates/firewall/ipv6-name/node.tag/rule/node.tag/hop-limit/lt/node.def
echo -en "type: u32\nhelp: Value to match a hop limit greater than or equal to it\nval_help: u32:0-255; Hop limit greater than value\nsyntax:expression: \$VAR(@) >= 0 && \$VAR(@) <= 255; \"gt must be between 0 and 255\"\ncommit:expression: (\$VAR(../lt/) == \"\") && (\$VAR(../eq/) == \"\"); \"you may only define one comparison (eq|lt|gt)\"\n" > /opt/vyatta/share/vyatta-cfg/templates/firewall/ipv6-name/node.tag/rule/node.tag/hop-limit/gt/node.def

The implementation requires the following changes to /opt/vyatta/share/perl5/Vyatta/IpTables/Rule.pm from nightly build 999.201803010337 :

--- /opt/vyatta/share/perl5/Vyatta/IpTables/Rule.pm.backup	2018-03-02 16:46:17.442860929 +0000
+++ /opt/vyatta/share/perl5/Vyatta/IpTables/Rule.pm	2018-03-02 20:53:33.761473262 +0000
@@ -59,7 +59,12 @@
     },
     _disable     => undef,
     _ip_version  => undef,
-    _comment     => undef
+    _comment     => undef,
+    _hop_limit  => {         # rps_patch
+        _eq      => undef,   # rps_patch
+        _lt      => undef,   # rps_patch
+        _gt      => undef,   # rps_patch
+    }
 );
 
 my %dummy_rule = (
@@ -112,7 +117,12 @@
     },
     _disable     => undef,
     _ip_version  => undef,
-    _comment     => undef
+    _comment     => undef,
+    _hop_limit  => {         # rps_patch
+        _eq      => undef,   # rps_patch
+        _lt      => undef,   # rps_patch
+        _gt      => undef,   # rps_patch
+    } 
 );
 
 my $DEBUG = 'false';
@@ -206,6 +216,10 @@
 
     $self->{_disable} = $config->$exists_func("disable");
 
+    $self->{_hop_limit}->{_eq} = $config->$val_func("hop-limit eq");  # rps_patch
+    $self->{_hop_limit}->{_lt} = $config->$val_func("hop-limit lt");  # rps_patch
+    $self->{_hop_limit}->{_gt} = $config->$val_func("hop-limit gt");  # rps_patch
+
     # TODO: need $config->exists("$level source") in Vyatta::Config.pm
     $src->$addr_setup("$level source");
     $dst->$addr_setup("$level destination");
@@ -255,6 +269,7 @@
     print "mod table: $self->{_mod_table}\n"     if defined $self->{_mod_table};
     print "mod dscp: $self->{_mod_dscp}\n"       if defined $self->{_mod_dscp};
     print "mod tcp-mss: $self->{_mod_tcpmss}\n"  if defined $self->{_mod_tcpmss};
+    print "hop-limit: $self->{_hop_limit}\n"     if defined $self->{_hop_limit};  # rps_patch
 
     $src->print();
     $dst->print();
@@ -423,6 +438,16 @@
         }
     }
 
+    # Setup HL rule if configured                                       # rps_patch
+    #                                                                   # rps_patch
+    if      ( defined($self->{_hop_limit}->{_eq}) ) {                   # rps_patch
+        $rule .= " -m hl --hl-eq $self->{_hop_limit}->{_eq}";           # rps_patch
+    } elsif ( defined($self->{_hop_limit}->{_lt}) ) {                   # rps_patch
+        $rule .= " -m hl --hl-lt $self->{_hop_limit}->{_lt}";           # rps_patch
+    } elsif ( defined($self->{_hop_limit}->{_gt}) ) {                   # rps_patch
+        $rule .= " -m hl --hl-gt $self->{_hop_limit}->{_gt}";           # rps_patch
+    }                                                                   # rps_patch
+
     # add the source and destination rules
     ($srcrule, $err_str) = $src->rule();
     return ($err_str,) if (!defined($srcrule));

Note rps_patch comments added for clarity and should be stripped before commit.

This patch appears to be functional and I recommend its review and inclusion in 1.2.

Details

Difficulty level
Unknown (require assessment)
Version
-
Why the issue appeared?
Will be filled on close

Event Timeline

rps added a subscriber: dmbaturin.

P.S. @dmbaturin If you can direct me to some instructions on how you would prefer suggested patches be submitted I can re-work to make it easier on you.

For reference this is the standard ruleset I use on our servers for IPv6 (limited to only what is necessary for DHCPv6 and ICMPv6):

ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 1 -j ACCEPT                      # Destination Unreachable [RFC4443]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 2 -j ACCEPT                      # Packet Too Big [RFC4443]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 3 -j ACCEPT                      # Time Exceeded [RFC4443]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 4 -j ACCEPT                      # Parameter Problem [RFC4443]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 128 -j ACCEPT                    # Echo Request [RFC4443]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 130 -j ACCEPT                    # Multicast Listener Query [RFC2710]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 131 -j ACCEPT                    # Multicast Listener Report [RFC2710]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 132 -j ACCEPT                    # Multicast Listener Done [RFC2710]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 134 -m hl --hl-eq 255 -j ACCEPT  # Router Advertisement [RFC4861]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 135 -m hl --hl-eq 255 -j ACCEPT  # Neighbor Solicitation [RFC4861]
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 136 -m hl --hl-eq 255 -j ACCEPT  # Neighbor Advertisement [RFC4861]
ip6tables -A INPUT -p udp -m udp --sport 547 --dport 546 -m hl --hl-eq 255 -j ACCEPT    # DHCPv6
syncer triaged this task as Normal priority.

Will this make it into 1.2 before the freeze?

@rps Sorry for late reply. I would prefer a git format patch of course, but I've merged it by hand and it seems to work fine. It will be in tomorrow's release candidate and today's nightly build.

IPv4 ttl matching is missing too.
could this be copied to IPv4?
it's exactly same except the iptables module name called ttl

ttl (IPv4-specific)
This module matches the time to live field in the IP header.
[!] --ttl-eq ttl
Matches the given TTL value.
--ttl-gt ttl
Matches if TTL is greater than the given TTL value.
--ttl-lt ttl
Matches if TTL is less than the given TTL value.