]> git.proxmox.com Git - pve-firewall.git/blobdiff - src/PVE/Firewall.pm
remove bridge chains
[pve-firewall.git] / src / PVE / Firewall.pm
index bf8f1931dfd425a300b2a059cc3f926997309362..835b26a5e8b243f480867489a85083dac17ba778 100644 (file)
@@ -15,7 +15,7 @@ use File::Basename;
 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";
@@ -56,12 +56,28 @@ PVE::JSONSchema::register_standard_option('ipset-name', {
     maxLength => 20,                     
 });
 
-my $security_group_pattern = '[A-Za-z][A-Za-z0-9\-\_]+';
+PVE::JSONSchema::register_standard_option('pve-fw-alias', {
+    description => "Alias name.",
+    type => 'string',
+    pattern => '[A-Za-z][A-Za-z0-9\-\_]+',
+    minLength => 2,
+    maxLength => 20,                     
+});
+
+PVE::JSONSchema::register_standard_option('pve-fw-loglevel' => {
+    description => "Log level.",
+    type => 'string', 
+    enum => ['emerg', 'alert', 'crit', 'err', 'warning', 'notice', 'info', 'debug', 'nolog'],
+    optional => 1,
+});
+
+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,                             
 });
@@ -705,6 +721,7 @@ sub parse_address_list {
     my ($str) = @_;
 
     return if $str =~ m/^(\+)(\S+)$/; # ipset ref
+    return if $str =~ m/^${ip_alias_pattern}$/;
 
     my $count = 0;
     my $iprange = 0;
@@ -797,7 +814,7 @@ sub copy_opject_with_digest {
        $sha->add($k, ':', $v, "\n");
     }
 
-    my $digest = $sha->b64digest;
+    my $digest = $sha->hexdigest;
 
     $res->{digest} = $digest;
 
@@ -821,7 +838,7 @@ sub copy_list_with_digest {
        push @$res, $data;
     }
 
-    my $digest = $sha->b64digest;
+    my $digest = $sha->hexdigest;
 
     foreach my $entry (@$res) {
        $entry->{digest} = $digest;
@@ -847,7 +864,7 @@ my $rule_properties = {
        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,
     },
@@ -979,7 +996,7 @@ sub verify_rule {
        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'"});
     }
@@ -1085,7 +1102,7 @@ sub iptables_get_chains {
 
        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/^fwbr\d+(v\d+)?-(:?FW|IN|OUT|IPS)$/;
        return 1 if $name =~ m/^GROUP-(:?[^\s\-]+)-(:?IN|OUT)$/;
 
        return undef;
@@ -1197,10 +1214,18 @@ sub ruleset_generate_cmdstr {
     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";
-
+        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}$/){
+           my $alias = lc($source);
+           my $e = $cluster_conf->{aliases}->{$alias};
+           die "no such alias $source\n" if !$e;
+           push @cmd, "-s $e->{cidr}";
         } elsif ($source =~ m/\-/){
            push @cmd, "-m iprange --src-range $source";
 
@@ -1210,15 +1235,23 @@ sub ruleset_generate_cmdstr {
     }
 
     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";
-
+        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}$/){
+           my $alias = lc($source);
+           my $e = $cluster_conf->{aliases}->{$alias};
+           die "no such alias $dest" if !$e;
+           push @cmd, "-d $e->{cidr}";
         } elsif ($dest =~ m/^(\d+)\.(\d+).(\d+).(\d+)\-(\d+)\.(\d+).(\d+).(\d+)$/){
            push @cmd, "-m iprange --dst-range $dest";
 
        } else {
-           push @cmd, "-s $dest";
+           push @cmd, "-d $dest";
         }
     }
 
@@ -1286,11 +1319,18 @@ sub ruleset_generate_rule {
        $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 {
@@ -1358,36 +1398,7 @@ sub ruleset_addlog {
 
     $logrule = "$rule $logrule" if defined($rule);
 
-    ruleset_addrule($ruleset, $chain, $logrule)
-}
-
-sub generate_bridge_chains {
-    my ($ruleset, $hostfw_conf, $bridge, $routing_table) = @_;
-
-    my $options = $hostfw_conf->{options} || {};
-
-    die "error: detected direct route to bridge '$bridge'\n"
-       if !$options->{allow_bridge_route} && $routing_table->{$bridge};
-
-    if (!ruleset_chain_exist($ruleset, "$bridge-FW")) {
-       ruleset_create_chain($ruleset, "$bridge-FW");
-       ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o $bridge -m physdev --physdev-is-out -j $bridge-FW");
-       ruleset_addrule($ruleset, "PVEFW-FORWARD", "-i $bridge -m physdev --physdev-is-in -j $bridge-FW");
-    }
-
-    if (!ruleset_chain_exist($ruleset, "$bridge-OUT")) {
-       ruleset_create_chain($ruleset, "$bridge-OUT");
-       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");
-       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
-       ruleset_addrule($ruleset, "$bridge-FW", "-m physdev --physdev-is-out -j ACCEPT ");
-    }
+    ruleset_addrule($ruleset, $chain, $logrule);
 }
 
 sub ruleset_add_chain_policy {
@@ -1418,12 +1429,12 @@ sub ruleset_add_chain_policy {
 }
 
 sub ruleset_create_vm_chain {
-    my ($ruleset, $chain, $options, $macaddr, $direction) = @_;
+    my ($ruleset, $chain, $options, $host_options, $macaddr, $direction) = @_;
 
     ruleset_create_chain($ruleset, $chain);
     my $accept = generate_nfqueue($options);
 
-    if (!(defined($options->{nosmurfs}) && $options->{nosmurfs} == 0)) {
+    if (!(defined($host_options->{nosmurfs}) && $host_options->{nosmurfs} == 0)) {
        ruleset_addrule($ruleset, $chain, "-m conntrack --ctstate INVALID,NEW -j PVEFW-smurfs");
     }
 
@@ -1437,7 +1448,7 @@ sub ruleset_create_vm_chain {
        }
     }
 
-    if ($options->{tcpflags}) {
+    if ($host_options->{tcpflags}) {
        ruleset_addrule($ruleset, $chain, "-p tcp -j PVEFW-tcpflags");
     }
 
@@ -1461,6 +1472,8 @@ sub ruleset_generate_vm_rules {
 
     my $lc_direction = lc($direction);
 
+    my $in_accept = generate_nfqueue($options);
+
     foreach my $rule (@$rules) {
        next if $rule->{iface} && $rule->{iface} ne $netid;
        next if !$rule->{enable};
@@ -1479,13 +1492,18 @@ sub ruleset_generate_vm_rules {
 
        } else {
            next if $rule->{type} ne $lc_direction;
-           if ($direction eq 'OUT') {
-               ruleset_generate_rule($ruleset, $chain, $rule,
-                                     { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, undef, $cluster_conf);
-           } else {
-               my $accept = generate_nfqueue($options);
-               ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => $accept , REJECT => "PVEFW-reject" }, undef, $cluster_conf);
-           }
+           eval {
+               if ($direction eq 'OUT') {
+                   ruleset_generate_rule($ruleset, $chain, $rule,
+                                         { ACCEPT => "PVEFW-SET-ACCEPT-MARK", REJECT => "PVEFW-reject" }, 
+                                         undef, $cluster_conf);
+               } else {
+                   ruleset_generate_rule($ruleset, $chain, $rule, 
+                                         { ACCEPT => $in_accept , REJECT => "PVEFW-reject" }, 
+                                         undef, $cluster_conf);
+               }
+           };
+           warn $@ if $@;
        }
     }
 }
@@ -1493,45 +1511,38 @@ sub ruleset_generate_vm_rules {
 sub generate_nfqueue {
     my ($options) = @_;
 
-    my $action = "";
-    if($options->{ips}){
-       $action = "NFQUEUE";
-       if($options->{ips_queues} && $options->{ips_queues} =~ m/^(\d+)(:(\d+))?$/) {
-           if(defined($3) && defined($1)) {
+    if ($options->{ips}) {
+       my $action = "NFQUEUE";
+       if ($options->{ips_queues} && $options->{ips_queues} =~ m/^(\d+)(:(\d+))?$/) {
+           if (defined($3) && defined($1)) {
                $action .= " --queue-balance $1:$3";
-           }elsif (defined($1)) {
+           } elsif (defined($1)) {
                $action .= " --queue-num $1";
            }
        }
        $action .= " --queue-bypass" if $feature_ipset_nomatch; #need kernel 3.10
-    }else{
-       $action = "ACCEPT";
+       return $action;
+    } else {
+       return "ACCEPT";
     }
-
-    return $action;
 }
 
 sub ruleset_generate_vm_ipsrules {
-    my ($ruleset, $options, $direction, $iface, $bridge) = @_;
+    my ($ruleset, $options, $direction, $iface) = @_;
 
     if ($options->{ips} && $direction eq 'IN') {
        my $nfqueue = generate_nfqueue($options);
 
-       if (!ruleset_chain_exist($ruleset, "$bridge-IPS")) {
+       if (!ruleset_chain_exist($ruleset, "PVEFW-IPS")) {
            ruleset_create_chain($ruleset, "PVEFW-IPS");
        }
 
-       if (!ruleset_chain_exist($ruleset, "$bridge-IPS")) {
-           ruleset_create_chain($ruleset, "$bridge-IPS");
-           ruleset_insertrule($ruleset, "PVEFW-IPS", "-o $bridge -m physdev --physdev-is-out -j $bridge-IPS");
-       }
-
-        ruleset_addrule($ruleset, "$bridge-IPS", "-m physdev --physdev-out $iface --physdev-is-bridged -j $nfqueue");
+        ruleset_addrule($ruleset, "PVEFW-IPS", "-m physdev --physdev-out $iface --physdev-is-bridged -j $nfqueue");
     }
 }
 
 sub generate_venet_rules_direction {
-    my ($ruleset, $cluster_conf, $vmfw_conf, $vmid, $ip, $direction) = @_;
+    my ($ruleset, $cluster_conf, $hostfw_conf, $vmfw_conf, $vmid, $ip, $direction) = @_;
 
     parse_address_list($ip); # make sure we have a valid $ip list
 
@@ -1540,11 +1551,12 @@ sub generate_venet_rules_direction {
     my $rules = $vmfw_conf->{rules};
 
     my $options = $vmfw_conf->{options};
+    my $hostfw_options = $vmfw_conf->{options};
     my $loglevel = get_option_log_level($options, "log_level_${lc_direction}");
 
     my $chain = "venet0-$vmid-$direction";
 
-    ruleset_create_vm_chain($ruleset, $chain, $options, undef, $direction);
+    ruleset_create_vm_chain($ruleset, $chain, $options, $hostfw_options, undef, $direction);
 
     ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $chain, 'venet', $direction);
 
@@ -1586,22 +1598,23 @@ sub generate_venet_rules_direction {
 }
 
 sub generate_tap_rules_direction {
-    my ($ruleset, $cluster_conf, $iface, $netid, $macaddr, $vmfw_conf, $vmid, $bridge, $direction) = @_;
+    my ($ruleset, $cluster_conf, $hostfw_conf, $iface, $netid, $macaddr, $vmfw_conf, $vmid, $direction) = @_;
 
     my $lc_direction = lc($direction);
 
     my $rules = $vmfw_conf->{rules};
 
     my $options = $vmfw_conf->{options};
+    my $hostfw_options = $hostfw_conf->{options};
     my $loglevel = get_option_log_level($options, "log_level_${lc_direction}");
 
     my $tapchain = "$iface-$direction";
 
-    ruleset_create_vm_chain($ruleset, $tapchain, $options, $macaddr, $direction);
+    ruleset_create_vm_chain($ruleset, $tapchain, $options, $hostfw_options, $macaddr, $direction);
 
     ruleset_generate_vm_rules($ruleset, $rules, $cluster_conf, $tapchain, $netid, $direction, $options);
 
-    ruleset_generate_vm_ipsrules($ruleset, $options, $direction, $iface, $bridge);
+    ruleset_generate_vm_ipsrules($ruleset, $options, $direction, $iface);
 
     # implement policy
     my $policy;
@@ -1618,22 +1631,21 @@ sub generate_tap_rules_direction {
 
     # plug the tap chain to bridge chain
     if ($direction eq 'IN') {
-       ruleset_insertrule($ruleset, "$bridge-IN",
-                          "-m physdev --physdev-is-bridged --physdev-out $iface -j $tapchain");
+       ruleset_addrule($ruleset, "PVEFW-FWBR-IN",
+                       "-m physdev --physdev-is-bridged --physdev-out $iface -j $tapchain");
     } else {
-       ruleset_insertrule($ruleset, "$bridge-OUT",
-                          "-m physdev --physdev-in $iface -j $tapchain");
+       ruleset_addrule($ruleset, "PVEFW-FWBR-OUT",
+                       "-m physdev --physdev-is-bridged --physdev-in $iface -j $tapchain");
     }
 }
 
 sub enable_host_firewall {
     my ($ruleset, $hostfw_conf, $cluster_conf) = @_;
 
-    # fixme: allow security groups
-
     my $options = $hostfw_conf->{options};
     my $cluster_options = $cluster_conf->{options};
     my $rules = $hostfw_conf->{rules};
+    my $cluster_rules = $cluster_conf->{rules};
 
     # host inbound firewall
     my $chain = "PVEFW-HOST-IN";
@@ -1659,7 +1671,8 @@ sub enable_host_firewall {
     # we use RETURN because we need to check also tap rules
     my $accept_action = 'RETURN';
 
-    foreach my $rule (@$rules) {
+    # add host rules first, so that cluster wide rules can be overwritten
+    foreach my $rule (@$rules, @$cluster_rules) {
        next if $rule->{type} ne 'in';
        ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => $accept_action, REJECT => "PVEFW-reject" }, undef, $cluster_conf);
     }
@@ -1684,7 +1697,8 @@ sub enable_host_firewall {
     # we use RETURN because we may want to check other thigs later
     $accept_action = 'RETURN';
 
-    foreach my $rule (@$rules) {
+    # add host rules first, so that cluster wide rules can be overwritten
+    foreach my $rule (@$rules, @$cluster_rules) {
        next if $rule->{type} ne 'out';
        ruleset_generate_rule($ruleset, $chain, $rule, { ACCEPT => $accept_action, REJECT => "PVEFW-reject" }, undef, $cluster_conf);
     }
@@ -1778,7 +1792,7 @@ sub parse_fw_rule {
        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";
     }
@@ -1824,7 +1838,7 @@ sub parse_vmfw_option {
 
     my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog";
 
-    if ($line =~ m/^(enable|dhcp|macfilter|nosmurfs|tcpflags|ips):\s*(0|1)\s*$/i) {
+    if ($line =~ m/^(enable|dhcp|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) {
@@ -1851,7 +1865,7 @@ sub parse_hostfw_option {
 
     my $loglevels = "emerg|alert|crit|err|warning|notice|info|debug|nolog";
 
-    if ($line =~ m/^(enable|nosmurfs|tcpflags|allow_bridge_route|optimize):\s*(0|1)\s*$/i) {
+    if ($line =~ m/^(enable|nosmurfs|tcpflags|optimize):\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) {
@@ -1887,6 +1901,27 @@ sub parse_clusterfw_option {
     return ($opt, $value);
 }
 
+sub parse_clusterfw_alias {
+    my ($line) = @_;
+
+    # we can add single line comments to the end of the line
+    my $comment = decode('utf8', $1) if $line =~ s/\s*#\s*(.*?)\s*$//;
+
+    if ($line =~ m/^(\S+)\s(\S+)$/) {
+       my ($name, $cidr) = ($1, $2);
+       $cidr =~ s|/32$||;
+       pve_verify_ipv4_or_cidr($cidr);
+       my $data = {
+           name => $name,
+           cidr => $cidr,
+       };
+       $data->{comment} = $comment  if $comment;
+       return $data;
+    }
+
+    return undef;
+}
+
 sub parse_vm_fw_rules {
     my ($filename, $fh) = @_;
 
@@ -1992,6 +2027,7 @@ sub parse_cluster_fw_rules {
     my $res = { 
        rules => [], 
        options => {}, 
+       aliases => {}, 
        groups => {}, 
        group_comments => {}, 
        ipset => {} ,
@@ -2010,6 +2046,11 @@ sub parse_cluster_fw_rules {
            next;
        }
 
+       if ($line =~ m/^\[aliases\]$/i) {
+           $section = 'aliases';
+           next;
+       }
+
        if ($line =~ m/^\[group\s+(\S+)\]\s*(?:#\s*(.*?)\s*)?$/i) {
            $section = 'groups';
            $group = lc($1);
@@ -2046,6 +2087,12 @@ sub parse_cluster_fw_rules {
                $res->{options}->{$opt} = $value;
            };
            warn "$prefix: $@" if $@;
+       } elsif ($section eq 'aliases') {
+           eval {
+               my $data = parse_clusterfw_alias($line);
+               $res->{aliases}->{lc($data->{name})} = $data;
+           };
+           warn "$prefix: $@" if $@;
        } elsif ($section eq 'rules') {
            my $rule;
            eval { $rule = parse_fw_rule($line, 1, 1); };
@@ -2070,12 +2117,14 @@ sub parse_cluster_fw_rules {
            my $nomatch = $1;
            my $cidr = $2;
 
-           $cidr =~ s|/32$||;
+           if($cidr !~ m/^${ip_alias_pattern}$/) {
+               $cidr =~ s|/32$||;
            
-           eval { pve_verify_ipv4_or_cidr($cidr); };
-           if (my $err = $@) {
-               warn "$prefix: $cidr - $err";
-               next;
+               eval { pve_verify_ipv4_or_cidr($cidr); };
+               if (my $err = $@) {
+                   warn "$prefix: $cidr - $err";
+                   next;
+               }
            }
 
            my $entry = { cidr => $cidr }; 
@@ -2137,6 +2186,22 @@ sub read_local_vm_config {
     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) = @_;
 
@@ -2199,6 +2264,24 @@ my $format_options = sub {
     return $raw;
 };
 
+my $format_aliases = sub {
+    my ($aliases) = @_;
+
+    my $raw = '';
+
+    $raw .= "[ALIASES]\n\n";
+    foreach my $k (keys %$aliases) {
+       my $e = $aliases->{$k};
+       $raw .= "$e->{name} $e->{cidr}";
+       $raw .= " # " . encode('utf8', $e->{comment})
+           if $e->{comment} && $e->{comment} !~ m/^\s*$/;
+       $raw .= "\n";
+    }
+    $raw .= "\n";
+
+    return $raw;
+};
+
 my $format_ipset = sub {
     my ($options) = @_;
 
@@ -2229,7 +2312,7 @@ sub save_vmfw_conf {
     my $options = $vmfw_conf->{options};
     $raw .= &$format_options($options) if scalar(keys %$options);
 
-    my $rules = $vmfw_conf->{rules};
+    my $rules = $vmfw_conf->{rules} || [];
     if (scalar(@$rules)) {
        $raw .= "[RULES]\n\n";
        $raw .= &$format_rules($rules, 1);
@@ -2277,6 +2360,7 @@ sub generate_std_chains {
 
     # same as shorewall smurflog.
     my $chain = 'PVEFW-smurflog';
+    $pve_std_chains->{$chain} = [];
 
     push @{$pve_std_chains->{$chain}}, get_log_rule_base($chain, 0, "DROP: ", $loglevel) if $loglevel;
     push @{$pve_std_chains->{$chain}}, "-j DROP";
@@ -2284,6 +2368,8 @@ sub generate_std_chains {
     # same as shorewall logflags action.
     $loglevel = get_option_log_level($options, 'tcp_flags_log_level');
     $chain = 'PVEFW-logflags';
+    $pve_std_chains->{$chain} = [];
+
     # fixme: is this correctly logged by pvewf-logger? (ther is no --log-ip-options for NFLOG)
     push @{$pve_std_chains->{$chain}}, get_log_rule_base($chain, 0, "DROP: ", $loglevel) if $loglevel;
     push @{$pve_std_chains->{$chain}}, "-j DROP";
@@ -2304,12 +2390,12 @@ sub generate_ipset_chains {
     my ($ipset_ruleset, $fw_conf) = @_;
 
     foreach my $ipset (keys %{$fw_conf->{ipset}}) {
-       generate_ipset($ipset_ruleset, "PVEFW-$ipset", $fw_conf->{ipset}->{$ipset});
+       generate_ipset($ipset_ruleset, "PVEFW-$ipset", $fw_conf->{ipset}->{$ipset}, $fw_conf->{aliases});
     }
 }
 
 sub generate_ipset {
-    my ($ipset_ruleset, $name, $options) = @_;
+    my ($ipset_ruleset, $name, $options, $aliases) = @_;
 
     my $hashsize = scalar(@$options);
     if ($hashsize <= 64) {
@@ -2323,7 +2409,18 @@ sub generate_ipset {
     # remove duplicates
     my $nethash = {};
     foreach my $entry (@$options) {
-       $nethash->{$entry->{cidr}} = $entry;
+       my $cidr = $entry->{cidr};
+       if ($cidr =~ m/^${ip_alias_pattern}$/) {
+           my $alias = lc($cidr);
+           if ($aliases->{$alias}) {
+               $entry->{cidr} = $aliases->{$alias}->{cidr};
+               $nethash->{$entry->{cidr}} = $entry;
+           } else {
+               warn "no such alias '$cidr'\n" if !$aliases->{$alias};
+           }
+       } else {
+           $nethash->{$entry->{cidr}} = $entry;
+       }
     }
 
     foreach my $cidr (sort keys %$nethash) {
@@ -2374,34 +2471,6 @@ sub read_pvefw_status {
     return $status;
 }
 
-# fixme: move to pve-common PVE::ProcFSTools
-sub read_proc_net_route {
-    my $filename = "/proc/net/route";
-
-    my $res = {};
-
-    my $fh = IO::File->new ($filename, "r");
-    return $res if !$fh;
-
-    my $int_to_quad = sub {
-       return join '.' => map { ($_[0] >> 8*(3-$_)) % 256 } (3, 2, 1, 0);
-    };
-
-    while (defined(my $line = <$fh>)) {
-       next if $line =~/^Iface\s+Destination/; # skip head
-       my ($iface, $dest, $gateway, $metric, $mask, $mtu) = (split(/\s+/, $line))[0,1,2,6,7,8];
-       push @{$res->{$iface}}, {
-           dest => &$int_to_quad(hex($dest)),
-           gateway => &$int_to_quad(hex($gateway)),
-           mask => &$int_to_quad(hex($mask)),
-           metric => $metric,
-           mtu => $mtu,
-       };
-    }
-
-    return $res;
-}
-
 sub load_clusterfw_conf {
 
     my $cluster_conf = {};
@@ -2420,6 +2489,9 @@ sub save_clusterfw_conf {
     my $options = $cluster_conf->{options};
     $raw .= &$format_options($options) if scalar(keys %$options);
 
+    my $aliases = $cluster_conf->{aliases};
+    $raw .= &$format_aliases($aliases) if scalar(keys %$aliases);
     foreach my $ipset (sort keys %{$cluster_conf->{ipset}}) {
        if (my $comment = $cluster_conf->{ipset_comments}->{$ipset}) {
            my $utf8comment = encode('utf8', $comment);
@@ -2491,8 +2563,7 @@ sub compile {
     my $vmdata = read_local_vm_config();
     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);
@@ -2503,9 +2574,30 @@ sub compile {
     ruleset_create_chain($ruleset, "PVEFW-OUTPUT");
 
     ruleset_create_chain($ruleset, "PVEFW-FORWARD");
+    
+    ruleset_create_chain($ruleset, "PVEFW-FWBR-IN");
+    ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m physdev --physdev-is-bridged --physdev-in link+ -j PVEFW-FWBR-IN");
+
+    ruleset_create_chain($ruleset, "PVEFW-FWBR-OUT");
+    ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m physdev --physdev-is-bridged --physdev-out link+ -j PVEFW-FWBR-OUT");
 
     my $hostfw_options = $hostfw_conf->{options} || {};
 
+    # fixme: what log level should we use here?
+    my $loglevel = get_option_log_level($hostfw_options, "log_level_out");
+
+    if($hostfw_options->{optimize}){
+
+       my $accept = ruleset_chain_exist($ruleset, "PVEFW-IPS") ? "PVEFW-IPS" : "ACCEPT";
+       ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate INVALID -j DROP");
+       ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate RELATED,ESTABLISHED -j $accept");
+    }
+
+    if ($cluster_conf->{ipset}->{blacklist}){
+       ruleset_addlog($ruleset, "PVEFW-FORWARD", 0, "DROP: ", $loglevel, "-m set --match-set PVEFW-blacklist src");
+       ruleset_addrule($ruleset, "PVEFW-FORWARD", "-m set --match-set PVEFW-blacklist src -j DROP");
+    }
+
     generate_std_chains($ruleset, $hostfw_options);
 
     my $hostfw_enable = !(defined($hostfw_options->{enable}) && ($hostfw_options->{enable} == 0));
@@ -2525,18 +2617,11 @@ sub compile {
            next if !$net;
            my $iface = "tap${vmid}i$1";
 
-           my $bridge = $net->{bridge};
-           next if !$bridge; # fixme: ?
-
-           $bridge .= "v$net->{tag}" if $net->{tag};
-
-           generate_bridge_chains($ruleset, $hostfw_conf, $bridge, $routing_table);
-
            my $macaddr = $net->{macaddr};
-           generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
-                                        $vmfw_conf, $vmid, $bridge, 'IN');
-           generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
-                                        $vmfw_conf, $vmid, $bridge, 'OUT');
+           generate_tap_rules_direction($ruleset, $cluster_conf, $hostfw_conf, $iface, $netid, $macaddr,
+                                        $vmfw_conf, $vmid, 'IN');
+           generate_tap_rules_direction($ruleset, $cluster_conf, $hostfw_conf, $iface, $netid, $macaddr,
+                                        $vmfw_conf, $vmid, 'OUT');
        }
     }
 
@@ -2550,55 +2635,25 @@ sub compile {
 
        if ($conf->{ip_address} && $conf->{ip_address}->{value}) {
            my $ip = $conf->{ip_address}->{value};
-           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, $hostfw_conf, $vmfw_conf, $vmid, $ip, 'IN');
+           generate_venet_rules_direction($ruleset, $cluster_conf, $hostfw_conf, $vmfw_conf, $vmid, $ip, 'OUT');
        }
 
        if ($conf->{netif} && $conf->{netif}->{value}) {
            my $netif = PVE::OpenVZ::parse_netif($conf->{netif}->{value});
            foreach my $netid (keys %$netif) {
                my $d = $netif->{$netid};
-               my $bridge = $d->{bridge};
-               if (!$bridge) {
-                   warn "no bridge device for CT $vmid iface '$netid'\n";
-                   next; # fixme?
-               }
-
-               generate_bridge_chains($ruleset, $hostfw_conf, $bridge, $routing_table);
 
                my $macaddr = $d->{mac};
                my $iface = $d->{host_ifname};
-               generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
-                                            $vmfw_conf, $vmid, $bridge, 'IN');
-               generate_tap_rules_direction($ruleset, $cluster_conf, $iface, $netid, $macaddr,
-                                            $vmfw_conf, $vmid, $bridge, 'OUT');
+               generate_tap_rules_direction($ruleset, $cluster_conf, $hostfw_conf, $iface, $netid, $macaddr,
+                                            $vmfw_conf, $vmid, 'IN');
+               generate_tap_rules_direction($ruleset, $cluster_conf, $hostfw_conf, $iface, $netid, $macaddr,
+                                            $vmfw_conf, $vmid, 'OUT');
            }
        }
     }
 
-    if($hostfw_options->{optimize}){
-
-       my $accept = ruleset_chain_exist($ruleset, "PVEFW-IPS") ? "PVEFW-IPS" : "ACCEPT";
-       ruleset_insertrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate RELATED,ESTABLISHED -j $accept");
-       ruleset_insertrule($ruleset, "PVEFW-FORWARD", "-m conntrack --ctstate INVALID -j DROP");
-    }
-
-    # fixme: what log level should we use here?
-    my $loglevel = get_option_log_level($hostfw_options, "log_level_out");
-
-    # fixme: should we really block inter-bridge traffic?
-
-    # always allow traffic from containers?
-    ruleset_addrule($ruleset, "PVEFW-FORWARD", "-i venet0 -j RETURN");
-
-    # disable interbridge routing
-    ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o vmbr+ -j PVEFW-Drop");
-    ruleset_addrule($ruleset, "PVEFW-FORWARD", "-i vmbr+ -j PVEFW-Drop");
-    ruleset_addlog($ruleset, "PVEFW-FORWARD", 0, "DROP: ", $loglevel, "-o vmbr+");
-    ruleset_addlog($ruleset, "PVEFW-FORWARD", 0, "DROP: ", $loglevel, "-i vmbr+");
-    ruleset_addrule($ruleset, "PVEFW-FORWARD", "-o vmbr+ -j DROP");
-    ruleset_addrule($ruleset, "PVEFW-FORWARD", "-i vmbr+ -j DROP");
-
     return ($ruleset, $ipset_ruleset);
 }
 
@@ -2766,10 +2821,6 @@ sub apply_ruleset {
 
     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);
 
@@ -2808,6 +2859,11 @@ sub apply_ruleset {
     }
 
     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 {
@@ -2875,18 +2931,14 @@ sub update {
        my $cluster_conf = load_clusterfw_conf();
        my $cluster_options = $cluster_conf->{options};
 
-       my $enable = !(defined($cluster_options->{enable}) && ($cluster_options->{enable} == 0));
+       my $enable = $cluster_options->{enable};
 
        my $status = read_pvefw_status();
 
        die "Firewall is disabled - cannot start\n" if !$enable && $start;
 
        if (!$enable) {
-           if ($status ne 'stopped') {
-               print "trying to stop firewall (firewall is disabled)\n" if $verbose;
-               PVE::Firewall::remove_pvefw_chains();
-               PVE::Firewall::save_pvefw_status('stopped');
-           }
+           PVE::Firewall::remove_pvefw_chains();
            print "Firewall disabled\n" if $verbose;
            return;
        }