[pve-devel] [PATCH 09/19] add ipv6 ipset support

Alexandre Derumier aderumier at odiso.com
Wed Jul 16 01:14:25 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 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



More information about the pve-devel mailing list