[pve-devel] [PATCH] basic ipv6 implementation

Alexandre Derumier aderumier at odiso.com
Mon Jun 30 06:04:51 CEST 2014


duplicate iptables rules to ip6tables rules.

-currently only support vms port rules.
-stdchains are a little differents, as ipv6 don't support broadcast and icmp types are differents.
 (don't known what's shorewall generate for ipv6)

fixme
-----

- ip address at formatv6 not implemented)
- ipset v6 not yet implemented
- aliases v6 not yet implemented
- host rules ipv6 not implemented (do we need it ? pve-proxy don't work with ipv6 for example)

Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
---
 src/PVE/Firewall.pm |  313 ++++++++++++++++++++++++++++++++++++++-------------
 src/pve-firewall    |   15 +--
 2 files changed, 243 insertions(+), 85 deletions(-)

diff --git a/src/PVE/Firewall.pm b/src/PVE/Firewall.pm
index 27cf1e6..65154b7 100644
--- a/src/PVE/Firewall.pm
+++ b/src/PVE/Firewall.pm
@@ -576,6 +576,99 @@ my $pve_std_chains = {
     ],
 };
 
+my $pve_std_chainsv6 = {
+    'PVEFW-SET-ACCEPT-MARK' => [
+	"-j MARK --set-mark 1",
+    ],
+    'PVEFW-DropBroadcast' => [
+	# same as shorewall 'Broadcast'
+	# simply DROP BROADCAST/MULTICAST/ANYCAST
+	# we can use this to reduce logging
+	#{ action => 'DROP', dsttype => 'BROADCAST' }, #no broadcast in ipv6
+	{ action => 'DROP', dsttype => 'MULTICAST' },
+	{ action => 'DROP', dsttype => 'ANYCAST' },
+	#{ action => 'DROP', dest => '224.0.0.0/4' },
+    ],
+    'PVEFW-reject' => [
+	# same as shorewall 'reject'
+	#{ action => 'DROP', dsttype => 'BROADCAST' },
+	#{ action => 'DROP', source => '224.0.0.0/4' },
+	{ action => 'DROP', proto => 'icmpv6' },
+	"-p tcp -j REJECT --reject-with tcp-reset",
+	#"-p udp -j REJECT --reject-with icmp-port-unreachable",
+	#"-p icmp -j REJECT --reject-with icmp-host-unreachable",
+	#"-j REJECT --reject-with icmp-host-prohibited",
+    ],
+    'PVEFW-Drop' => [
+	# same as shorewall 'Drop', which is equal to DROP,
+	# but REJECT/DROP some packages to reduce logging,
+	# and ACCEPT critical ICMP types
+	{ action => 'PVEFW-reject',  proto => 'tcp', dport => '43' }, # REJECT 'auth'
+	# we are not interested in BROADCAST/MULTICAST/ANYCAST
+	{ action => 'PVEFW-DropBroadcast' },
+	# ACCEPT critical ICMP types
+	{ action => 'ACCEPT', proto => 'icmpv6', dport => 'destination-unreachable' },
+	{ action => 'ACCEPT', proto => 'icmpv6', dport => 'time-exceeded' },
+	{ action => 'ACCEPT', proto => 'icmpv6', dport => 'packet-too-big' },
+
+	# Drop packets with INVALID state
+	"-m conntrack --ctstate INVALID -j DROP",
+	# Drop Microsoft SMB noise
+	{ action => 'DROP', proto => 'udp', dport => '135,445', nbdport => 2 },
+	{ action => 'DROP', proto => 'udp', dport => '137:139'},
+	{ action => 'DROP', proto => 'udp', dport => '1024:65535', sport => 137 },
+	{ action => 'DROP', proto => 'tcp', dport => '135,139,445', nbdport => 3 },
+	{ action => 'DROP', proto => 'udp', dport => 1900 }, # UPnP
+	# Drop new/NotSyn traffic so that it doesn't get logged
+	"-p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j DROP",
+	# Drop DNS replies
+	{ action => 'DROP', proto => 'udp', sport => 53 },
+    ],
+    'PVEFW-Reject' => [
+	# same as shorewall 'Reject', which is equal to Reject,
+	# but REJECT/DROP some packages to reduce logging,
+	# and ACCEPT critical ICMP types
+	{ action => 'PVEFW-reject',  proto => 'tcp', dport => '43' }, # REJECT 'auth'
+	# we are not interested in BROADCAST/MULTICAST/ANYCAST
+	{ action => 'PVEFW-DropBroadcast' },
+	# ACCEPT critical ICMP types
+	{ action => 'ACCEPT', proto => 'icmpv6', dport => 'destination-unreachable' },
+	{ action => 'ACCEPT', proto => 'icmpv6', dport => 'time-exceeded' },
+	{ action => 'ACCEPT', proto => 'icmpv6', dport => 'packet-too-big' },
+
+	# Drop packets with INVALID state
+	"-m conntrack --ctstate INVALID -j DROP",
+	# Drop Microsoft SMB noise
+	{ action => 'PVEFW-reject', proto => 'udp', dport => '135,445', nbdport => 2 },
+	{ action => 'PVEFW-reject', proto => 'udp', dport => '137:139'},
+	{ action => 'PVEFW-reject', proto => 'udp', dport => '1024:65535', sport => 137 },
+	{ action => 'PVEFW-reject', proto => 'tcp', dport => '135,139,445', nbdport => 3 },
+	{ action => 'DROP', proto => 'udp', dport => 1900 }, # UPnP
+	# Drop new/NotSyn traffic so that it doesn't get logged
+	"-p tcp -m tcp ! --tcp-flags FIN,SYN,RST,ACK SYN -j DROP",
+	# Drop DNS replies
+	{ action => 'DROP', proto => 'udp', sport => 53 },
+    ],
+    'PVEFW-tcpflags' => [
+	# same as shorewall tcpflags action.
+	# Packets arriving on this interface are checked for som illegal combinations of TCP flags
+	"-p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG FIN,PSH,URG -g PVEFW-logflags",
+	"-p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG NONE -g PVEFW-logflags",
+	"-p tcp -m tcp --tcp-flags SYN,RST SYN,RST -g PVEFW-logflags",
+	"-p tcp -m tcp --tcp-flags FIN,SYN FIN,SYN -g PVEFW-logflags",
+	"-p tcp -m tcp --sport 0 --tcp-flags FIN,SYN,RST,ACK SYN -g PVEFW-logflags",
+    ],
+    'PVEFW-smurfs' => [
+	#does smurf attack works with ipv6, as broadcast not exist ???
+
+	# same as shorewall smurfs action
+	# Filter packets for smurfs (packets with a broadcast address as the source).
+	#"-s 0.0.0.0/32 -j RETURN",
+	#"-m addrtype --src-type BROADCAST -g PVEFW-smurflog",
+	#"-s 224.0.0.0/4 -g PVEFW-smurflog",
+    ],
+};
+
 # iptables -p icmp -h
 my $icmp_type_names = {
     any => 1,
@@ -617,6 +710,32 @@ my $icmp_type_names = {
     'address-mask-reply' => 1,
 };
 
+# ip6tables -p icmpv6 -h
+
+my $icmpv6_type_names = {
+    any => 1,
+    'destination-unreachable' => 1,
+    'no-route' => 1,
+    'communication-prohibited' => 1,
+    'address-unreachable' => 1,
+    'port-unreachable' => 1,
+    'packet-too-big' => 1,
+    'time-exceeded' => 1,
+    'ttl-zero-during-transit' => 1,
+    'ttl-zero-during-reassembly' => 1,
+    'parameter-problem' => 1,
+    'bad-header' => 1,
+    'unknown-header-type' => 1,
+    'unknown-option' => 1,
+    'echo-request' => 1,
+    'echo-reply' => 1,
+    'router-solicitation' => 1,
+    'router-advertisement' => 1,
+    'neighbour-solicitation' => 1,
+    'neighbour-advertisement' => 1,
+    'redirect' => 1,
+};
+
 sub init_firewall_macros {
 
     $pve_fw_parsed_macros = {};
@@ -811,7 +930,7 @@ sub parse_address_list {
 }
 
 sub parse_port_name_number_or_range {
-    my ($str) = @_;
+    my ($str, $ipv6) = @_;
 
     my $services = PVE::Firewall::get_etc_services();
     my $count = 0;
@@ -827,7 +946,10 @@ sub parse_port_name_number_or_range {
 	    my $port = $1;
 	    die "invalid port '$port'\n" if $port > 65535;
 	} else {
-	    if ($icmp_type_names->{$item}) {
+	    	
+	    if ($icmp_type_names->{$item} && !$ipv6) {
+		$icmp_port = 1;
+	    } elsif ($icmpv6_type_names->{$item} && $ipv6) {
 		$icmp_port = 1;
 	    } else {
 		die "invalid port '$item'\n" if !$services->{byname}->{$item};
@@ -1064,7 +1186,7 @@ my $rule_env_iface_lookup = {
 };
 
 sub verify_rule {
-    my ($rule, $cluster_conf, $fw_conf, $rule_env, $noerr) = @_;
+    my ($rule, $cluster_conf, $fw_conf, $rule_env, $noerr, $ipv6) = @_;
 
     my $allow_groups = $rule_env eq 'group' ? 0 : 1;
 
@@ -1151,14 +1273,14 @@ sub verify_rule {
     }
 
     if ($rule->{dport}) {
-	eval { parse_port_name_number_or_range($rule->{dport}); };
+	eval { parse_port_name_number_or_range($rule->{dport}, $ipv6); };
 	&$add_error('dport', $@) if $@;
 	&$add_error('proto', "missing property - 'dport' requires this property")
 	    if !$rule->{proto};
     }
 
     if ($rule->{sport}) {
-	eval { parse_port_name_number_or_range($rule->{sport}); };
+	eval { parse_port_name_number_or_range($rule->{sport}, $ipv6); };
 	&$add_error('sport', $@) if $@;
 	&$add_error('proto', "missing property - 'sport' requires this property")
 	    if !$rule->{proto};
@@ -1230,9 +1352,10 @@ sub enable_bridge_firewall {
 my $rule_format = "%-15s %-30s %-30s %-15s %-15s %-15s\n";
 
 sub iptables_restore_cmdlist {
-    my ($cmdlist) = @_;
+    my ($cmdlist, $ipv6) = @_;
 
-    run_command("/sbin/iptables-restore -n", input => $cmdlist);
+    my $iptablescmd = $ipv6 ? "/sbin/ip6tables-restore" : "/sbin/iptables-restore";
+    run_command("$iptablescmd -n", input => $cmdlist);
 }
 
 sub ipset_restore_cmdlist {
@@ -1242,6 +1365,7 @@ sub ipset_restore_cmdlist {
 }
 
 sub iptables_get_chains {
+    my ($ipv6) = @_;
 
     my $res = {};
 
@@ -1296,7 +1420,8 @@ sub iptables_get_chains {
 	}
     };
 
-    run_command("/sbin/iptables-save", outfunc => $parser);
+    my $iptablescmd = $ipv6 ? "/sbin/ip6tables-save" : "/sbin/iptables-save";
+    run_command($iptablescmd, outfunc => $parser);
 
     return wantarray ? ($res, $hooks) : $res;
 }
@@ -1351,15 +1476,15 @@ sub ipset_get_chains {
 }
 
 sub ruleset_generate_cmdstr {
-    my ($ruleset, $chain, $rule, $actions, $goto, $cluster_conf, $fw_conf) = @_;
+    my ($ruleset, $chain, $rule, $actions, $goto, $cluster_conf, $fw_conf, $ipv6) = @_;
 
     return if defined($rule->{enable}) && !$rule->{enable};
     return if $rule->{errors};
 
     die "unable to emit macro - internal error" if $rule->{macro}; # should not happen
 
-    my $nbdport = defined($rule->{dport}) ? parse_port_name_number_or_range($rule->{dport}) : 0;
-    my $nbsport = defined($rule->{sport}) ? parse_port_name_number_or_range($rule->{sport}) : 0;
+    my $nbdport = defined($rule->{dport}) ? parse_port_name_number_or_range($rule->{dport}, $ipv6) : 0;
+    my $nbsport = defined($rule->{sport}) ? parse_port_name_number_or_range($rule->{sport}, $ipv6) : 0;
 
     my @cmd = ();
 
@@ -1440,10 +1565,11 @@ sub ruleset_generate_cmdstr {
 	    if ($multiport == 2) && ($rule->{dport} ne $rule->{sport});
 
 	if ($rule->{dport}) {
-	    if ($rule->{proto} && $rule->{proto} eq 'icmp') {
+	    if ($rule->{proto} && ($rule->{proto} eq 'icmp' || $rule->{proto} eq 'icmpv6')) {
 		# Note: we use dport to store --icmp-type
-		die "unknown icmp-type '$rule->{dport}'\n" if !defined($icmp_type_names->{$rule->{dport}});
-		push @cmd, "-m icmp --icmp-type $rule->{dport}";
+		die "unknown icmp-type '$rule->{dport}'\n" if (!defined($icmp_type_names->{$rule->{dport}}) && !$ipv6);
+		die "unknown icmp-type '$rule->{dport}'\n" if (!defined($icmpv6_type_names->{$rule->{dport}}) && $ipv6);
+		push @cmd, "-m $rule->{proto} --$rule->{proto}-type $rule->{dport}";
 	    } else {
 		if ($nbdport > 1) {
 		    if ($multiport == 2) {
@@ -1481,7 +1607,7 @@ sub ruleset_generate_cmdstr {
 }
 
 sub ruleset_generate_rule {
-    my ($ruleset, $chain, $rule, $actions, $goto, $cluster_conf, $fw_conf) = @_;
+    my ($ruleset, $chain, $rule, $actions, $goto, $cluster_conf, $fw_conf, $ipv6) = @_;
 
     my $rules;
 
@@ -1495,7 +1621,7 @@ sub ruleset_generate_rule {
 
     my @cmds = ();
     foreach my $tmp (@$rules) {
-	if (my $cmdstr = ruleset_generate_cmdstr($ruleset, $chain, $tmp, $actions, $goto, $cluster_conf, $fw_conf)) {
+	if (my $cmdstr = ruleset_generate_cmdstr($ruleset, $chain, $tmp, $actions, $goto, $cluster_conf, $fw_conf, $ipv6)) {
 	    push @cmds, $cmdstr;
 	}
     }
@@ -1589,6 +1715,7 @@ sub ruleset_add_chain_policy {
 
 	ruleset_addrule($ruleset, $chain, "-j DROP");
     } elsif ($policy eq 'REJECT') {
+
 	ruleset_addrule($ruleset, $chain, "-j PVEFW-Reject");
 
 	ruleset_addlog($ruleset, $chain, $vmid, "policy $policy: ", $loglevel);
@@ -1630,18 +1757,23 @@ sub ruleset_chain_add_input_filters {
 }
 
 sub ruleset_create_vm_chain {
-    my ($ruleset, $chain, $options, $macaddr, $ipfilter_ipset, $direction) = @_;
+    my ($ruleset, $chain, $options, $macaddr, $ipfilter_ipset, $direction, $ipv6) = @_;
 
     ruleset_create_chain($ruleset, $chain);
     my $accept = generate_nfqueue($options);
 
     if (!(defined($options->{dhcp}) && $options->{dhcp} == 0)) {
 	if ($direction eq 'OUT') {
+	    my $sport = $ipv6 ? "546" : "68";
+	    my $dport = $ipv6 ? "547" : "67";
 	    ruleset_generate_rule($ruleset, $chain, { action => 'PVEFW-SET-ACCEPT-MARK',
-						      proto => 'udp', sport => 68, dport => 67 });
+						      proto => 'udp', sport => $sport, dport => $dport });
 	} else {
+	    my $sport = $ipv6 ? "547" : "67";
+	    my $dport = $ipv6 ? "546" : "68";
+
 	    ruleset_generate_rule($ruleset, $chain, { action => 'ACCEPT',
-						      proto => 'udp', sport => 67, dport => 68 });
+						      proto => 'udp', sport => $sport, dport => $dport });
 	}
     }
 
@@ -1657,12 +1789,12 @@ sub ruleset_create_vm_chain {
 }
 
 sub ruleset_add_group_rule {
-    my ($ruleset, $cluster_conf, $chain, $rule, $direction, $action) = @_;
+    my ($ruleset, $cluster_conf, $chain, $rule, $direction, $action, $ipv6) = @_;
 
     my $group = $rule->{action};
     my $group_chain = "GROUP-$group-$direction";
     if(!ruleset_chain_exist($ruleset, $group_chain)){
-	generate_group_rules($ruleset, $cluster_conf, $group);
+	generate_group_rules($ruleset, $cluster_conf, $group, $ipv6);
     }
 
     if ($direction eq 'OUT' && $rule->{iface_out}) {
@@ -1677,7 +1809,7 @@ sub ruleset_add_group_rule {
 }
 
 sub ruleset_generate_vm_rules {
-    my ($ruleset, $rules, $cluster_conf, $vmfw_conf, $chain, $netid, $direction, $options) = @_;
+    my ($ruleset, $rules, $cluster_conf, $vmfw_conf, $chain, $netid, $direction, $options, $ipv6) = @_;
 
     my $lc_direction = lc($direction);
 
@@ -1688,18 +1820,18 @@ sub ruleset_generate_vm_rules {
 	next if !$rule->{enable} || $rule->{errors};
 	if ($rule->{type} eq 'group') {
 	    ruleset_add_group_rule($ruleset, $cluster_conf, $chain, $rule, $direction,
-				   $direction eq 'OUT' ? 'RETURN' : $in_accept);
+				   $direction eq 'OUT' ? 'RETURN' : $in_accept, $ipv6);
 	} else {
 	    next if $rule->{type} ne $lc_direction;
 	    eval {
 		if ($direction eq 'OUT') {
 		    ruleset_generate_rule($ruleset, $chain, $rule,
 					  { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" },
-					  undef, $cluster_conf, $vmfw_conf);
+					  undef, $cluster_conf, $vmfw_conf, $ipv6);
 		} else {
 		    ruleset_generate_rule($ruleset, $chain, $rule,
 					  { ACCEPT => $in_accept , REJECT => "PVEFW-reject" },
-					  undef, $cluster_conf, $vmfw_conf);
+					  undef, $cluster_conf, $vmfw_conf, $ipv6);
 		}
 	    };
 	    warn $@ if $@;
@@ -1741,7 +1873,7 @@ sub ruleset_generate_vm_ipsrules {
 }
 
 sub generate_venet_rules_direction {
-    my ($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, $direction) = @_;
+    my ($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, $direction, $ipv6) = @_;
 
     my $lc_direction = lc($direction);
 
@@ -1752,9 +1884,9 @@ sub generate_venet_rules_direction {
 
     my $chain = "venet0-$vmid-$direction";
 
-    ruleset_create_vm_chain($ruleset, $chain, $options, undef, undef, $direction);
+    ruleset_create_vm_chain($ruleset, $chain, $options, undef, undef, $direction, $ipv6);
 
-    ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $vmfw_conf, $chain, 'venet', $direction);
+    ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $vmfw_conf, $chain, 'venet', $direction, undef, $ipv6);
 
     # implement policy
     my $policy;
@@ -1783,7 +1915,7 @@ sub generate_venet_rules_direction {
 }
 
 sub generate_tap_rules_direction {
-    my ($ruleset, $cluster_conf, $iface, $netid, $macaddr, $vmfw_conf, $vmid, $direction) = @_;
+    my ($ruleset, $cluster_conf, $iface, $netid, $macaddr, $vmfw_conf, $vmid, $direction, $ipv6) = @_;
 
     my $lc_direction = lc($direction);
 
@@ -1799,10 +1931,10 @@ sub generate_tap_rules_direction {
 	if $vmfw_conf->{ipset}->{$ipfilter_name};	
 
     # create chain with mac and ip filter
-    ruleset_create_vm_chain($ruleset, $tapchain, $options, $macaddr, $ipfilter_ipset, $direction);
+    ruleset_create_vm_chain($ruleset, $tapchain, $options, $macaddr, $ipfilter_ipset, $direction, $ipv6);
 
     if ($options->{enable}) {
-	ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $vmfw_conf, $tapchain, $netid, $direction, $options);
+	ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $vmfw_conf, $tapchain, $netid, $direction, $options, $ipv6);
 
 	ruleset_generate_vm_ipsrules($ruleset, $options, $direction, $iface);
 
@@ -1949,7 +2081,7 @@ sub enable_host_firewall {
 }
 
 sub generate_group_rules {
-    my ($ruleset, $cluster_conf, $group) = @_;
+    my ($ruleset, $cluster_conf, $group, $ipv6) = @_;
 
     my $rules = $cluster_conf->{groups}->{$group};
 
@@ -1965,7 +2097,7 @@ sub generate_group_rules {
 
     foreach my $rule (@$rules) {
 	next if $rule->{type} ne 'in';
-	ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, undef, $cluster_conf);
+	ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, undef, $cluster_conf, $ipv6);
     }
 
     $chain = "GROUP-${group}-OUT";
@@ -1978,7 +2110,7 @@ sub generate_group_rules {
 	# we use PVEFW-SET-ACCEPT-MARK (Instead of ACCEPT) because we need to
 	# check also other tap rules later
 	ruleset_generate_rule($ruleset, $chain, $rule,
-			      { ACCEPT => 'PVEFW-SET-ACCEPT-MARK', REJECT => "PVEFW-reject" }, undef, $cluster_conf);
+			      { ACCEPT => 'PVEFW-SET-ACCEPT-MARK', REJECT => "PVEFW-reject" }, undef, $cluster_conf, $ipv6);
     }
 }
 
@@ -1989,7 +2121,7 @@ for (my $i = 0; $i < $MAX_NETS; $i++)  {
 }
 
 sub parse_fw_rule {
-    my ($prefix, $line, $cluster_conf, $fw_conf, $rule_env, $verbose) = @_;
+    my ($prefix, $line, $cluster_conf, $fw_conf, $rule_env, $verbose, $ipv6) = @_;
 
     my $orig_line = $line;
 
@@ -2053,7 +2185,7 @@ sub parse_fw_rule {
 
     die "unable to parse rule parameters: $line\n" if length($line);
 
-    $rule = verify_rule($rule, $cluster_conf, $fw_conf, $rule_env, 1);
+    $rule = verify_rule($rule, $cluster_conf, $fw_conf, $rule_env, 1, $ipv6);
     if ($verbose && $rule->{errors}) {
 	warn "$prefix - errors in rule parameters: $orig_line\n";
 	foreach my $p (keys %{$rule->{errors}}) {
@@ -2168,7 +2300,7 @@ sub parse_alias {
 }
 
 sub generic_fw_config_parser {
-    my ($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env) = @_;
+    my ($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env, $ipv6) = @_;
 
     my $section;
     my $group;
@@ -2265,7 +2397,7 @@ sub generic_fw_config_parser {
 	    warn "$prefix: $@" if $@;
 	} elsif ($section eq 'rules') {
 	    my $rule;
-	    eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, $res, $rule_env, $verbose); };
+	    eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, $res, $rule_env, $verbose, $ipv6); };
 	    if (my $err = $@) {
 		warn "$prefix: $err";
 		next;
@@ -2273,7 +2405,7 @@ sub generic_fw_config_parser {
 	    push @{$res->{$section}}, $rule;
 	} elsif ($section eq 'groups') {
 	    my $rule;
-	    eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, undef, 'group', $verbose); };
+	    eval { $rule = parse_fw_rule($prefix, $line, $cluster_conf, undef, 'group', $verbose, $ipv6); };
 	    if (my $err = $@) {
 		warn "$prefix: $err";
 		next;
@@ -2328,15 +2460,15 @@ sub generic_fw_config_parser {
 }
 
 sub parse_hostfw_config {
-    my ($filename, $fh, $cluster_conf, $verbose) = @_;
+    my ($filename, $fh, $cluster_conf, $verbose, $ipv6) = @_;
 
     my $empty_conf = { rules => [], options => {}};
 
-    return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, 'host');
+    return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, 'host', $ipv6);
 }
 
 sub parse_vmfw_config {
-    my ($filename, $fh, $cluster_conf, $rule_env, $verbose) = @_;
+    my ($filename, $fh, $cluster_conf, $rule_env, $verbose, $ipv6) = @_;
 
     my $empty_conf = {
 	rules => [],
@@ -2346,11 +2478,11 @@ sub parse_vmfw_config {
 	ipset_comments => {},
     };
 
-    return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env);
+    return generic_fw_config_parser($filename, $fh, $verbose, $cluster_conf, $empty_conf, $rule_env, $ipv6);
 }
 
 sub parse_clusterfw_config {
-    my ($filename, $fh, $verbose) = @_;
+    my ($filename, $fh, $verbose, $ipv6) = @_;
 
     my $section;
     my $group;
@@ -2365,7 +2497,7 @@ sub parse_clusterfw_config {
 	ipset_comments => {},
     };
 
-    return generic_fw_config_parser($filename, $fh, $verbose, $empty_conf, $empty_conf, 'cluster');
+    return generic_fw_config_parser($filename, $fh, $verbose, $empty_conf, $empty_conf, 'cluster', $ipv6);
 }
 
 sub run_locked {
@@ -2417,7 +2549,7 @@ sub read_local_vm_config {
 };
 
 sub load_vmfw_conf {
-    my ($cluster_conf, $rule_env, $vmid, $dir, $verbose) = @_;
+    my ($cluster_conf, $rule_env, $vmid, $dir, $verbose, $ipv6) = @_;
 
     my $vmfw_conf = {};
 
@@ -2425,7 +2557,7 @@ sub load_vmfw_conf {
 
     my $filename = "$dir/$vmid.fw";
     if (my $fh = IO::File->new($filename, O_RDONLY)) {
-	$vmfw_conf = parse_vmfw_config($filename, $fh, $cluster_conf, $rule_env, $verbose);
+	$vmfw_conf = parse_vmfw_config($filename, $fh, $cluster_conf, $rule_env, $verbose, $ipv6);
 	$vmfw_conf->{vmid} = $vmid;
     }
 
@@ -2562,17 +2694,17 @@ sub save_vmfw_conf {
 }
 
 sub read_vm_firewall_configs {
-    my ($cluster_conf, $vmdata, $dir, $verbose) = @_;
+    my ($cluster_conf, $vmdata, $dir, $verbose, $ipv6) = @_;
 
     my $vmfw_configs = {};
 
     foreach my $vmid (keys %{$vmdata->{qemu}}) {
-	my $vmfw_conf = load_vmfw_conf($cluster_conf, 'vm', $vmid, $dir, $verbose);
+	my $vmfw_conf = load_vmfw_conf($cluster_conf, 'vm', $vmid, $dir, $verbose, $ipv6);
 	next if !$vmfw_conf->{options}; # skip if file does not exists
 	$vmfw_configs->{$vmid} = $vmfw_conf;
     }
     foreach my $vmid (keys %{$vmdata->{openvz}}) {
-	my $vmfw_conf = load_vmfw_conf($cluster_conf, 'ct', $vmid, $dir, $verbose);
+	my $vmfw_conf = load_vmfw_conf($cluster_conf, 'ct', $vmid, $dir, $verbose, $ipv6);
 	next if !$vmfw_conf->{options}; # skip if file does not exists
 	$vmfw_configs->{$vmid} = $vmfw_conf;
     }
@@ -2598,7 +2730,7 @@ sub get_option_log_level {
 }
 
 sub generate_std_chains {
-    my ($ruleset, $options) = @_;
+    my ($ruleset, $options, $pve_std_chains, $ipv6) = @_;
 
     my $loglevel = get_option_log_level($options, 'smurf_log_level');
 
@@ -2622,7 +2754,7 @@ sub generate_std_chains {
 	ruleset_create_chain($ruleset, $chain);
 	foreach my $rule (@{$pve_std_chains->{$chain}}) {
 	    if (ref($rule)) {
-		ruleset_generate_rule($ruleset, $chain, $rule);
+		ruleset_generate_rule($ruleset, $chain, $rule, undef, undef, undef, undef, $ipv6);
 	    } else {
 		ruleset_addrule($ruleset, $chain, $rule);
 	    }
@@ -2689,13 +2821,13 @@ sub round_powerof2 {
 }
 
 sub load_clusterfw_conf {
-    my ($filename, $verbose) = @_;
+    my ($filename, $verbose, $ipv6) = @_;
 
     $filename = $clusterfw_conf_filename if !defined($filename);
 
     my $cluster_conf = {};
     if (my $fh = IO::File->new($filename, O_RDONLY)) {
-	$cluster_conf = parse_clusterfw_config($filename, $fh, $verbose);
+	$cluster_conf = parse_clusterfw_config($filename, $fh, $verbose, $ipv6);
     }
 
     return $cluster_conf;
@@ -2741,13 +2873,13 @@ sub save_clusterfw_conf {
 }
 
 sub load_hostfw_conf {
-    my ($cluster_conf, $filename, $verbose) = @_;
+    my ($cluster_conf, $filename, $verbose, $ipv6) = @_;
 
     $filename = $hostfw_conf_filename if !defined($filename);
 
     my $hostfw_conf = {};
     if (my $fh = IO::File->new($filename, O_RDONLY)) {
-	$hostfw_conf = parse_hostfw_config($filename, $fh, $cluster_conf, $verbose);
+	$hostfw_conf = parse_hostfw_config($filename, $fh, $cluster_conf, $verbose, $ipv6);
     }
     return $hostfw_conf;
 }
@@ -2771,26 +2903,26 @@ sub save_hostfw_conf {
 }
 
 sub compile {
-    my ($cluster_conf, $hostfw_conf, $vmdata, $verbose) = @_;
+    my ($cluster_conf, $hostfw_conf, $vmdata, $verbose, $ipv6) = @_;
 
     my $vmfw_configs;
 
     if ($vmdata) { # test mode
 	my $testdir = $vmdata->{testdir} || die "no test directory specified";
 	my $filename = "$testdir/cluster.fw";
-	$cluster_conf = load_clusterfw_conf($filename, $verbose);
+	$cluster_conf = load_clusterfw_conf($filename, $verbose, $ipv6);
 
 	$filename = "$testdir/host.fw";
-	$hostfw_conf = load_hostfw_conf($cluster_conf, $filename, $verbose);
+	$hostfw_conf = load_hostfw_conf($cluster_conf, $filename, $verbose, $ipv6);
 
-	$vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, $testdir, $verbose);
+	$vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, $testdir, $verbose, $ipv6);
     } else { # normal operation
-	$cluster_conf = load_clusterfw_conf(undef, $verbose) if !$cluster_conf;
+	$cluster_conf = load_clusterfw_conf(undef, $verbose, $ipv6) if !$cluster_conf;
 
-	$hostfw_conf = load_hostfw_conf($cluster_conf, undef, $verbose) if !$hostfw_conf;
+	$hostfw_conf = load_hostfw_conf($cluster_conf, undef, $verbose, $ipv6) if !$hostfw_conf;
 
 	$vmdata = read_local_vm_config();
-	$vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, undef, $verbose);
+	$vmfw_configs = read_vm_firewall_configs($cluster_conf, $vmdata, undef, $verbose, $ipv6);
     }
 
     $cluster_conf->{ipset}->{venet0} = [];
@@ -2840,13 +2972,17 @@ sub compile {
 
     ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o venet0 -m set --match-set ${venet0_ipset_chain} dst -j PVEFW-VENET-IN");
 
-    generate_std_chains($ruleset, $hostfw_options);
+    if($ipv6){
+	generate_std_chains($ruleset, $hostfw_options, $pve_std_chainsv6, $ipv6);
+    }else{
+	generate_std_chains($ruleset, $hostfw_options, $pve_std_chains);
+    }
 
     my $hostfw_enable = !(defined($hostfw_options->{enable}) && ($hostfw_options->{enable} == 0));
 
     my $ipset_ruleset = {};
 
-    if ($hostfw_enable) {
+    if ($hostfw_enable && !$ipv6) {
 	eval { enable_host_firewall($ruleset, $hostfw_conf, $cluster_conf); };
 	warn $@ if $@; # just to be sure - should not happen
     }
@@ -2870,9 +3006,9 @@ sub compile {
 
 		my $macaddr = $net->{macaddr};
 		generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
-					     $vmfw_conf, $vmid, 'IN');
+					     $vmfw_conf, $vmid, 'IN', $ipv6);
 		generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
-					     $vmfw_conf, $vmid, 'OUT');
+					     $vmfw_conf, $vmid, 'OUT', $ipv6);
 	    }
 	};
 	warn $@ if $@; # just to be sure - should not happen
@@ -2902,8 +3038,8 @@ sub compile {
 			push @{$cluster_conf->{ipset}->{venet0}}, $venet0ipset;
 		    }
 
-		    generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'IN');
-		    generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'OUT');
+		    generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'IN', $ipv6);
+		    generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'OUT', $ipv6);
 		}
 	    }
 
@@ -2916,9 +3052,9 @@ sub compile {
 		    my $macaddr = $d->{mac};
 		    my $iface = $d->{host_ifname};
 		    generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
-						 $vmfw_conf, $vmid, 'IN');
+						 $vmfw_conf, $vmid, 'IN', $ipv6);
 		    generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
-						 $vmfw_conf, $vmid, 'OUT');
+						 $vmfw_conf, $vmid, 'OUT', $ipv6);
 		}
 	    }
 	};
@@ -2980,11 +3116,11 @@ sub print_sig_rule {
 }
 
 sub get_ruleset_cmdlist {
-    my ($ruleset, $verbose) = @_;
+    my ($ruleset, $verbose, $ipv6) = @_;
 
     my $cmdlist = "*filter\n"; # we pass this to iptables-restore;
 
-    my ($active_chains, $hooks) = iptables_get_chains();
+    my ($active_chains, $hooks) = iptables_get_chains($ipv6);
     my $statushash = get_ruleset_status($ruleset, $active_chains, \&iptables_chain_digest, $verbose);
 
     # create missing chains first
@@ -3095,7 +3231,7 @@ sub get_ipset_cmdlist {
 }
 
 sub apply_ruleset {
-    my ($ruleset, $hostfw_conf, $ipset_ruleset, $verbose) = @_;
+    my ($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6, $verbose) = @_;
 
     enable_bridge_firewall();
 
@@ -3103,6 +3239,7 @@ sub apply_ruleset {
 	get_ipset_cmdlist($ipset_ruleset, undef, $verbose);
 
     my ($cmdlist, $changes) = get_ruleset_cmdlist($ruleset, $verbose);
+    my ($cmdlistv6, $changesv6) = get_ruleset_cmdlist($rulesetv6, $verbose, 1);
 
     if ($verbose) {
 	if ($ipset_changes) {
@@ -3115,11 +3252,17 @@ sub apply_ruleset {
 	    print "iptables changes:\n";
 	    print $cmdlist;
 	}
+
+	if ($changesv6) {
+	    print "ip6tables changes:\n";
+	    print $cmdlistv6;
+	}
     }
 
     ipset_restore_cmdlist($ipset_create_cmdlist);
 
     iptables_restore_cmdlist($cmdlist);
+    iptables_restore_cmdlist($cmdlistv6, 1);
 
     ipset_restore_cmdlist($ipset_delete_cmdlist) if $ipset_delete_cmdlist;
 
@@ -3136,6 +3279,18 @@ sub apply_ruleset {
 	}
     }
 
+    # test ipv6
+    my $active_chainsv6 = iptables_get_chains(1);
+    my $statushashv6 = get_ruleset_status($rulesetv6, $active_chainsv6, \&iptables_chain_digest, 0);
+
+    foreach my $chain (sort keys %$rulesetv6) {
+	my $stat = $statushashv6->{$chain};
+	if ($stat->{action} ne 'exists') {
+	    warn "unable to update chain '$chain'\n";
+	    $errors = 1;
+	}
+    }
+
     die "unable to apply firewall changes\n" if $errors;
 
     update_nf_conntrack_max($hostfw_conf);
@@ -3179,8 +3334,9 @@ sub update_nf_conntrack_tcp_timeout_established {
 }
 
 sub remove_pvefw_chains {
+    my ($ipv6) = @_;
 
-    my ($chash, $hooks) = iptables_get_chains();
+    my ($chash, $hooks) = iptables_get_chains($ipv6);
     my $cmdlist = "*filter\n";
 
     foreach my $h (qw(INPUT OUTPUT FORWARD)) {
@@ -3198,7 +3354,7 @@ sub remove_pvefw_chains {
     }
     $cmdlist .= "COMMIT\n";
 
-    iptables_restore_cmdlist($cmdlist);
+    iptables_restore_cmdlist($cmdlist, $ipv6);
 
     my $ipset_chains = ipset_get_chains();
 
@@ -3230,14 +3386,15 @@ sub update {
 
 	if (!$cluster_options->{enable}) {
 	    PVE::Firewall::remove_pvefw_chains();
+	    PVE::Firewall::remove_pvefw_chains(1);
 	    return;
 	}
 
 	my $hostfw_conf = load_hostfw_conf();
 
 	my ($ruleset, $ipset_ruleset) = compile($cluster_conf, $hostfw_conf);
-
-	apply_ruleset($ruleset, $hostfw_conf, $ipset_ruleset);
+	my ($rulesetv6) = compile($cluster_conf, $hostfw_conf, undef, undef, 1);
+	apply_ruleset($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6);
     };
 
     run_locked($code);
diff --git a/src/pve-firewall b/src/pve-firewall
index befee44..cc15c83 100755
--- a/src/pve-firewall
+++ b/src/pve-firewall
@@ -153,7 +153,7 @@ sub run_server {
 	1 while (waitpid(-1, POSIX::WNOHANG()) > 0);
 	
 	syslog('info' , "clear firewall rules");
-	eval { PVE::Firewall::remove_pvefw_chains(); die "STOP";};
+	eval { PVE::Firewall::remove_pvefw_chains(); PVE::Firewall::remove_pvefw_chains(1); die "STOP";};
 	warn $@ if $@;
 
 	cleanup();
@@ -345,12 +345,12 @@ __PACKAGE__->register_method ({
 	    if ($status eq 'running') {
 		
 		my ($ruleset, $ipset_ruleset) = PVE::Firewall::compile($cluster_conf, undef, undef, $verbose);
-
+		my ($rulesetv6) =  PVE::Firewall::compile($cluster_conf, undef, undef, $verbose, 1);
 		$verbose = 0; # do not show iptables details
 		my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset, $verbose);
 		my ($test, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset, $verbose);
-	      
-		$res->{changes} = ($ipset_changes || $ruleset_changes) ? 1 : 0;
+		my ($testv6, $ruleset6_changes) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, $verbose, 1);
+		$res->{changes} = ($ipset_changes == 1 || $ruleset_changes == 1 || $rulesetv6 == 1) ? 1 : 0;
 	    }
 
 	    return $res;
@@ -377,15 +377,16 @@ __PACKAGE__->register_method ({
 
 	my $code = sub {
 
-	    my $verbose = 1;
+	    my $verbose = 0;
 
 	    my $cluster_conf = PVE::Firewall::load_clusterfw_conf(undef, $verbose); 
 	    my ($ruleset, $ipset_ruleset) = PVE::Firewall::compile($cluster_conf, undef, undef, $verbose);
+	    my ($rulesetv6) =  PVE::Firewall::compile($cluster_conf, undef, undef, $verbose, 1);
 
 	    my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset, $verbose);
 	    my (undef, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset, $verbose);
-
-	    if ($ipset_changes || $ruleset_changes) {
+	    my (undef, $ruleset6_changes) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, $verbose, 1);
+	    if ($ipset_changes || $ruleset_changes || $ruleset6_changes) {
 		print "detected changes\n";
 	    } else {
 		print "no changes\n";
-- 
1.7.10.4




More information about the pve-devel mailing list