On Wed, Jul 02, 2003 at 04:59:10PM +0100, Steve Kersley wrote:

> rdr on $INTERNAL proto tcp from <blocked> to any port 80 -> $web port 80

It depends on whether $web is on the internal or external network. If it
is on the internal network, you're trying to 'bounce' or 'reflect' the
connection out through the interface it arrived on, which has its own
catches (see http://www.openbsd.org/faq/pf/rdr.html#reflect).

If $web is on the external network, the student's connections must pass
the internal and external interfaces. You're filtering on both, so you
need a state entry on both interfaces.

First, the connection arrives on the internal interface. It gets
translated first (destination is replaced with $web), then filtered on
the internal interface.

> pass in quick on $INTERNAL proto tcp from <blocked> to port 80 flags S/SA keep state

This is the first matching rule, and it has 'quick', so the connection
passes the internal interface and creates state (covering the replies
going back out through the internal interface).

Now the connection (already translated) passes out through the external
interface.

> pass out on $EXTERNAL proto tcp flags S/SA keep state

Matches, but no quick, so evaluation continues. But no further rules
match, so this becomes the last matching rule. The connection passes and
creates state.

So, it should work. But rdr (like nat) has limits on a bridge. For
instance, the bridge will decide what interface to send a frame out
through based on the destination MAC address. In your case, that should
be the MAC address of the default gateway (assuming the student tries to
access an external web server). Then pf changes the destination IP
address to $web. Then bridge sends the frame out through the interface
it decided already, to the unchanged destination MAC address (and not
$web's MAC address). The gateway might not forward the IP packet to $web
(because it's on the same local network, for instance), and generate
ICMP redirects instead.

If this is what happens, there are several possible workarounds, like
redirecting to 127.0.0.1 and either serving the page from there, or
proxying from there to $web. You can also try to use 'fastroute' on the
'pass out on $EXTERNAL ...' rule that covers those redirected
connections. This makes pf dispatch the the packet internally (creating
a new ethernet header, with the right destination MAC address), instead
of handing it back to the bridge code. 'route-to' should have a similar
effect.

To make sure, can you

  - tcpdump -nvvve on both interfaces and try to establish a connection
    from a blocked student host to an external web server? What do you
    see on the internal interface, what on the external? If you can
    limit the output to the packet related to one connection (and
    icmp errors), that would be great.

  - add 'log' to all block rules, repeat the first step, show us
    the blocked packets logged by pflogd(8)?

  - enable debug logging (pfctl -xm) and check /var/log/messages?

Daniel

Reply via email to