use PVE::JSONSchema qw(register_standard_option get_standard_option);
use PVE::Cluster;
use PVE::ProcFSTools;
-use PVE::Tools qw($IPV4RE);
+use PVE::Tools qw($IPV4RE $IPV6RE);
use File::Basename;
use File::Path;
use IO::File;
my $max_ipset_name_length = 64;
my $max_group_name_length = 20;
-PVE::JSONSchema::register_format('IPv4orCIDR', \&pve_verify_ipv4_or_cidr);
-sub pve_verify_ipv4_or_cidr {
+PVE::JSONSchema::register_format('IPorCIDR', \&pve_verify_ip_or_cidr);
+sub pve_verify_ip_or_cidr {
my ($cidr, $noerr) = @_;
- 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";
die "value does not look like a valid IP address or CIDR network\n";
}
-PVE::JSONSchema::register_format('IPv4orCIDRorAlias', \&pve_verify_ipv4_or_cidr_or_alias);
-sub pve_verify_ipv4_or_cidr_or_alias {
+PVE::JSONSchema::register_format('IPorCIDRorAlias', \&pve_verify_ip_or_cidr_or_alias);
+sub pve_verify_ip_or_cidr_or_alias {
my ($cidr, $noerr) = @_;
return if $cidr =~ m/^(?:$ip_alias_pattern)$/;
- if ($cidr =~ m!^(?:$IPV4RE)(/(\d+))?$!) {
- return $cidr if Net::IP->new($cidr);
- return undef if $noerr;
- die Net::IP::Error() . "\n";
- }
- return undef if $noerr;
- die "value does not look like a valid IP address or CIDR network\n";
+ return pve_verify_ip_or_cidr($cidr, $noerr);
}
PVE::JSONSchema::register_standard_option('ipset-name', {
my $new_ipversion = Net::IP::ip_is_ipv6($ip->ip()) ? 6 : 4;
die "detected mixed ipv4/ipv6 addresses in address list '$str'\n"
- if defined($ipversion) && ($new_ipversion != $ipversion);
+ if $ipversion && ($new_ipversion != $ipversion);
$ipversion = $new_ipversion;
}
my ($rule, $cluster_conf, $fw_conf, $rule_env, $noerr) = @_;
my $allow_groups = $rule_env eq 'group' ? 0 : 1;
- my $ipversion = undef;
my $allow_iface = $rule_env_iface_lookup->{$rule_env};
die "unknown rule_env '$rule_env'\n" if !defined($allow_iface); # should not happen
};
my $check_ipset_or_alias_property = sub {
- my ($name) = @_;
+ my ($name, $expected_ipversion) = @_;
if (my $value = $rule->{$name}) {
if ($value =~ m/^\+/) {
} elsif ($value =~ m/^${ip_alias_pattern}$/){
my $alias = lc($value);
&$add_error($name, "no such alias '$value'")
- if !($cluster_conf->{aliases}->{$alias} || ($fw_conf && $fw_conf->{aliases}->{$alias}))
+ if !($cluster_conf->{aliases}->{$alias} || ($fw_conf && $fw_conf->{aliases}->{$alias}));
+
+ my $e = $fw_conf->{aliases}->{$alias} if $fw_conf;
+ $e = $cluster_conf->{aliases}->{$alias} if !$e && $cluster_conf;
+
+ die "detected mixed ipv4/ipv6 adresses in rule\n"
+ if $expected_ipversion && ($expected_ipversion != $e->{ipversion});
}
}
};
if !$rule->{proto};
}
+ my $ipversion;
+
if ($rule->{source}) {
eval { $ipversion = parse_address_list($rule->{source}); };
&$add_error('source', $@) if $@;
- &$check_ipset_or_alias_property('source');
+ &$check_ipset_or_alias_property('source', $ipversion);
}
if ($rule->{dest}) {
- eval { $ipversion = parse_address_list($rule->{dest}); };
+ eval {
+ my $dest_ipversion = parse_address_list($rule->{dest});
+ die "detected mixed ipv4/ipv6 adresses in rule\n"
+ if $ipversion && $dest_ipversion && ($dest_ipversion != $ipversion);
+ $ipversion = $dest_ipversion if $dest_ipversion;
+ };
&$add_error('dest', $@) if $@;
- &$check_ipset_or_alias_property('dest');
+ &$check_ipset_or_alias_property('dest', $ipversion);
}
if ($rule->{macro} && !$error_count) {
}
sub ruleset_add_group_rule {
- my ($ruleset, $cluster_conf, $chain, $rule, $direction, $action) = @_;
+ my ($ruleset, $cluster_conf, $chain, $rule, $direction, $action, $ipversion) = @_;
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, $ipversion);
}
if ($direction eq 'OUT' && $rule->{iface_out}) {
}
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, $ipversion) = @_;
my $lc_direction = lc($direction);
foreach my $rule (@$rules) {
next if $rule->{iface} && $rule->{iface} ne $netid;
next if !$rule->{enable} || $rule->{errors};
+ next if $rule->{ipversion} && ($rule->{ipversion} != $ipversion);
+
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, $ipversion);
} else {
next if $rule->{type} ne $lc_direction;
eval {
}
sub generate_venet_rules_direction {
- my ($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, $direction) = @_;
+ my ($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, $direction, $ipversion) = @_;
my $lc_direction = lc($direction);
ruleset_create_vm_chain($ruleset, $chain, $options, undef, undef, $direction);
- 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, $ipversion);
# implement policy
my $policy;
}
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, $ipversion) = @_;
my $lc_direction = lc($direction);
ruleset_create_vm_chain($ruleset, $tapchain, $options, $macaddr, $ipfilter_ipset, $direction);
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, $ipversion);
ruleset_generate_vm_ipsrules($ruleset, $options, $direction, $iface);
}
sub enable_host_firewall {
- my ($ruleset, $hostfw_conf, $cluster_conf) = @_;
+ my ($ruleset, $hostfw_conf, $cluster_conf, $ipversion) = @_;
my $options = $hostfw_conf->{options};
my $cluster_options = $cluster_conf->{options};
eval {
if ($rule->{type} eq 'group') {
- ruleset_add_group_rule($ruleset, $cluster_conf, $chain, $rule, 'IN', $accept_action);
+ ruleset_add_group_rule($ruleset, $cluster_conf, $chain, $rule, 'IN', $accept_action, $ipversion);
} elsif ($rule->{type} eq 'in') {
ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => $accept_action, REJECT => "PVEFW-reject" },
undef, $cluster_conf, $hostfw_conf);
$rule->{iface_out} = $rule->{iface} if $rule->{iface};
eval {
if ($rule->{type} eq 'group') {
- ruleset_add_group_rule($ruleset, $cluster_conf, $chain, $rule, 'OUT', $accept_action);
+ ruleset_add_group_rule($ruleset, $cluster_conf, $chain, $rule, 'OUT', $accept_action, $ipversion);
} elsif ($rule->{type} eq 'out') {
ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => $accept_action, REJECT => "PVEFW-reject" },
undef, $cluster_conf, $hostfw_conf);
}
sub generate_group_rules {
- my ($ruleset, $cluster_conf, $group) = @_;
+ my ($ruleset, $cluster_conf, $group, $ipversion) = @_;
my $rules = $cluster_conf->{groups}->{$group};
foreach my $rule (@$rules) {
next if $rule->{type} ne 'in';
+ next if $rule->{ipversion} && $rule->{ipversion} ne $ipversion;
ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, undef, $cluster_conf);
}
foreach my $rule (@$rules) {
next if $rule->{type} ne 'out';
+ next if $rule->{ipversion} && $rule->{ipversion} ne $ipversion;
# 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,
return $cidr;
}
+sub parse_ip_or_cidr {
+ my ($cidr) = @_;
+
+ my $ipversion;
+
+ if ($cidr =~ m!^(?:$IPV6RE)(/(\d+))?$!) {
+ $cidr =~ s|/128$||;
+ $ipversion = 6;
+ } elsif ($cidr =~ m!^(?:$IPV4RE)(/(\d+))?$!) {
+ $cidr =~ s|/32$||;
+ $ipversion = 4;
+ } else {
+ die "value does not look like a valid IP address or CIDR network\n";
+ }
+
+ return wantarray ? ($cidr, $ipversion) : $cidr;
+}
+
sub parse_alias {
my ($line) = @_;
if ($line =~ m/^(\S+)\s(\S+)$/) {
my ($name, $cidr) = ($1, $2);
- $cidr =~ s|/32$||;
- pve_verify_ipv4_or_cidr($cidr);
+ my $ipversion;
+
+ ($cidr, $ipversion) = parse_ip_or_cidr($cidr);
+
my $data = {
name => $name,
cidr => $cidr,
+ ipversion => $ipversion,
};
$data->{comment} = $comment if $comment;
return $data;
if ($cidr =~ m/^${ip_alias_pattern}$/) {
resolve_alias($cluster_conf, $res, $cidr); # make sure alias exists
} else {
- $cidr =~ s|/32$||;
- pve_verify_ipv4_or_cidr_or_alias($cidr);
+ $cidr = parse_ip_or_cidr($cidr);
}
};
if (my $err = $@) {
my $ipset_ruleset = {};
- if ($hostfw_enable) {
- eval { enable_host_firewall($ruleset, $hostfw_conf, $cluster_conf); };
+ # currently pveproxy don't works with ipv6, so let's generate host fw ipv4 only for the moment
+ if ($hostfw_enable && ($ipversion == 4)) {
+ eval { enable_host_firewall($ruleset, $hostfw_conf, $cluster_conf, $ipversion); };
warn $@ if $@; # just to be sure - should not happen
}
my $macaddr = $net->{macaddr};
generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
- $vmfw_conf, $vmid, 'IN');
+ $vmfw_conf, $vmid, 'IN', $ipversion);
generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
- $vmfw_conf, $vmid, 'OUT');
+ $vmfw_conf, $vmid, 'OUT', $ipversion);
}
};
warn $@ if $@; # just to be sure - should not happen
if ($conf->{ip_address} && $conf->{ip_address}->{value}) {
my $ip = $conf->{ip_address}->{value};
$ip =~ s/\s+/,/g;
- parse_address_list($ip); # make sure we have a valid $ip list
- my @ips = split(',', $ip);
+ my @ips = ();
- foreach my $singleip (@ips) {
- my $venet0ipset = {};
- $venet0ipset->{cidr} = $singleip;
- push @{$cluster_conf->{ipset}->{venet0}}, $venet0ipset;
+ foreach my $singleip (split(',', $ip)) {
+ my $singleip_ver = parse_address_list($singleip); # make sure we have a valid $ip list
+ push @{$cluster_conf->{ipset}->{venet0}}, { cidr => $singleip };
+ push @ips, $singleip if $singleip_ver == $ipversion;
}
- generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'IN');
- generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, 'OUT');
+ if (scalar(@ips)) {
+ my $ip_list = join(',', @ips);
+ generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip_list, 'IN', $ipversion);
+ generate_venet_rules_direction($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip_list, 'OUT', $ipversion);
+ }
}
}
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', $ipversion);
generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
- $vmfw_conf, $vmid, 'OUT');
+ $vmfw_conf, $vmid, 'OUT', $ipversion);
}
}
};