Keywords: openbsd 3.8, pf, accounting, per-protocol, per-ip, bridging
I am in a bit of a mess trying to get this setup to work. The topology
is as follows:
Host Martin -\ >
217.218.45.80 \ "local zone" < "internet zone"
\ >
\ <
Host Sven -------[switch]---------[openbsd bridge]---- internet
217.218.44.209 / / \
/ sis1 sis0
/ 217.218.44.85
Host Janne --/
217.218.44.208
All IPs are public and working. I use bridging since I have only very
few IPs available (and they are not in a continuous range, as you can
see). The setup works very nicely and all, but when I want to do
accounting, things aren't as easy as I expected.
What I want to do is:
- Accurately account for packets and bytes per client in and out
between the local and internet zone, i.e. don't do accounting when
SSH-ing from host Sven to the OpenBSD bridge for maintenance, etc.
- Preferably account for each protocol (TCP, UDP, ICMP) per client.
This is not a must, however.
- Keep the firewalling capabilities intact.
Now, if I understand correctly, with the mechanism of pf evaluating
only the last matching rule, this presents some problems when it comes
to accounting.
This is the current ruleset:
# ---- pf.conf begins ----
ext_if="sis0"
int_if="sis1"
host_martin="217.218.45.80"
host_sven="217.218.44.209"
host_janne="217.218.44.208"
block in on $ext_if
pass out on $ext_if
pass in on $int_if
pass out on $int_if
pass in on $ext_if proto tcp to $host_janne port { 22 25 80 443 } keep state
pass in on $ext_if proto { tcp udp } to $host_sven port { 5500 } keep state
pass in on $ext_if proto { tcp udp } to $host_martin port 80 keep state
# ---- pf.conf ends ----
To do accounting, I was thinking that I simply could add some labels
to the rules and use pfctl -sl to fetch the counters. But because of
the statefullness and the fact that only the last matching rule is
evaluated, I fail to understand how to do it properly.
For example, for client Sven, something along the lines of:
pass out on $ext_if from $host_sven label "OUT-SVEN" keep state
pass out on $ext_if proto tcp from $host_sven to any label "OUT-TCP-SVEN" keep
state
pass out on $ext_if proto udp from $host_sven to any label "OUT-UDP-SVEN" keep
state
pass out on $ext_if proto icmp from $host_sven to any label "OUT-ICMP-SVEN"
keep state
would (after pinging a host with 8 pings, which also includes a DNS
lookup) give the following output from pfctl -sl:
OUT-SVEN 22 0 0 0 0 0 0
OUT-TCP-SVEN 3 0 0 0 0 0 0
OUT-UDP-SVEN 3 2 166 1 109 1 57
OUT-ICMP-SVEN 3 16 960 8 480 8 480
This highlights a feature of 'last match wins', the OUT-SVEN label
counters have not increased (only the evaluation counter, of course).
What I could do is to label each rule and then calculate totals by
summing each in/out counter, but with several firewall rules and
per-port filtering, this would result in a terrible cludge.
Do you have any idea of how this could be done differently?
One idea I had was to introduce another action in the filtering
section of pf.conf, a "dupe" or "eval" action that would evaluate the
rule (exactly like 'pass quick') but also send the packet traversing
the rest of the ruleset (like normal 'pass'). Together with labels,
this would make it very easy to do accounting, e.g.
eval out on $ext_if from $host_sven label "OUT-SVEN"
eval out on $ext_if proto tcp from $host_sven to any "OUT-TCP-SVEN"
eval out on $ext_if proto udp from $host_sven to any "OUT-UDP-SVEN"
eval out on $ext_if proto icmp from $host_sven to any "OUT-ICMP-SVEN"
As far as I understand, this is how the Linux netfilter + ipac-ng does
evaluation, and I'm currently considering converting to that (though I
don't want to).
regards,
sven