]> git.proxmox.com Git - pve-firewall.git/blobdiff - test/fwtester.pl
fwtester: do not set packet default values
[pve-firewall.git] / test / fwtester.pl
index 832d35eea66a2557a3ff684f81039029c46a107c..555e1516700ad15769450adbec98c4b27bff96cf 100755 (executable)
@@ -5,12 +5,28 @@ use strict;
 use warnings;
 use Data::Dumper;
 use PVE::Firewall;
+use Getopt::Long;
+use File::Basename;
 
 my $mark;
 my $trace;
 
+my $outside_iface = 'eth0';
+my $outside_bridge = 'vmbr0';
+
 my $debug = 0;
 
+sub print_usage_and_exit {
+    die "usage: $0 [--debug] [testfile [testid]]\n";
+}
+
+if (!GetOptions ('debug' => \$debug)) {
+    print_usage_and_exit();
+}
+
+my $testfilename = shift;
+my $testid = shift;
+
 sub add_trace {
     my ($text) = @_;
 
@@ -192,9 +208,17 @@ sub route_packet {
        $pkg->{iface_in} = $pkg->{iface_out} = undef;
        $pkg->{physdev_in} = $pkg->{physdev_out} = undef;
 
-       if ($route_state eq 'host') {
+       if ($route_state eq 'from-outside') {
+           $next_route_state = $outside_bridge || die 'internal error';
+           $next_physdev_in = $outside_iface || die 'internal error';
+       } elsif ($route_state eq 'host') {
 
-           if ($target->{type} eq 'ct') {
+           if ($target->{type} eq 'outside') {
+               $pkg->{iface_in} = 'lo';
+               $pkg->{iface_out} = $outside_bridge;
+               $chain = 'PVEFW-OUTPUT';
+               $next_route_state = $outside_iface
+           } elsif ($target->{type} eq 'ct') {
                $pkg->{iface_in} = 'lo';
                $pkg->{iface_out} = 'venet0';
                $chain = 'PVEFW-OUTPUT';
@@ -217,6 +241,13 @@ sub route_packet {
                $pkg->{iface_out} = 'lo';
                $next_route_state = 'host';
 
+           } elsif ($target->{type} eq 'outside') {
+               
+               $chain = 'PVEFW-FORWARD';
+               $pkg->{iface_in} = 'venet0';
+               $pkg->{iface_out} = $outside_bridge;
+               $next_route_state = $outside_iface;
+
            } elsif ($target->{type} eq 'vm') {
 
                $chain = 'PVEFW-FORWARD';
@@ -265,6 +296,24 @@ sub route_packet {
                $pkg->{iface_out} = 'lo';
                $next_route_state = 'host';
 
+               if ($route_state eq $outside_bridge) {
+
+               } else {
+
+               }
+
+           } elsif ($target->{type} eq 'outside') {
+
+               $chain = 'PVEFW-FORWARD';
+               $pkg->{iface_in} = $route_state;
+               $pkg->{iface_out} = $outside_bridge;
+               $pkg->{physdev_in} = $physdev_in;
+               # conditionally set physdev_out (same behavior as kernel)
+               if ($route_state eq $outside_bridge) {
+                   $pkg->{physdev_out} = $outside_iface || die 'internal error';
+               }
+               $next_route_state = $outside_iface;
+
            } elsif ($target->{type} eq 'ct') {
 
                $chain = 'PVEFW-FORWARD';
@@ -275,16 +324,12 @@ sub route_packet {
            } elsif ($target->{type} eq 'vm') {
 
                $chain = 'PVEFW-FORWARD';
+               $pkg->{iface_in} = $route_state;
+               $pkg->{iface_out} = $target->{bridge};
+               $pkg->{physdev_in} = $physdev_in;
+               # conditionally set physdev_out (same behavior as kernel)
                if ($route_state eq $target->{bridge}) {
-                   $pkg->{iface_in} = $route_state;
-                   $pkg->{iface_out} = $route_state;
-                   $pkg->{physdev_in} = $physdev_in;
                    $pkg->{physdev_out} = $target->{fwpr} || die 'internal error';
-               } else {
-                   $pkg->{iface_in} = $route_state;
-                   $pkg->{iface_out} = $route_state;
-                   $pkg->{physdev_in} = $physdev_in;
-                   # do not set physdev_out (same behavior as kernel)
                }
                $next_route_state = 'fwbr-in';
 
@@ -350,18 +395,21 @@ sub simulate_firewall {
     my $from = delete $test->{from} || die "missing 'from' field";
     my $to = delete $test->{to} || die "missing 'to' field";
     my $action = delete $test->{action} || die "missing 'action'";
-
+    
+    my $testid = delete $test->{id};
+    
     die "from/to needs to be different" if $from eq $to;
 
     my $pkg = {
        proto => 'tcp',
-       sport => '1234',
-       dport => '4321',
-       source => '10.11.12.13',
-       dest => '10.11.12.14',
+       sport => undef,
+       dport => undef,
+       source => undef,
+       dest => undef,
     };
 
     while (my ($k,$v) = each %$test) {
+       die "unknown attribute '$k'\n" if !exists($pkg->{$k});
        $pkg->{$k} = $v;
     }
 
@@ -372,6 +420,9 @@ sub simulate_firewall {
     if ($from eq 'host') {
        $from_info->{type} = 'host';
        $start_state = 'host';
+    } elsif ($from eq 'outside') {
+       $from_info->{type} = 'outside';
+       $start_state = 'from-outside';
     } elsif ($from =~ m/^ct(\d+)$/) {
        my $vmid = $1;
        $from_info = extract_ct_info($vmdata, $vmid);
@@ -395,6 +446,9 @@ sub simulate_firewall {
     if ($to eq 'host') {
        $target->{type} = 'host';
        $target->{iface} = 'host';
+    } elsif ($to eq 'outside') {
+       $target->{type} = 'outside';
+       $target->{iface} = $outside_iface;
     } elsif ($to =~ m/^ct(\d+)$/) {
        my $vmid = $1;
        $target = extract_ct_info($vmdata, $vmid);
@@ -421,25 +475,30 @@ sub simulate_firewall {
 }
 
 sub run_tests {
-    my ($vmdata, $testdir) = @_;
+    my ($vmdata, $testdir, $testfile, $testid) = @_;
+
+    $testfile = 'tests' if !$testfile;
 
     $vmdata->{testdir} = $testdir;
 
     my ($ruleset, $ipset_ruleset) = 
        PVE::Firewall::compile(undef, undef, $vmdata);
 
-    my $testfile = "$testdir/tests";
-    my $fh = IO::File->new($testfile) ||
-       die "unable to open '$testfile' - $!\n";
+    my $filename = "$testdir/$testfile";
+    my $fh = IO::File->new($filename) ||
+       die "unable to open '$filename' - $!\n";
 
+    my $testcount = 0;
     while (defined(my $line = <$fh>)) {
        next if $line =~ m/^\s*$/;
        next if $line =~ m/^#.*$/;
        if ($line =~ m/^\{.*\}\s*$/) {
            my $test = eval $line;
            die $@ if $@;
+           next if defined($testid) && (!defined($test->{id}) || ($testid ne $test->{id}));
            $trace = '';
            print Dumper($ruleset) if $debug;
+           $testcount++;
            eval { simulate_firewall($ruleset, $ipset_ruleset, $vmdata, $test); };
            if (my $err = $@) {
 
@@ -447,7 +506,7 @@ sub run_tests {
 
                print "$trace\n" if !$debug;
 
-               print "$testfile line $.: $line";
+               print "$filename line $.: $line";
 
                print "test failed: $err\n";
 
@@ -458,7 +517,9 @@ sub run_tests {
        }
     }
 
-    print "PASS: $testfile\n";
+    die "no tests found\n" if $testcount <= 0;
+
+    print "PASS: $filename\n";
 
     return undef;
 }
@@ -486,9 +547,26 @@ my $vmdata = {
     },
 };
 
-foreach my $dir (<test-*>) {
-    next if ! -d $dir;
-    run_tests($vmdata, $dir);
+if ($testfilename) {
+    my $testfile;
+    my $dir;
+
+    if (-d $testfilename) {
+       $dir = $testfilename;
+    } elsif (-f $testfilename) {
+       $dir = dirname($testfilename);
+       $testfile = basename($testfilename);
+    } else {
+       die "no such file/dir '$testfilename'\n"; 
+    }
+
+    run_tests($vmdata, $dir, $testfile, $testid);
+
+} else { 
+    foreach my $dir (<test-*>) {
+       next if ! -d $dir;
+       run_tests($vmdata, $dir);
+    }
 }
 
 print "OK - all tests passed\n";