big change here, we create now a ipset which include 2 others ipsets for ipv4 and ipv6
PVEFW-0-blacklist list:set PVEFW-0-blacklist-v4 hash:net family inet4 PVEFW-0-blacklist-v6 hash:net family inet6 v4 and v6, are only created if ip address are defined in the set in iptables rules, we use the main set. Benchmark show no performance impact Signed-off-by: Alexandre Derumier <aderum...@odiso.com> --- src/PVE/Firewall.pm | 73 +++++++++++++++++++++++++++++++++--------- src/PVE/FirewallSimulator.pm | 10 +++--- 2 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm index 600df95..8e96571 100644 --- a/src/PVE/Firewall.pm +++ b/src/PVE/Firewall.pm @@ -63,7 +63,7 @@ sub pve_verify_ipv4_or_cidr_or_alias { return if $cidr =~ m/^(?:$ip_alias_pattern)$/; - if ($cidr =~ m!^(?:$IPV4RE)(/(\d+))?$!) { + if ($cidr =~ m!^(?:$IPV6RE|$IPV4RE)(/(\d+))?$!) { return $cidr if Net::IP->new($cidr); return undef if $noerr; die Net::IP::Error() . "\n"; @@ -2719,42 +2719,64 @@ sub generate_ipset { die "duplicate ipset chain '$name'\n" if defined($ipset_ruleset->{$name}); - my $hashsize = scalar(@$options); - if ($hashsize <= 64) { - $hashsize = 64; - } else { - $hashsize = round_powerof2($hashsize); - } - - $ipset_ruleset->{$name} = ["create $name hash:net family inet hashsize $hashsize maxelem $hashsize"]; + $ipset_ruleset->{$name} = ["create $name list:set size 4"]; # remove duplicates my $nethash = {}; foreach my $entry (@$options) { next if $entry->{errors}; # skip entries with errors eval { - my $cidr = resolve_alias($clusterfw_conf, $fw_conf, $entry->{cidr}); - $nethash->{$cidr} = { cidr => $cidr, nomatch => $entry->{nomatch} }; + my $cidr = $entry->{cidr}; + if ($entry->{cidr} =~ m/^${ip_alias_pattern}$/){ # make sure alias exists + $cidr = resolve_alias($clusterfw_conf, $fw_conf, $entry->{cidr}); + } + my $ipversion = get_ip_version($cidr); + $cidr = compress_ipv6(lc($cidr)); + + $nethash->{$ipversion}->{$cidr} = { cidr => $cidr, nomatch => $entry->{nomatch} }; }; warn $@ if $@; } + generate_ipset_ipversion($nethash->{4}, $name, $ipset_ruleset, "4"); + generate_ipset_ipversion($nethash->{6}, $name, $ipset_ruleset, "6"); + +} + +sub generate_ipset_ipversion { + my ($nethash, $name, $ipset_ruleset, $ipversion) = @_; + + my $hashsize = scalar keys %$nethash; + return if $hashsize == 0; + + if ($hashsize <= 64) { + $hashsize = 64; + } else { + $hashsize = round_powerof2($hashsize); + } + + my $inet = $ipversion eq "6" ? "inet6" : "inet"; + my $subname = "$name-v$ipversion"; + + $ipset_ruleset->{$subname} = ["create $subname hash:net family $inet hashsize $hashsize maxelem $hashsize"]; + push @{$ipset_ruleset->{$name}}, "add $name $subname"; foreach my $cidr (sort keys %$nethash) { my $entry = $nethash->{$cidr}; - my $cmd = "add $name $cidr"; + my $cmd = "add $subname $cidr"; if ($entry->{nomatch}) { if ($feature_ipset_nomatch) { - push @{$ipset_ruleset->{$name}}, "$cmd nomatch"; + push @{$ipset_ruleset->{$subname}}, "$cmd nomatch"; } else { warn "ignore !$cidr - nomatch not supported by kernel\n"; } } else { - push @{$ipset_ruleset->{$name}}, $cmd; + push @{$ipset_ruleset->{$subname}}, $cmd; } } -} + +} sub round_powerof2 { my ($int) = @_; @@ -3131,6 +3153,7 @@ sub get_ipset_cmdlist { my $delete_cmdlist = ""; my $active_chains = ipset_get_chains(); + my $statushash = get_ruleset_status($ruleset, $active_chains, \&ipset_chain_digest, $verbose); # remove stale _swap chains @@ -3149,6 +3172,11 @@ sub get_ipset_cmdlist { $cmdlist .= "$cmd\n"; } } + } + + foreach my $chain (sort keys %$ruleset) { + my $stat = $statushash->{$chain}; + die "internal error" if !$stat; if ($stat->{action} eq 'update') { my $chain_swap = $chain."_swap"; @@ -3164,7 +3192,7 @@ sub get_ipset_cmdlist { } - foreach my $chain (keys %$statushash) { + foreach my $chain (sort keys %$statushash) { next if $statushash->{$chain}->{action} ne 'delete'; $delete_cmdlist .= "flush $chain\n"; @@ -3325,4 +3353,17 @@ sub update { run_locked($code); } +# Replace longest run of null blocks with a double colon +sub compress_ipv6 { + my $ip = shift; + if (my @runs = $ip =~ /((?:(?:^|:)(?:0000))+:?)/g ) { + my $max = $runs[0]; + for (@runs[1..$#runs]) { + $max = $_ if length($max) < length; + } + $ip =~ s/$max/::/; + } + $ip =~ s/:0{1,3}/:/g; + return $ip; +} 1; diff --git a/src/PVE/FirewallSimulator.pm b/src/PVE/FirewallSimulator.pm index 30ae847..bdb97f5 100644 --- a/src/PVE/FirewallSimulator.pm +++ b/src/PVE/FirewallSimulator.pm @@ -53,9 +53,11 @@ sub nf_dev_match { } sub ipset_match { - my ($ipsetname, $ipset, $ipaddr) = @_; + my ($ipsetname, $ipset_ruleset, $ipaddr) = @_; my $ip = Net::IP->new($ipaddr); + my $ipversion = PVE::Firewall::get_ip_version($ipaddr); + my $ipset = $ipset_ruleset->{"$ipsetname-v$ipversion"}; foreach my $entry (@$ipset) { next if $entry =~ m/^create/; # simply ignore @@ -157,9 +159,9 @@ sub rule_match { my $ipset = $ipset_ruleset->{$2}; die "no such ip set '$2'" if !$ipset; if ($neg) { - return undef if ipset_match($1, $ipset, $pkg->{source}); + return undef if ipset_match($2, $ipset_ruleset, $pkg->{source}); } else { - return undef if !ipset_match($1, $ipset, $pkg->{source}); + return undef if !ipset_match($2, $ipset_ruleset, $pkg->{source}); } next; } @@ -168,7 +170,7 @@ sub rule_match { die "missing destination" if !$pkg->{dest}; my $ipset = $ipset_ruleset->{$1}; die "no such ip set '$1'" if !$ipset; - return undef if !ipset_match($1, $ipset, $pkg->{dest}); + return undef if !ipset_match($1, $ipset_ruleset, $pkg->{dest}); next; } -- 1.7.10.4 _______________________________________________ pve-devel mailing list pve-devel@pve.proxmox.com http://pve.proxmox.com/cgi-bin/mailman/listinfo/pve-devel