[pve-devel] [PATCH v4 firewall] fix and improve multiport handling

Wolfgang Bumiller w.bumiller at proxmox.com
Mon Mar 12 11:55:18 CET 2018


The multiport `--ports` parameter is an `OR` match on source
and destination ports, so we should not use it.

We also don't actually use the port count, so let the port
range parser simply return a boolean and use the counter
only for the internal check. This also fixes a regression
caused by the previous multiport check which caused a single
port range to be recognized as a multiport option while it
did not have to be one, causing entries such as the SMB
macro to be added with `--match multiport` mistakenly, which
refused to accept the source port option.

Additionally, we now allow the case with 1 multiport and 1
single port entry: In order for the iptables command to
accept this the single port entry must come first, otherwise
it'll be passed to the multiport matcher (because why
shouldn't it interpret a singular `--Xport` as an alias to
the plural version `--Xports`... *sigh*).

Signed-off-by: Wolfgang Bumiller <w.bumiller at proxmox.com>
Fixes: 6a241ca745f7 ("check multiport limit in port ranges")
---
Changes:
  too many, added a more comprehensive commit message instead

 src/PVE/Firewall.pm | 45 ++++++++++++++++++---------------------------
 1 file changed, 18 insertions(+), 27 deletions(-)

diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm
index bc3d9fe..f952978 100644
--- a/src/PVE/Firewall.pm
+++ b/src/PVE/Firewall.pm
@@ -1063,7 +1063,7 @@ sub parse_port_name_number_or_range {
     die "too many entries in port list (> 15 numbers)\n"
 	if $count > 15;
 
-    return $count;
+    return (scalar(@elements) > 1);
 }
 
 PVE::JSONSchema::register_format('pve-fw-sport-spec', \&pve_fw_verify_sport_spec);
@@ -1885,19 +1885,11 @@ sub ipt_rule_to_cmds {
 	if (my $proto = $rule->{proto}) {
 	    push @match, "-p $proto";
 
-	    my $nbdport = defined($rule->{dport}) ? parse_port_name_number_or_range($rule->{dport}, 1) : 0;
-	    my $nbsport = defined($rule->{sport}) ? parse_port_name_number_or_range($rule->{sport}, 0) : 0;
+	    my $multidport = defined($rule->{dport}) && parse_port_name_number_or_range($rule->{dport}, 1);
+	    my $multisport = defined($rule->{sport}) && parse_port_name_number_or_range($rule->{sport}, 0);
 
-	    my $multiport = 0;
-	    $multiport++ if $nbdport > 1;
-	    $multiport++ if $nbsport > 1;
-
-	    push @match, "--match multiport" if $multiport;
-
-	    die "multiport: option '--sports' cannot be used together with '--dports'\n"
-		if ($multiport == 2) && ($rule->{dport} ne $rule->{sport});
-
-	    if ($rule->{dport}) {
+	    my $add_dport = sub {
+		return if !$rule->{dport};
 		if ($proto eq 'icmp') {
 		    # Note: we use dport to store --icmp-type
 		    die "unknown icmp-type '$rule->{dport}'\n"
@@ -1910,28 +1902,27 @@ sub ipt_rule_to_cmds {
 		    push @match, "-m icmpv6 --icmpv6-type $rule->{dport}";
 		} elsif (!$PROTOCOLS_WITH_PORTS->{$proto}) {
 		    die "protocol $proto does not have ports\n";
+		} elsif ($multidport) {
+		    push @match, "--match multiport", "--dports $rule->{dport}";
 		} else {
-		    if ($nbdport > 1) {
-			if ($multiport == 2) {
-			    push @match,  "--ports $rule->{dport}";
-			} else {
-			    push @match, "--dports $rule->{dport}";
-			}
-		    } else {
-			push @match, "--dport $rule->{dport}";
-		    }
+		    push @match, "--dport $rule->{dport}";
 		}
-	    }
+	    };
 
-	    if ($rule->{sport}) {
+	    my $add_sport = sub {
+		return if !$rule->{sport};
 		die "protocol $proto does not have ports\n"
 		    if !$PROTOCOLS_WITH_PORTS->{$proto};
-		if ($nbsport > 1) {
-		    push @match, "--sports $rule->{sport}" if $multiport != 2;
+		if ($multisport) {
+		    push @match, "--match multiport", "--sports $rule->{sport}";
 		} else {
 		    push @match, "--sport $rule->{sport}";
 		}
-	    }
+	    };
+
+	    $add_dport->() if $multisport;
+	    $add_sport->();
+	    $add_dport->() if !$multisport;
 	} elsif ($rule->{dport} || $rule->{sport}) {
 	    die "destination port '$rule->{dport}', but no protocol specified\n" if $rule->{dport};
 	    die "source port '$rule->{sport}', but no protocol specified\n" if $rule->{sport};
-- 
2.11.0




More information about the pve-devel mailing list