I'd like to set a condition so that a firewall rule will not apply to a list of ports, something along the lines of this:

 table <my_table> persist file "/etc/pf.anchors/entries"
 set skip on lo
 block out quick proto { tcp, udp } from any to ! <my_table> port != { 66 80 } 

This gives a syntax error when running 'sudo pfctl -vnf /etc/pf.conf'.

If I put the following, it works (only specify one port).

 block out quick proto { tcp, udp } from any to ! <my_table> port != 80

How can I specify a list of ports with the '!=' operator?

  • have you tried to use comma? != { 66, 80 } (it is a suggestion based on deduction, I never did it) – Prado Aug 29 '19 at 18:39
  • Already tried that, doesn't work. – GreekFire Aug 29 '19 at 18:56
  • Take a look here: freebsd.org/cgi/… – Prado Aug 29 '19 at 19:18

One should clearly understand what Pf's "lists" are. They aren't a part of ruleset that gets loaded into kernel in fact, but macros instead. It means they're expanded during preprocessing phase of rules loading — contrary to tables. Keeping that in mind saves one from "shooting in own foot".

Let's now see what you're trying to do:

block out quick proto { tcp, udp } from any to ! <my_table> port != { 66 80 } 
  • Block immediately If
    • it's TCP or it's UDP
    • AND it's destined to
      • IPs that aren't in my_table
      • AND ports, that aren't in the list

As I've told you lists' items would get expanded into a separate rule each. And this would break the logic I've just explained: only the first port of the list would be treated right, if you block immediately (quick) it obviously means no second checking — "not allowed port, ok, blocking it".

Mastering firewall ruleset you'd better keep things as simple as possible. Well, actually it's not only for firewalls — it's general and programming common sense. Double negations, exception of exceptions aren't that simple to follow.

So what are your options then? — You can take a look at the different angle: what and when do you want to pass?

# If it's destined to IP in <my_table> -- pass:
pass  out quick proto { tcp, udp } from any to <my_table>

# ElseIf it's to allowed ports -- pass:
pass  out quick proto { tcp, udp } to port { 66 80 }

# And this point would be reached only if it wasn't to <my_table>
# and to some ports other than allowed ones:
block out quick proto { tcp, udp }

This ruleset is way more readable indeed. Moreover — macro expansion doesn't spoil its logic.

Of course, Pf has some other means for solving this very task but describing them all properly would make this answer way too lengthy.


You can make the inverse command logic.

First Block all, then open just the ones you want:

The result is the same you tried by negating all except those chosen to be opened. :)

set skip on lo
port_pass = "{ 80 66 53 22 }"
block all
pass out on en0 proto { tcp, udp } to any port $port_pass keep state
  • 1. you can omit keep state, it was assumed by default even in that ancient Pf's version that MacOS comes with. 2. It's not same, because your ruleset doesn't lookup <my_table>'s content – poige Sep 11 '19 at 2:59

Figured it out:

Is by negating each individual port, inside the delimiter { }

 block out quick proto { tcp, udp } from any to ! <my_table> port { != 66, != 80 } 

I found the 'op-list' section for specifying port here: https://man.openbsd.org/pf.conf.5

This shows you how to apply the logic of adding more ports

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .