use File::Path;
use IO::File;
use Net::IP;
-use PVE::Tools qw(run_command lock_file);
+use PVE::Tools qw(run_command lock_file dir_glob_foreach);
use Encode;
my $hostfw_conf_filename = "/etc/pve/local/host.fw";
optional => 1,
});
-my $security_group_pattern = '[A-Za-z][A-Za-z0-9\-\_]+';
+my $security_group_name_pattern = '[A-Za-z][A-Za-z0-9\-\_]+';
+my $ip_alias_pattern = '[A-Za-z][A-Za-z0-9\-\_]+';
PVE::JSONSchema::register_standard_option('pve-security-group-name', {
description => "Security Group name.",
type => 'string',
- pattern => $security_group_pattern,
+ pattern => $security_group_name_pattern,
minLength => 2,
maxLength => 20,
});
my ($str) = @_;
return if $str =~ m/^(\+)(\S+)$/; # ipset ref
- return if $str =~ m/^${security_group_pattern}$/; # aliases
+ return if $str =~ m/^${ip_alias_pattern}$/;
my $count = 0;
my $iprange = 0;
description => "Rule action ('ACCEPT', 'DROP', 'REJECT') or security group name.",
type => 'string',
optional => 1,
- pattern => $security_group_pattern,
+ pattern => $security_group_name_pattern,
maxLength => 20,
minLength => 2,
},
raise_param_exc({ type => "security groups not allowed"})
if !$allow_groups;
raise_param_exc({ action => "invalid characters in security group name"})
- if $rule->{action} !~ m/^${security_group_pattern}$/;
+ if $rule->{action} !~ m/^${security_group_name_pattern}$/;
} else {
raise_param_exc({ type => "unknown rule type '$type'"});
}
return 1 if $name =~ m/^venet0-\d+-(:?IN|OUT)$/;
- return 1 if $name =~ m/^vmbr\d+-(:?FW|IN|OUT|IPS)$/;
+ return 1 if $name =~ m/^vmbr\d+(v\d+)?-(:?FW|IN|OUT|IPS)$/;
return 1 if $name =~ m/^GROUP-(:?[^\s\-]+)-(:?IN|OUT)$/;
return undef;
my $dest = $rule->{dest};
if ($source) {
- if ($source =~ m/^(\+)(\S+)$/) {
- die "no such ipset $2" if !$cluster_conf->{ipset}->{$2};
- push @cmd, "-m set --match-set PVEFW-$2 src";
-
- } elsif ($source =~ m/^${security_group_pattern}$/){
- die "no such alias $source" if !$cluster_conf->{aliases}->{$source};
+ if ($source =~ m/^\+/) {
+ if ($source =~ m/^\+(${security_group_name_pattern})$/) {
+ die "no such ipset '$1'\n" if !$cluster_conf->{ipset}->{$1};
+ push @cmd, "-m set --match-set PVEFW-$1 src";
+ } else {
+ die "invalid security group name '$source'\n";
+ }
+ } elsif ($source =~ m/^${ip_alias_pattern}$/){
+ die "no such alias $source\n" if !$cluster_conf->{aliases}->{$source};
push @cmd, "-s $cluster_conf->{aliases}->{$source}";
} elsif ($source =~ m/\-/){
}
if ($dest) {
- if ($dest =~ m/^(\+)(\S+)$/) {
- die "no such ipset $2" if !$cluster_conf->{ipset}->{$2};
- push @cmd, "-m set --match-set PVEFW-$2 dst";
-
- } elsif ($dest =~ m/^${security_group_pattern}$/){
+ if ($dest =~ m/^\+/) {
+ if ($dest =~ m/^\+(${security_group_name_pattern})$/) {
+ die "no such ipset '$1'\n" if !$cluster_conf->{ipset}->{$1};
+ push @cmd, "-m set --match-set PVEFW-$1 dst";
+ } else {
+ die "invalid security group name '$dest'\n";
+ }
+ } elsif ($dest =~ m/^${ip_alias_pattern}$/){
die "no such alias $dest" if !$cluster_conf->{aliases}->{$dest};
push @cmd, "-d $cluster_conf->{aliases}->{$dest}";
$rules = [ $rule ];
}
+ # update all or nothing
+
+ my @cmds = ();
foreach my $tmp (@$rules) {
if (my $cmdstr = ruleset_generate_cmdstr($ruleset, $chain, $tmp, $actions, $goto, $cluster_conf)) {
- ruleset_addrule($ruleset, $chain, $cmdstr);
+ push @cmds, $cmdstr;
}
}
+
+ foreach my $cmdstr (@cmds) {
+ ruleset_addrule($ruleset, $chain, $cmdstr);
+ }
}
sub ruleset_generate_rule_insert {
}
sub generate_bridge_chains {
- my ($ruleset, $hostfw_conf, $bridge, $routing_table) = @_;
+ my ($ruleset, $hostfw_conf, $bridge, $routing_table, $bridges_config) = @_;
my $options = $hostfw_conf->{options} || {};
if (!ruleset_chain_exist($ruleset, "$bridge-OUT")) {
ruleset_create_chain($ruleset, "$bridge-OUT");
+
+ if($options->{optimize}){
+ foreach my $interface (@{$bridges_config->{$bridge}}) {
+ ruleset_addrule($ruleset, "$bridge-OUT", "-m physdev --physdev-is-bridged --physdev-in $interface -g PVEFW-SET-ACCEPT-MARK");
+ }
+ }
+
ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-in -j $bridge-OUT");
ruleset_insertrule($ruleset, "PVEFW-INPUT", "-i $bridge -m physdev --physdev-is-in -j $bridge-OUT");
}
if (!ruleset_chain_exist($ruleset, "$bridge-IN")) {
ruleset_create_chain($ruleset, "$bridge-IN");
+
+ if($options->{optimize}){
+ foreach my $interface (@{$bridges_config->{$bridge}}) {
+ ruleset_addrule($ruleset, "$bridge-IN", "-m physdev --physdev-is-bridged --physdev-out $interface -j ACCEPT");
+ }
+ }
+
ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-out -j $bridge-IN");
ruleset_addrule($ruleset, "$bridge-FW", "-m mark --mark 1 -j ACCEPT");
# accept traffic to unmanaged bridge ports
# plug the tap chain to bridge chain
if ($direction eq 'IN') {
- ruleset_insertrule($ruleset, "$bridge-IN",
+ ruleset_addrule($ruleset, "$bridge-IN",
"-m physdev --physdev-is-bridged --physdev-out $iface -j $tapchain");
} else {
- ruleset_insertrule($ruleset, "$bridge-OUT",
+ ruleset_addrule($ruleset, "$bridge-OUT",
"-m physdev --physdev-in $iface -j $tapchain");
}
}
die "wrong number of rule elements\n" if scalar(@data) != 3;
die "groups disabled\n" if !$allow_groups;
- die "invalid characters in group name\n" if $action !~ m/^${security_group_pattern}$/;
+ die "invalid characters in group name\n" if $action !~ m/^${security_group_name_pattern}$/;
} else {
die "unknown rule type '$type'\n";
}
my $nomatch = $1;
my $cidr = $2;
- if($cidr !~ m/^${security_group_pattern}$/) {
+ if($cidr !~ m/^${ip_alias_pattern}$/) {
$cidr =~ s|/32$||;
eval { pve_verify_ipv4_or_cidr($cidr); };
return $vmdata;
};
+sub read_bridges_config {
+
+ my $bridgehash = {};
+
+ dir_glob_foreach('/sys/class/net', 'vmbr(\d+)', sub {
+ my ($bridge) = @_;
+
+ dir_glob_foreach("/sys/class/net/$bridge/brif", '((eth|bond)(\d+)(\.(\d+))?)', sub {
+ my ($interface) = @_;
+ push @{$bridgehash->{$bridge}}, $interface;
+ });
+ });
+
+ return $bridgehash;
+};
+
sub load_vmfw_conf {
my ($vmid) = @_;
my $nethash = {};
foreach my $entry (@$options) {
my $cidr = $entry->{cidr};
- #check aliases
- if ($cidr =~ m/^${security_group_pattern}$/){
- die "no such alias $cidr" if !$aliases->{$cidr};
- $entry->{cidr} = $aliases->{$cidr};
+ if ($cidr =~ m/^${ip_alias_pattern}$/) {
+ if ($aliases->{$cidr}) {
+ $entry->{cidr} = $aliases->{$cidr};
+ } else {
+ warn "no such alias '$cidr'\n" if !$aliases->{$cidr};
+ }
}
$nethash->{$entry->{cidr}} = $entry;
}
my $vmfw_configs = read_vm_firewall_configs($vmdata);
my $routing_table = read_proc_net_route();
+
+ my $bridges_config = read_bridges_config();
my $ipset_ruleset = {};
generate_ipset_chains($ipset_ruleset, $cluster_conf);
$bridge .= "v$net->{tag}" if $net->{tag};
- generate_bridge_chains($ruleset, $hostfw_conf, $bridge, $routing_table);
+ generate_bridge_chains($ruleset, $hostfw_conf, $bridge, $routing_table, $bridges_config);
my $macaddr = $net->{macaddr};
generate_tap_rules_direction($ruleset, $cluster_conf, $hostfw_conf, $iface, $netid, $macaddr,
next; # fixme?
}
- generate_bridge_chains($ruleset, $hostfw_conf, $bridge, $routing_table);
+ generate_bridge_chains($ruleset, $hostfw_conf, $bridge, $routing_table, $bridges_config);
my $macaddr = $d->{mac};
my $iface = $d->{host_ifname};
enable_bridge_firewall();
- update_nf_conntrack_max($hostfw_conf);
-
- update_nf_conntrack_tcp_timeout_established($hostfw_conf);
-
my ($ipset_create_cmdlist, $ipset_delete_cmdlist, $ipset_changes) =
get_ipset_cmdlist($ipset_ruleset, undef, $verbose);
}
die "unable to apply firewall changes\n" if $errors;
+
+ update_nf_conntrack_max($hostfw_conf);
+
+ update_nf_conntrack_tcp_timeout_established($hostfw_conf);
+
}
sub update_nf_conntrack_max {