PF rate limiting DNS

Hello,

Do you know hashlimit module of iptables ? It's a very cool tool. But i don't find an equivalent on *BSD.
Block an IP who request x times on TCP/UDP.

Then, with PF, I try to make similar thing. I've an Open DNS resolver (for lab test only), and I flood it with hping3 on TCP (53) and UDP (53) and tcpdump on it to see packets. I've create lot of /etc/pf.conf to make that but NOTHING works. All packets sends continue to pass out (tcpdump() view).

Code:
    ext_if = "vtnet0"
    set loginterface $ext_if
     
    # TCP timout settings
     
    #set timeout tcp.first 60
    #set timeout tcp.established 86400
    set optimization aggressive
    set timeout { adaptive.start 20000, adaptive.end 220000 }
    #set limit states 200000
    set limit states 200000
    set skip on lo
    set debug urgent
    set block-policy drop
    set loginterface $ext_if
    set state-policy if-bound
    set fingerprints "/etc/pf.os"
    set ruleset-optimization none

    ### Timeout Options
    set optimization normal
    set timeout { tcp.closing 60, tcp.established 7200}

    #------ Normalization --------#
    scrub in all
    scrub out all
    #------ Anti-spoofing --------#
     
    #antispoof for $ext_if inet
    antispoof for lo0
     
    #------ Loopback -------------#
     
    pass in  quick on lo0 all
    pass out quick on lo0 all

    dns_ip="x.x.x.x"
    table <abusive_ips> persist
    block in quick from <abusive_ips>
    pass in on $ext_if proto tcp to $dns_ip port 53 flags S/SA keep state (max-src-conn 100, max-src-conn-rate 15/5, overload <abusive_ips> flush)
    #pass in on $ext_if proto udp to $dns_ip port 53 flags S/SA keep state (max-src-conn 100, max-src-conn-rate 15/5, overload <abusive_ips> flush)
    pass in on $ext_if proto tcp to $dns_ip port 53 synproxy state
    pass in on $ext_if proto udp to $dns_ip port 53 synproxy state

Or a little rule :
Code:
ext_if = "vtnet0"
table <flood> persist
block quick from <flood>

pass inet proto tcp from any to $ext_if port 53 \
flags S/SA keep state \
(max-src-conn 100, max-src-conn-rate 15/5, \
overload <flood> flush global)

I don't understand why the rules don't work.
Anyone have a pf.conf who works on UDP 53 ?

Thanks all and sorry for my English.
 
Are you saying that even with the rules you are still seeing everything when you run tcpdump() on the PF box? Keep in mind tcpdump() is using the raw bpf() device which means it sees everything. I would suggest running pfctl -t flood -T show to check the flooding address is actually added to the block table. Also, if you change your rule to this:

Code:
block log quick from <flood>

And you run the pflog() service, you can run tcpdump -i pflog0 and see things as they got blocked. There are also a handful of more detailed tcpdump invocations in the pflog() man page.
 
Additionally, here is what I am using that rule for but with port 22 traffic.

/etc/pf.conf
Code:
wan_phy = "em3"
table <bruteforce> persist
table <firewall> const { self }
block drop in log quick on $wan_phy from <bruteforce> to any
pass in on $wan_phy inet proto tcp to <firewall> port 22 flags S/SA keep state \
        (max-src-conn 10, max-src-conn-rate 10/3, \
         overload <bruteforce> flush global)

pfctl -t bruteforce -T show
Code:
   1.93.24.85
   1.93.34.222
   1.93.34.234
   151.11.201.3
   200.116.52.211

pfctl -vsa
Code:
block drop in log quick on em3 from <bruteforce> to any
  [ Evaluations: 456905    Packets: 194       Bytes: 15468       States: 0     ]
  [ Inserted: uid 0 pid 13034 State Creations: 0     ]

...

pass in on em3 inet proto tcp from any to <firewall> port = ssh flags S/SA keep state (source-track rule, max-src-conn 10, max-src-conn-rate 10/3, overload <bruteforce> flush global, src.track 3)
  [ Evaluations: 23550     Packets: 35434     Bytes: 6749047     States: 0     ]
  [ Inserted: uid 0 pid 13034 State Creations: 2345  ]
 
I really don't understand. I'm okay for my bullshit about tcpdump() but i modified my rules like yours.

/etc/pf.conf
Code:
ext_if = "vtnet0"
table <flood> persist
block drop in log quick on $ext_if from <flood> to any
pass in on $ext_if inet proto tcp to <flood> port 53 flags S/SA keep state \
      (max-src-conn 10, max-src-conn-rate 10/3, \
     overload <flood> flush global)

And i ran tcpdump() like that from an another computer and from an another network :
sudo hping3 -p 53 -S 77.x.64.x

And on the computer with rules :
tcpdump -i pflog0
Code:
After more 10 try :
0 packets captured
0 packets received by filter
0 packets dropped by kernel

pfctl -t flood -T show
NOTHING

pfctl -vsa
Code:
No ALTQ support in kernel
ALTQ related functions disabled
FILTER RULES:
block drop in log quick on vtnet0 from <flood> to any
  [ Evaluations: 3320      Packets: 0         Bytes: 0           States: 0     ]
  [ Inserted: uid 0 pid 58810 State Creations: 0     ]
pass in on vtnet0 inet proto tcp from any to <flood> port = domain flags S/SA keep state (source-track rule, max-src-conn 10, max-src-conn-rate 10/3, overload <flood> flush global, src.track 3)
  [ Evaluations: 1715      Packets: 0         Bytes: 0           States: 0     ]
  [ Inserted: uid 0 pid 58810 State Creations: 0     ]

INFO:
Status: Enabled for 0 days 00:11:50           Debug: Urgent

Hostid:   0x861d2cb4
Checksum: 0x3c32f45ea46747a099bace34b5d96356

State Table                          Total             Rate
  current entries                        0               
  searches                            3322            4.7/s
  inserts                                0            0.0/s
  removals                               0            0.0/s
Source Tracking Table
  current entries                        0               
  searches                               0            0.0/s
  inserts                                0            0.0/s
  removals                               0            0.0/s
Counters
  match                               3322            4.7/s
  bad-offset                             0            0.0/s
  fragment                               0            0.0/s
  short                                  0            0.0/s
  normalize                              0            0.0/s
  memory                                 0            0.0/s
  bad-timestamp                          0            0.0/s
  congestion                             0            0.0/s
  ip-option                              5            0.0/s
  proto-cksum                            0            0.0/s
  state-mismatch                         0            0.0/s
  state-insert                           0            0.0/s
  state-limit                            0            0.0/s
  src-limit                              0            0.0/s
  synproxy                               0            0.0/s
Limit Counters
  max states per rule                    0            0.0/s
  max-src-states                         0            0.0/s
  max-src-nodes                          0            0.0/s
  max-src-conn                           0            0.0/s
  max-src-conn-rate                      0            0.0/s
  overload table insertion               0            0.0/s
  overload flush states                  0            0.0/s

TIMEOUTS:
tcp.first                   120s
tcp.opening                  30s
tcp.established           86400s
tcp.closing                 900s
tcp.finwait                  45s
tcp.closed                   90s
tcp.tsdiff                   30s
udp.first                    60s
udp.single                   30s
udp.multiple                 60s
icmp.first                   20s
icmp.error                   10s
other.first                  60s
other.single                 30s
other.multiple               60s
frag                         30s
interval                     10s
adaptive.start             6000 states
adaptive.end              12000 states
src.track                     0s

LIMITS:
states        hard limit    10000
src-nodes     hard limit    10000
frags         hard limit     5000
table-entries hard limit   200000

TABLES:
-pa-r--	flood

OS FINGERPRINTS:
710 fingerprints loaded

I really don't understand...
 
Watch it there, my example used two separate tables with one being the source as <bruteforce> and one for a destination of the firewall itself as <firewall>. You are using the table of bad guys as both the source of your block rule and your destination. You will never have any hits on the pass rule. Either do something like what I did or change it to "any".

Secondly, hping3 still waits for a reply from each packet and is probably too slow to trigger the rule. Take a look at it's man page regarding the --flood option. Once your rules are sensible that should surely be enough traffic to trigger it.
 
I've enabled my rules exactly like yours, but changed ssh to 53. Launched tcpdump with the --flood option and when I show my table, it is again empty. tcpdump -i pflog0 shows me nothing again.

pfctl -vsa
Code:
No ALTQ support in kernel
ALTQ related functions disabled
FILTER RULES:
block drop in log quick on vtnet0 from <flood> to any
  [ Evaluations: 100188    Packets: 0         Bytes: 0           States: 0     ]
  [ Inserted: uid 0 pid 48355 State Creations: 0     ]
pass in on vtnet0 inet proto tcp from any to <fw> port = domain flags S/SA keep state (source-track rule, max-src-conn 10, max-src-conn-rate 10/3, overload <flood> flush global, src.track 3)
  [ Evaluations: 54308     Packets: 50998     Bytes: 2461672     States: 0     ]
  [ Inserted: uid 0 pid 48355 State Creations: 10347 ]

What is the problem?
 
I never tested my rules with hping3. I have in the past just not this one. I was already using fail2ban but this was just an extra measure so I didn't worry about testing. A few days later I saw the hits and left it at that.

So I looked into what appears to happen with hping3. The command overloads the number PF states but since it never finishes the handshake it doesn't seem to trigger the rule. I installed nmap on another box and ran the nping --tcp-connect --rate=200 -c 200 -p 22 -q 10.100.82.1. This does finish the TCP handshake and trigger the rule.
 
Back
Top