[pve-devel] [PATCH 09/15] add ipv6 ipset support
Alexandre Derumier
aderumier at odiso.com
Thu Jul 10 10:22:36 CEST 2014
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 <aderumier at 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 e6d3ce9..f1ff636 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";
@@ -2720,42 +2720,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) = @_;
@@ -3132,6 +3154,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
@@ -3150,6 +3173,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";
@@ -3165,7 +3193,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";
@@ -3326,4 +3354,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
More information about the pve-devel
mailing list