],
'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' },
}
sub parse_port_name_number_or_range {
- my ($str) = @_;
+ my ($str, $dport) = @_;
my $services = PVE::Firewall::get_etc_services();
my $count = 0;
my $port = $1;
die "invalid port '$port'\n" if $port > 65535;
} else {
- if ($icmp_type_names->{$item}) {
+ if ($dport && $icmp_type_names->{$item}) {
$icmp_port = 1;
- } elsif ($icmpv6_type_names->{$item}) {
+ } elsif ($dport && $icmpv6_type_names->{$item}) {
$icmp_port = 1;
} else {
die "invalid port '$item'\n" if !$services->{byname}->{$item};
return $count;
}
-PVE::JSONSchema::register_format('pve-fw-port-spec', \&pve_fw_verify_port_spec);
-sub pve_fw_verify_port_spec {
+PVE::JSONSchema::register_format('pve-fw-sport-spec', \&pve_fw_verify_sport_spec);
+sub pve_fw_verify_sport_spec {
my ($portstr) = @_;
- parse_port_name_number_or_range($portstr);
+ parse_port_name_number_or_range($portstr, 0);
+
+ return $portstr;
+}
+
+PVE::JSONSchema::register_format('pve-fw-dport-spec', \&pve_fw_verify_dport_spec);
+sub pve_fw_verify_dport_spec {
+ my ($portstr) = @_;
+
+ parse_port_name_number_or_range($portstr, 1);
return $portstr;
}
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;
}
optional => 1,
},
sport => {
- type => 'string', format => 'pve-fw-port-spec',
+ type => 'string', format => 'pve-fw-sport-spec',
optional => 1,
},
dport => {
- type => 'string', format => 'pve-fw-port-spec',
+ type => 'string', format => 'pve-fw-dport-spec',
optional => 1,
},
comment => {
}
if ($rule->{dport}) {
- eval { parse_port_name_number_or_range($rule->{dport}); };
+ eval { parse_port_name_number_or_range($rule->{dport}, 1); };
&$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}, 0); };
&$add_error('sport', $@) if $@;
&$add_error('proto', "missing property - 'sport' requires this property")
if !$rule->{proto};
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}, 1) : 0;
+ my $nbsport = defined($rule->{sport}) ? parse_port_name_number_or_range($rule->{sport}, 0) : 0;
my @cmd = ();
}
sub ruleset_chain_add_ndp {
- my ($ruleset, $chain, $ipversion, $options) = @_;
+ my ($ruleset, $chain, $ipversion, $options, $direction, $accept) = @_;
return if $ipversion != 6 || (defined($options->{ndp}) && !$options->{ndp});
- ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type router-solicitation -j ACCEPT");
- 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");
+ ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type router-solicitation $accept");
+ if ($direction ne 'OUT' || $options->{radv}) {
+ ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type router-advertisement $accept");
+ }
+ ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type neighbor-solicitation $accept");
+ ruleset_addrule($ruleset, $chain, "-p icmpv6 --icmpv6-type neighbor-advertisement $accept");
}
sub ruleset_chain_add_conn_filters {
}
}
- ruleset_chain_add_ndp($ruleset, $chain, $ipversion, $options);
-
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, "-j MARK --set-mark 0"); # clear mark
}
+
+ my $accept_action = $direction eq 'OUT' ? '-g PVEFW-SET-ACCEPT-MARK' : "-j $accept";
+ ruleset_chain_add_ndp($ruleset, $chain, $ipversion, $options, $direction, $accept_action);
}
sub ruleset_add_group_rule {
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_ndp($ruleset, $chain, $ipversion, $options, 'IN', '-j RETURN');
ruleset_chain_add_input_filters($ruleset, $chain, $ipversion, $options, $cluster_conf, $loglevel);
# we use RETURN because we need to check also tap rules
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
$accept_action = 'RETURN';
+ ruleset_chain_add_ndp($ruleset, $chain, $ipversion, $options, 'OUT', "-j $accept_action");
ruleset_addrule($ruleset, $chain, "-p igmp -j $accept_action"); # important for multicast
my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog";
- if ($line =~ m/^(enable|dhcp|ndp|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) {