On Thu, Jul 27, 2017 at 12:52 PM, Donald Clark Jackson <don.jack...@gmail.com> wrote: > table <public> const { !10/8 !172.16/12 !192.168/16 0/0 } > guest_hq_if = "em3" > guest_hq_net = $guest_hq_if:network > pass log (matches) from $guest_hq_net to <public> keep state > > match out log (matches) on $external_if inet from $guest_hq_net nat-to > ($external_if)
I'm not sure this ruleset is doing what you think it's doing. Try running the following command to verify that your <public> table contains what you think it should: # pfctl -t public -T show Putting aside for the moment whether 0/0 works or not, I think you have another problem with the ! marks. To explain the problem, let's pretend the entire Internet consists of just 3 class A networks: 1/8, 2/8 and 3/8. So what does { !1/8 !2/8 } mean? It's a table containing all the addresses not in 1/8 (i.e. 2/8 3/8), plus all the addresses not in 2/8 (i.e. 1/8 3/8): { 2/8 3/8 1/8 3/8 } simplifies to { 1/8 2/8 3/8 }, which matches everything and is probably not what you intended. I don't think you can even use ! in a table, even though "pfctl -nf" doesn't appear to complain about it at all. When I try it in 5.9/amd64 the tables end up being not defined. If you want to provide an Internet-only guest network, I recommend you try something like this: table <rfc1918> { 10/8, 172.16/12, 192.168/16 } guest_if = "em3" ext_if = "em0" # or whatever your egress interface is pass in log on $guest_if from $guest_if:network to any block in log quick on $guest_if to $guest_if # note 1 block out log quick on $ext_if from $guest_if:network to <rfc1918> # note 2 pass out log on $ext_if from $guest_if:network nat-to ($ext_if) This lets guest traffic into the router, then relies on your routing table to decide which outbound interface the traffic should then be forwarded to. Traffic to the Internet (going out through $ext_if) is passed with an explicit "pass out" rule (and NAT is applied at the same time). Traffic attempting to reach your other internal networks is not forwarded because there is no "pass out" rule on any other interface allowing it. note 1: Connections *to* your router rather than *through* your router are dropped, to prevent guests from trying to ssh into your router. If you want to allow guests to connect to your router for certain services (e.g. dns) then you will want to be more selective. note 2: Internet traffic to "unrouteable" Internet addresses is dropped. Note that this rule explicitly applies only to traffic "from $guest_if:network" because other outbound traffic might legitimately need to send to such an address. For example, some ISP's will provide a 10/8 address as your default route, and blocking 10/8 unconditionally will prevent you from doing things like pinging that router. As you have discovered, once you have more than two networks (internal and external) things can start to get complicated, which means the opportunity to make mistakes goes up dramatically. What I do to make things clearer (and to reduce the impact of mistakes) is to use tags to categorize traffic: ### BEGIN ### private = "em0" guest = "em1" inet = "em2" table <rfc1918> const { 10/8, 172.16/12, 192.168/16 } # Inbound traffic is blocked by default and tagged TBD until it can be categorized block in log all tag TBD # Inbound / ACCEPT - traffic that should be accepted by this router (not forwarded) pass in log quick on $private inet proto tcp from $private:network to $private port ssh tagged TBD tag ACCEPT pass in log quick on $private inet proto icmp from $private:network to (self) icmp-type echoreq tagged TBD tag ACCEPT pass in log quick on $guest inet proto icmp from $guest:network to (self) icmp-type echoreq tagged TBD tag ACCEPT pass in log quick on $private inet proto {udp tcp} from $private:network to em0 port {domain ntp} tagged TBD tag ACCEPT pass in log quick on $guest inet proto {udp tcp} from $guest:network to $guest port {domain ntp} tagged TBD tag ACCEPT pass in log quick on $private inet proto tcp from $private:network to port ftp divert-to 127.0.0.1 port 8021 tagged TBD tag ACCEPT pass in log quick on $guest inet proto tcp from $guest:network to port ftp divert-to 127.0.0.1 port 8021 tagged TBD tag ACCEPT anchor "ftp-proxy/*" # Inbound / FORWARD - traffic that should be forwarded by this router pass in log quick on $private from $private:network tagged TBD tag FORWARD block in log quick on $guest from $guest:network to $private:network tagged TBD tag BLOCK pass in log quick on $guest from $guest:network tagged TBD tag FORWARD # Outbound / FORWARD block out log all # by default block all outbound traffic not explicitly allowed pass out log quick on $private tagged FORWARD pass out log quick on $guest tagged FORWARD block out log quick on $inet to <rfc1918> tag BLOCK pass out log quick on $inet nat-to ($inet) tagged FORWARD # Outbound - traffic that may originate from this router (even this is blocked by default) pass out log quick on $inet inet proto udp from ($inet) port bootpc to any port bootps # ISP DHCP service pass out log quick on $inet proto icmp from ($inet) icmp-type echoreq # ping pass out log quick on $inet proto {udp tcp} from ($inet) to any port domain # DNS pass out log quick on $inet proto {udp tcp} from ($inet) to any port ntp # NTP pass out log quick on $inet proto tcp from ($inet) to any port 443 # HTTPS for ntpd constraints pass out log quick on $inet proto tcp from ($inet) to any port ftp # ftp-proxy ### END ### Note that the "FORWARD" tag is the only one that actually gets used for something (it gets set by an "in" rule, then is used in a later "out" rule to allow specifically that traffic to pass). The other tags don't do anything once set except act as documentation of intent. They are also a bit of a safety net -- I could accidentally omit any "quick" and the ruleset wouldn't behave (very) differently. As a side benefit, the tags show up if you use "pfctl -s rules" which helps to make the rules easier to understand even with all the comments removed. -ken