Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 20 Aug 2012 14:16:16 -0400
From:      J David <j.david.lists@gmail.com>
To:        freebsd-pf@freebsd.org
Subject:   Re: Fighting DDOS attacks with pf
Message-ID:  <CABXB=RTeqFS%2Bn1NSdxie9BLM-rEwMp9h4jNyxs1-vaPZHE5G5g@mail.gmail.com>
In-Reply-To: <20120820162752.GA28945@DataIX.net>
References:  <CABXB=RQZx1m05gVNh4x3zc7sovGA8ZpzyaZeq_Gd1QHS0n7r1g@mail.gmail.com> <20120820162752.GA28945@DataIX.net>

next in thread | previous in thread | raw e-mail | index | archive | help
Unfortunately, I think my reference to DDOS attacks has distracted
from the underlying issue.

PF allows a rule like this:

pass in proto tcp from any to any port www keep state (max 100,
source-track rule, max-src-states 3)

(adapted from the man page)

We want this rule:

pass in proto tcp from any to any port www keep state (max 100,
dest-track rule, max-dst-states 3)

Obviously there is no such rule.  I'm simply curious whether anyone
has found a way to bend the PF syntax to create the same behavior.

Perhaps I can offer a couple of examples that will clarify.

If I do this:

table <ProtectedIPs> { 10.0.0.0/30 }
block drop
pass in proto tcp from any to <ProtectedIPs> port 80 keep state ( max 4000 )

This will not work for us.  If traffic to 10.0.0.2 creates all 4000
allowed states, packets to *all* of the protected IPs will stop
matching the pass rule and all of the protected IPs are effectively
knocked offline.

If, however, I do this:

ProtectedIPs = "{ 10.0.0.0, 10.0.0.1, 10.0.0.2, 10.0.0.3 }"
block drop
pass in proto tcp from any to $ProtectedIPs port 80 keep state ( max 1000 )

Then PF will create one rule for each destination IP, and will
therefore limit the number of states on a per-destination-IP basis.
Thus, when traffic to 10.0.0.2 creates 1000 states on its pass rule,
further packets match "block drop" instead, but only for that IP;
traffic for the other three IPs is unaffected.

That is the effect we want to create.

This works.  The problem we have is that the scale is much larger.
Listing four example IP's in the PF config is not difficult,
hardcoding thousands of destination IPs into the config file
individually would be unwieldy and difficult to maintain, as well as
possibly creating performance problems within pf, as rule processing
goes from O(1) to O(n).

Since that doesn't seem practical, we use the script I mentioned
earlier that scans the state table and adds targeted IPs to a pf table
to be blocked.  This is fine, except it introduces a period of up to a
minute where spillover damage may occur.

What I am hoping to find is a way of doing this entirely within pf, in
order to cut the response time and to gracefully flow from overflow to
non-overflow conditions.

There are a couple of ways of doing this.

One would be to find a couple of really clever rules that would create
the effect we are looking for.  This is what I was hoping to find by
asking here.

The other would be to find some manageable way to generate the
individual rules for each IP -- shouldn't be tough to script if the
pf.conf syntax isn't up to it -- if adding a few thousand "pass" rules
won't crush performance.  If anyone has worked with ridiculously large
rulesets of that sort, I would love to hear about their experiences..

Thanks!



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CABXB=RTeqFS%2Bn1NSdxie9BLM-rEwMp9h4jNyxs1-vaPZHE5G5g>