]> git.proxmox.com Git - pve-firewall.git/blobdiff - src/PVE/FirewallSimulator.pm
bump version to 5.0.5
[pve-firewall.git] / src / PVE / FirewallSimulator.pm
index f2c2fbc2c9d72954700e1153629ddb05ce6c504a..fa5ed0e5e8baa4b6ea10fe7f0035bf97ccfc5028 100644 (file)
@@ -7,6 +7,12 @@ use PVE::Firewall;
 use File::Basename;
 use Net::IP;
 
+use base 'Exporter';
+our @EXPORT_OK = qw(
+$bridge_name_pattern
+$bridge_interface_pattern
+);
+
 # dynamically include PVE::QemuServer and PVE::LXC
 # to avoid dependency problems
 my $have_qemu_server;
@@ -21,20 +27,21 @@ eval {
     $have_lxc = 1;
 };
 
-my $mark;
+my $mark = 0;
 my $trace;
 my $debug = 0;
 
 my $NUMBER_RE = qr/0x[0-9a-fA-F]+|\d+/;
 
+our $bridge_name_pattern = '[a-zA-Z][a-zA-Z0-9]{0,9}';
+our $bridge_interface_pattern = "($bridge_name_pattern)/(\\S+)";
+
 sub debug {
     my $new_value = shift;
-
     $debug = $new_value if defined($new_value);
-
     return $debug;
 }
-    
+
 sub reset_trace {
     $trace = '';
 }
@@ -111,7 +118,7 @@ sub ipset_match {
 sub rule_match {
     my ($ipset_ruleset, $chain, $rule, $pkg) = @_;
 
-    $rule =~ s/^-A $chain // || die "got strange rule: $rule";
+    $rule =~ s/^-A $chain +// || die "got strange rule: $rule";
 
     while (length($rule)) {
 
@@ -120,26 +127,33 @@ sub rule_match {
 
            return undef if $cstate eq 'INVALID'; # no match
            return undef if $cstate eq 'RELATED,ESTABLISHED'; # no match
-           
+
            next if $cstate =~ m/NEW/;
-           
+
            die "cstate test '$cstate' not implemented\n";
        }
 
        if ($rule =~ s/^-m addrtype --src-type (\S+)\s*//) {
            my $atype = $1;
-           die "missing source address type (srctype)\n" 
+           die "missing source address type (srctype)\n"
                if !$pkg->{srctype};
            return undef if $atype ne $pkg->{srctype};
        }
 
        if ($rule =~ s/^-m addrtype --dst-type (\S+)\s*//) {
            my $atype = $1;
-           die "missing destination address type (dsttype)\n" 
+           die "missing destination address type (dsttype)\n"
                if !$pkg->{dsttype};
            return undef if $atype ne $pkg->{dsttype};
        }
 
+       if ($rule =~ s/^-m icmp(v6)? --icmp-type (\S+)\s*//) {
+           my $icmpv6 = !!$1;
+           my $icmptype = $2;
+           die "missing destination address type (dsttype)\n" if !defined($pkg->{dport});
+           return undef if $icmptype ne $pkg->{dport};
+       }
+
        if ($rule =~ s/^-i (\S+)\s*//) {
            my $devre = $1;
            die "missing interface (iface_in)\n" if !$pkg->{iface_in};
@@ -178,7 +192,7 @@ sub rule_match {
            return undef if !$ip->overlaps(Net::IP->new($pkg->{source})); # no match
            next;
        }
-    
+
        if ($rule =~ s/^-d (\S+)\s*//) {
            die "missing destination" if !$pkg->{dest};
            my $ip = Net::IP->new($1);
@@ -227,7 +241,7 @@ sub rule_match {
 
        if ($rule =~ s@^-m mark --mark ($NUMBER_RE)(?:/($NUMBER_RE))?\s*@@) {
            my ($value, $mask) = PVE::Firewall::get_mark_values($1, $2);
-           return undef if !defined($mark) || ($mark & $mask) != $value;
+           return undef if ($mark & $mask) != $value;
            next;
        }
 
@@ -248,7 +262,7 @@ sub rule_match {
        }
 
        if ($rule =~ s/^-j NFLOG --nflog-prefix \"[^\"]+\"$//) {
-           return undef; 
+           return undef;
        }
 
        last;
@@ -261,7 +275,7 @@ sub ruleset_simulate_chain {
     my ($ruleset, $ipset_ruleset, $chain, $pkg) = @_;
 
     add_trace("ENTER chain $chain\n");
-    
+
     my $counter = 0;
 
     if ($chain eq 'PVEFW-Drop') {
@@ -289,7 +303,7 @@ sub ruleset_simulate_chain {
            next;
        }
        add_trace("MATCH: $rule\n");
-       
+
        if ($action eq 'ACCEPT' || $action eq 'DROP' || $action eq 'REJECT') {
            add_trace("TERMINATE chain $chain: $action\n");
            return ($action, $counter);
@@ -382,7 +396,7 @@ sub route_packet {
            $pkg->{iface_out} = $from_info->{fwbr} || die 'internal error';
            $pkg->{physdev_in} = $from_info->{tapdev} || die 'internal error';
            $pkg->{physdev_out} = $from_info->{fwln} || die 'internal error';
-       
+
        } elsif ($route_state eq 'fwbr-in') {
 
            $chain = 'PVEFW-FORWARD';
@@ -392,8 +406,8 @@ sub route_packet {
            $pkg->{physdev_in} = $target->{fwln} || die 'internal error';
            $pkg->{physdev_out} = $target->{tapdev} || die 'internal error';
 
-       } elsif ($route_state =~ m/^vmbr\d+$/) {
-           
+       } elsif ($route_state =~ m/^$bridge_name_pattern$/) {
+
            die "missing physdev_in - internal error?" if !$physdev_in;
            $pkg->{physdev_in} = $physdev_in;
 
@@ -443,7 +457,7 @@ sub route_packet {
            my ($res, $ctr) = ruleset_simulate_chain($ruleset, $ipset_ruleset, $chain, $pkg);
            $rule_check_counter += $ctr;
            return ($res, $ipt_invocation_counter, $rule_check_counter) if $res ne 'ACCEPT';
-       } 
+       }
 
        $route_state = $next_route_state;
 
@@ -494,9 +508,9 @@ sub simulate_firewall {
     my $from = $test->{from} || die "missing 'from' field";
     my $to = $test->{to} || die "missing 'to' field";
     my $action = $test->{action} || die "missing 'action'";
-    
+
     my $testid = $test->{id};
-    
+
     die "from/to needs to be different" if $from eq $to;
 
     my $pkg = {
@@ -526,11 +540,6 @@ sub simulate_firewall {
        $from_info->{type} = 'host';
        $start_state = 'host';
        $pkg->{source} = $host_ip if !defined($pkg->{source});
-    } elsif ($from =~ m|^(vmbr\d+)/(\S+)$|) {
-       $from_info->{type} = 'bport';
-       $from_info->{bridge} = $1;
-       $from_info->{iface} = $2;
-       $start_state = 'from-bport';
     } elsif ($from eq 'outside') {
        $from_info->{type} = 'bport';
        $from_info->{bridge} = 'vmbr0';
@@ -545,15 +554,20 @@ sub simulate_firewall {
        return 'SKIPPED' if !$have_lxc;
        my $vmid = $1;
        $from_info = extract_ct_info($vmdata, $vmid, 0);
-       $start_state = 'fwbr-out'; 
+       $start_state = 'fwbr-out';
        $pkg->{mac_source} = $from_info->{macaddr};
     } elsif ($from =~ m/^vm(\d+)(i(\d))?$/) {
        return 'SKIPPED' if !$have_qemu_server;
        my $vmid = $1;
        my $netnum = $3 || 0;
        $from_info = extract_vm_info($vmdata, $vmid, $netnum);
-       $start_state = 'fwbr-out'; 
+       $start_state = 'fwbr-out';
        $pkg->{mac_source} = $from_info->{macaddr};
+    } elsif ($from =~ m|^$bridge_interface_pattern$|) {
+       $from_info->{type} = 'bport';
+       $from_info->{bridge} = $1;
+       $from_info->{iface} = $2;
+       $start_state = 'from-bport';
     } else {
        die "unable to parse \"from => '$from'\"\n";
     }
@@ -564,10 +578,6 @@ sub simulate_firewall {
        $target->{type} = 'host';
        $target->{iface} = 'host';
        $pkg->{dest} = $host_ip if !defined($pkg->{dest});
-    } elsif ($to =~ m|^(vmbr\d+)/(\S+)$|) {
-       $target->{type} = 'bport';
-       $target->{bridge} = $1;
-       $target->{iface} = $2;
     } elsif ($to eq 'outside') {
        $target->{type} = 'bport';
        $target->{bridge} = 'vmbr0';
@@ -586,6 +596,10 @@ sub simulate_firewall {
        my $vmid = $1;
        $target = extract_vm_info($vmdata, $vmid, 0);
        $target->{iface} = $target->{tapdev};
+    } elsif ($to =~ m|^$bridge_interface_pattern$|) {
+       $target->{type} = 'bport';
+       $target->{bridge} = $1;
+       $target->{iface} = $2;
     } else {
        die "unable to parse \"to => '$to'\"\n";
     }
@@ -593,16 +607,16 @@ sub simulate_firewall {
     $pkg->{source} = '100.100.1.2' if !defined($pkg->{source});
     $pkg->{dest} = '100.200.3.4' if !defined($pkg->{dest});
 
-    my ($res, $ic, $rc) = route_packet($ruleset, $ipset_ruleset, $pkg, 
+    my ($res, $ic, $rc) = route_packet($ruleset, $ipset_ruleset, $pkg,
                                       $from_info, $target, $start_state);
 
     add_trace("IPT statistics: invocation = $ic, checks = $rc\n");
+
     return $res if $action eq 'QUERY';
 
     die "test failed ($res != $action)\n" if $action ne $res;
 
-    return undef; 
+    return undef;
 }
 
 1;