apply ebtables_ruleset
authorAlexandre Derumier <aderumier@odiso.com>
Wed, 28 Mar 2018 08:53:29 +0000 (10:53 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Wed, 28 Mar 2018 09:35:06 +0000 (11:35 +0200)
need ebtables-save && ebtables-restore,  ebtables debian package don't include them.

ebtables-restore need to restore the full ruleset (atomicaly),
so we can't update only 1 chain

Signed-off-by: Alexandre Derumier <aderumier at odiso.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
Reviewed-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Tested-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
src/PVE/Firewall.pm
src/PVE/Service/pve_firewall.pm

index 82c6ac9..5452167 100644 (file)
@@ -1711,6 +1711,12 @@ sub ipset_restore_cmdlist {
     run_command("/sbin/ipset restore", input => $cmdlist, errmsg => "ipset_restore_cmdlist");
 }
 
+sub ebtables_restore_cmdlist {
+    my ($cmdlist) = @_;
+
+    run_command("/sbin/ebtables-restore", input => $cmdlist, errmsg => "ebtables_restore_cmdlist");
+}
+
 sub iptables_get_chains {
     my ($iptablescmd) = @_;
 
@@ -1821,6 +1827,42 @@ sub ipset_get_chains {
     return $res;
 }
 
+sub ebtables_get_chains {
+
+    my $res = {};
+    my $chains = {};
+
+    my $parser = sub {
+       my $line = shift;
+       return if $line =~ m/^#/;
+       return if $line =~ m/^\s*$/;
+       if ($line =~ m/^(?:\S+)\s(PVEFW-\S+)\s(?:\S+).*/) {
+           my $chain = $1;
+           $line =~ s/\s+$//;
+           push @{$chains->{$chain}}, $line;
+       } elsif ($line =~ m/^(?:\S+)\s(tap\d+i\d+-(:?IN|OUT))\s(?:\S+).*/) {
+           my $chain = $1;
+           $line =~ s/\s+$//;
+           push @{$chains->{$chain}}, $line;
+       } elsif ($line =~ m/^(?:\S+)\s(veth\d+i\d+-(:?IN|OUT))\s(?:\S+).*/) {
+           my $chain = $1;
+           $line =~ s/\s+$//;
+           push @{$chains->{$chain}}, $line;
+       } else {
+           # simply ignore the rest
+           return;
+       }
+    };
+
+    run_command("/sbin/ebtables-save", outfunc => $parser);
+
+    # compute digest for each chain
+    foreach my $chain (keys %$chains) {
+       $res->{$chain} = iptables_chain_digest($chains->{$chain});
+    }
+    return $res;
+}
+
 # substitude action of rule according to action hash
 sub rule_substitude_action {
     my ($rule, $actions) = @_;
@@ -3802,6 +3844,39 @@ sub get_ruleset_cmdlist {
     return wantarray ? ($cmdlist, $changes) : $cmdlist;
 }
 
+sub get_ebtables_cmdlist {
+    my ($ruleset, $verbose) = @_;
+
+    my $changes = 0;
+    my $cmdlist = "*filter\n";
+
+    my ($active_chains, $hooks) = ebtables_get_chains();
+    my $statushash = get_ruleset_status($ruleset, $active_chains, \&iptables_chain_digest, $verbose);
+
+    # create chains first
+    foreach my $chain (sort keys %$ruleset) {
+       my $stat = $statushash->{$chain};
+       die "internal error" if !$stat;
+       $cmdlist .= ":$chain ACCEPT\n";
+    }
+
+    if ($ruleset->{FORWARD}) {
+       $cmdlist .= "-A FORWARD -j PVEFW-FORWARD\n";
+    }
+
+    foreach my $chain (sort keys %$ruleset) {
+       my $stat = $statushash->{$chain};
+       die "internal error" if !$stat;
+       $changes = 1 if ($stat->{action} ne 'exists');
+
+       foreach my $cmd (@{$ruleset->{$chain}}) {
+           $cmdlist .= "$cmd\n";
+       }
+    }
+
+    return wantarray ? ($cmdlist, $changes) : $cmdlist;
+}
+
 sub get_ipset_cmdlist {
     my ($ruleset, $verbose) = @_;
 
@@ -3861,7 +3936,7 @@ sub get_ipset_cmdlist {
 }
 
 sub apply_ruleset {
-    my ($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6, $verbose) = @_;
+    my ($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6, $ebtables_ruleset, $verbose) = @_;
 
     enable_bridge_firewall();
 
@@ -3870,6 +3945,7 @@ sub apply_ruleset {
 
     my ($cmdlist, $changes) = get_ruleset_cmdlist($ruleset, $verbose);
     my ($cmdlistv6, $changesv6) = get_ruleset_cmdlist($rulesetv6, $verbose, "ip6tables");
+    my ($ebtables_cmdlist, $ebtables_changes) = get_ebtables_cmdlist($ebtables_ruleset, $verbose);
 
     if ($verbose) {
        if ($ipset_changes) {
@@ -3887,6 +3963,11 @@ sub apply_ruleset {
            print "ip6tables changes:\n";
            print $cmdlistv6;
        }
+
+       if ($ebtables_changes) {
+           print "ebtables changes:\n";
+           print $ebtables_cmdlist;
+       }
     }
 
     my $tmpfile = "$pve_fw_status_dir/ipsetcmdlist1";
@@ -3909,6 +3990,11 @@ sub apply_ruleset {
 
     ipset_restore_cmdlist($ipset_delete_cmdlist) if $ipset_delete_cmdlist;
 
+    ebtables_restore_cmdlist($ebtables_cmdlist);
+
+    $tmpfile = "$pve_fw_status_dir/ebtablescmdlist";
+    PVE::Tools::file_set_contents($tmpfile, $ebtables_cmdlist || '');
+
     # test: re-read status and check if everything is up to date
     my $active_chains = iptables_get_chains();
     my $statushash = get_ruleset_status($ruleset, $active_chains, \&iptables_chain_digest, 0);
@@ -3933,6 +4019,17 @@ sub apply_ruleset {
        }
     }
 
+    my $active_ebtables_chains = ebtables_get_chains();
+    my $ebtables_statushash = get_ruleset_status($ebtables_ruleset, $active_ebtables_chains, \&iptables_chain_digest, 0);
+
+    foreach my $chain (sort keys %$ebtables_ruleset) {
+       my $stat = $ebtables_statushash->{$chain};
+       if ($stat->{action} ne 'exists') {
+           warn "ebtables : unable to update chain '$chain'\n";
+           $errors = 1;
+       }
+    }
+
     die "unable to apply firewall changes\n" if $errors;
 
     update_nf_conntrack_max($hostfw_conf);
@@ -4050,7 +4147,7 @@ sub update {
 
        my ($ruleset, $ipset_ruleset, $rulesetv6, $ebtables_ruleset) = compile($cluster_conf, $hostfw_conf);
 
-       apply_ruleset($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6);
+       apply_ruleset($ruleset, $hostfw_conf, $ipset_ruleset, $rulesetv6, $ebtables_ruleset);
     };
 
     run_locked($code);
index a9a3435..5a0dd04 100755 (executable)
@@ -170,8 +170,9 @@ __PACKAGE__->register_method ({
                my (undef, undef, $ipset_changes) = PVE::Firewall::get_ipset_cmdlist($ipset_ruleset, $verbose);
                my ($test, $ruleset_changes) = PVE::Firewall::get_ruleset_cmdlist($ruleset, $verbose);
                my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, $verbose, "ip6tables");
++              my (undef, $ebtables_changes) = PVE::Firewall::get_ebtables_cmdlist($ebtables_ruleset, $verbose);
 
-               $res->{changes} = ($ipset_changes || $ruleset_changes || $ruleset_changesv6) ? 1 : 0;
+               $res->{changes} = ($ipset_changes || $ruleset_changes || $ruleset_changesv6 || $ebtables_changes) ? 1 : 0;
            }
 
            return $res;
@@ -212,7 +213,10 @@ __PACKAGE__->register_method ({
            print "\nip6tables cmdlist:\n";
            my (undef, $ruleset_changesv6) = PVE::Firewall::get_ruleset_cmdlist($rulesetv6, $verbose, "ip6tables");
 
-           if ($ipset_changes || $ruleset_changes || $ruleset_changesv6) {
+           print "\nebtables cmdlist:\n";
+           my (undef, $ebtables_changes) = PVE::Firewall::get_ebtables_cmdlist($ebtables_ruleset, $verbose);
+
+           if ($ipset_changes || $ruleset_changes || $ruleset_changesv6 || $ebtables_changes) {
                print "detected changes\n";
            } else {
                print "no changes\n";