],
'NeighborDiscovery' => [
"IPv6 neighbor solicitation, neighbor and router advertisement",
+ { action => 'PARAM', proto => 'icmpv6', dport => 'router-solicitation' },
{ action => 'PARAM', proto => 'icmpv6', dport => 'router-advertisement' },
{ action => 'PARAM', proto => 'icmpv6', dport => 'neighbor-solicitation' },
{ action => 'PARAM', proto => 'icmpv6', dport => 'neighbor-advertisement' },
],
+ 'DHCPv6' => [
+ { action => 'PARAM', proto => 'udp', dport => '546:547', sport => '546:547' },
+ ],
'Trcrt' => [
{ action => 'PARAM', proto => 'udp', dport => '33434:33524' },
{ action => 'PARAM', proto => 'icmpv6', dport => 'echo-request' },
'PVEFW-smurfs' => [
# 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",
+ "-s 0.0.0.0/32 -j RETURN", # allow DHCP
"-m addrtype --src-type BROADCAST -g PVEFW-smurflog",
"-s 224.0.0.0/4 -g PVEFW-smurflog",
],
next if !defined($v);
$data->{$k} = $v;
# Note: digest ignores refs ($rule->{errors})
- $sha->add($k, ':', $v, "\n") if !ref($v); ;
+ # since Digest::SHA expects a series of bytes,
+ # we have to encode the value here to prevent errors when
+ # using utf8 characters (eg. in comments)
+ $sha->add($k, ':', encode_utf8($v), "\n") if !ref($v); ;
}
push @$res, $data;
}
return 1 if $name =~ m/^PVEFW-\S+$/;
- return 1 if $name =~ m/^tap\d+i\d+-(:?IN|OUT)$/;
+ return 1 if $name =~ m/^tap\d+i\d+-(?:IN|OUT)$/;
- return 1 if $name =~ m/^veth\d+.\d+-(:?IN|OUT)$/; # fixme: dev name is configurable
+ return 1 if $name =~ m/^veth\d+i\d+-(?:IN|OUT)$/;
- return 1 if $name =~ m/^fwbr\d+(v\d+)?-(:?FW|IN|OUT|IPS)$/;
- return 1 if $name =~ m/^GROUP-(:?[^\s\-]+)-(:?IN|OUT)$/;
+ return 1 if $name =~ m/^fwbr\d+(v\d+)?-(?:FW|IN|OUT|IPS)$/;
+ return 1 if $name =~ m/^GROUP-(?:$security_group_name_pattern)-(?:IN|OUT)$/;
return undef;
};
if ($rule->{dport}) {
if ($rule->{proto} && $rule->{proto} eq 'icmp') {
# Note: we use dport to store --icmp-type
- die "unknown icmp-type '$rule->{dport}'\n" if !defined($icmp_type_names->{$rule->{dport}});
+ die "unknown icmp-type '$rule->{dport}'\n"
+ if $rule->{dport} !~ /^\d+$/ && !defined($icmp_type_names->{$rule->{dport}});
push @cmd, "-m icmp --icmp-type $rule->{dport}";
} elsif ($rule->{proto} && $rule->{proto} eq 'icmpv6') {
# Note: we use dport to store --icmpv6-type
- die "unknown icmpv6-type '$rule->{dport}'\n" if !defined($icmpv6_type_names->{$rule->{dport}});
+ die "unknown icmpv6-type '$rule->{dport}'\n"
+ if $rule->{dport} !~ /^\d+$/ && !defined($icmpv6_type_names->{$rule->{dport}});
push @cmd, "-m icmpv6 --icmpv6-type $rule->{dport}";
} else {
if ($nbdport > 1) {
}
}
+sub ruleset_chain_add_ndp {
+ my ($ruleset, $chain, $ipversion, $options, $direction) = @_;
+ return if $ipversion != 6 || (defined($options->{ndp}) && !$options->{ndp});
+
+ ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type router-solicitation -j ACCEPT");
+ if ($direction ne 'OUT' || $options->{radv}) {
+ ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type router-advertisement -j ACCEPT");
+ }
+ ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT");
+ ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT");
+}
+
sub ruleset_chain_add_conn_filters {
my ($ruleset, $chain, $accept) = @_;
my $accept = generate_nfqueue($options);
if (!(defined($options->{dhcp}) && $options->{dhcp} == 0)) {
- if ($direction eq 'OUT') {
- ruleset_generate_rule($ruleset, $chain, $ipversion,
- { action => 'PVEFW-SET-ACCEPT-MARK',
- proto => 'udp', sport => 68, dport => 67 });
- } else {
- ruleset_generate_rule($ruleset, $chain, $ipversion,
- { action => 'ACCEPT',
- proto => 'udp', sport => 67, dport => 68 });
+ if ($ipversion == 4) {
+ if ($direction eq 'OUT') {
+ ruleset_generate_rule($ruleset, $chain, $ipversion,
+ { action => 'PVEFW-SET-ACCEPT-MARK',
+ proto => 'udp', sport => 68, dport => 67 });
+ } else {
+ ruleset_generate_rule($ruleset, $chain, $ipversion,
+ { action => 'ACCEPT',
+ proto => 'udp', sport => 67, dport => 68 });
+ }
+ } elsif ($ipversion == 6) {
+ if ($direction eq 'OUT') {
+ ruleset_generate_rule($ruleset, $chain, $ipversion,
+ { action => 'PVEFW-SET-ACCEPT-MARK',
+ proto => 'udp', sport => 546, dport => 547 });
+ } else {
+ ruleset_generate_rule($ruleset, $chain, $ipversion,
+ { action => 'ACCEPT',
+ proto => 'udp', sport => 547, dport => 546 });
+ }
}
+
}
+ ruleset_chain_add_ndp($ruleset, $chain, $ipversion, $options, $direction);
+
if ($direction eq 'OUT') {
if (defined($macaddr) && !(defined($options->{macfilter}) && $options->{macfilter} == 0)) {
ruleset_addrule($ruleset, $chain, "-m mac ! --mac-source $macaddr -j DROP");
}
+ if ($ipversion == 6 && !$options->{radv}) {
+ ruleset_addrule($ruleset, $chain, '-p icmpv6 --icmpv6-type router-advertisement -j DROP');
+ }
if ($ipfilter_ipset) {
ruleset_addrule($ruleset, $chain, "-m set ! --match-set $ipfilter_ipset src -j DROP");
}
ruleset_addrule($ruleset, $chain, "-i lo -j ACCEPT");
+ ruleset_chain_add_ndp($ruleset, $chain, $ipversion, $options);
ruleset_chain_add_conn_filters($ruleset, $chain, 'ACCEPT');
ruleset_chain_add_input_filters($ruleset, $chain, $ipversion, $options, $cluster_conf, $loglevel);
ruleset_addrule($ruleset, $chain, "-o lo -j ACCEPT");
+ ruleset_chain_add_ndp($ruleset, $chain, $ipversion, $options);
ruleset_chain_add_conn_filters($ruleset, $chain, 'ACCEPT');
# we use RETURN because we may want to check other thigs later
my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog";
- if ($line =~ m/^(enable|dhcp|macfilter|ips):\s*(0|1)\s*$/i) {
+ if ($line =~ m/^(enable|dhcp|ndp|radv|macfilter|ips):\s*(0|1)\s*$/i) {
$opt = lc($1);
$value = int($2);
} elsif ($line =~ m/^(log_level_in|log_level_out):\s*(($loglevels)\s*)?$/i) {
my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog";
- if ($line =~ m/^(enable|nosmurfs|tcpflags):\s*(0|1)\s*$/i) {
+ if ($line =~ m/^(enable|nosmurfs|tcpflags|ndp):\s*(0|1)\s*$/i) {
$opt = lc($1);
$value = int($2);
} elsif ($line =~ m/^(log_level_in|log_level_out|tcp_flags_log_level|smurf_log_level):\s*(($loglevels)\s*)?$/i) {
$raw .= "\n";
}
- mkdir $pvefw_conf_dir;
-
my $filename = "$pvefw_conf_dir/$vmid.fw";
- PVE::Tools::file_set_contents($filename, $raw);
+ if ($raw) {
+ mkdir $pvefw_conf_dir;
+ PVE::Tools::file_set_contents($filename, $raw);
+ } else {
+ unlink $filename;
+ }
}
sub remove_vmfw_conf {
unlink $vmfw_conffile;
}
+sub clone_vmfw_conf {
+ my ($vmid, $newid) = @_;
+
+ my $sourcevm_conffile = "$pvefw_conf_dir/$vmid.fw";
+ my $clonevm_conffile = "$pvefw_conf_dir/$newid.fw";
+
+ if (-f $clonevm_conffile) {
+ unlink $clonevm_conffile;
+ }
+ if (-f $sourcevm_conffile) {
+ my $data = PVE::Tools::file_get_contents($sourcevm_conffile);
+ PVE::Tools::file_set_contents($clonevm_conffile, $data);
+ }
+}
+
sub read_vm_firewall_configs {
my ($cluster_conf, $vmdata, $dir, $verbose) = @_;
}
}
- mkdir $pvefw_conf_dir;
- PVE::Tools::file_set_contents($clusterfw_conf_filename, $raw);
+ if ($raw) {
+ mkdir $pvefw_conf_dir;
+ PVE::Tools::file_set_contents($clusterfw_conf_filename, $raw);
+ } else {
+ unlink $clusterfw_conf_filename;
+ }
}
sub load_hostfw_conf {
$raw .= "\n";
}
- PVE::Tools::file_set_contents($hostfw_conf_filename, $raw);
+ if ($raw) {
+ PVE::Tools::file_set_contents($hostfw_conf_filename, $raw);
+ } else {
+ unlink $hostfw_conf_filename;
+ }
}
sub compile {