In article <[EMAIL PROTECTED]>,
Andrew Hoying <[EMAIL PROTECTED]> wrote:
>I'm looking at implementing iplimit to lock down the number of simultaneous
>connections users behind our corporate firewall can have. I would like to be
>able to apply a soft limit and a hard limit to it, though. In other words I
>would like to be able to have, for example, a soft limit of 125 connections
>for up to a minute, and then a hard limit of 75 connections for anything
>longer than that. I know some web pages initiate a large number of
>connections that occasionally goes over 75 in a short length of time for an
>individual user, but quickly falls off again after the web page downloads.

Uhhhh...which severely broken web browser opens up 75 connections at a
time to load a web page with images?  Most web browsers I've seen queue
requests across 6-10 parallel connections, and/or fetch multiple objects
per connection.  I've seen some evil "download managers" that split a
request for a single large file into 60+ connections, but those are a
whole different kettle of fish (for me, it means they get DROPped for
the rest of the day, as I don't tolerate such impolite behavior).

Anyway, enough of my questions, on to yours.  ;-)

>Would it be feasible to add this feature to the iplimit patch?

You can already do it with iplimit and recent.

Here's some iptables rules that implement what you asked for (more or
less)--take a look and tell me what you think.  Note the comments in
the Close50 chain which deals with the surplus connections when you
switch from soft to hard limit.  Probably the safest thing to do is to
keep the existing ones and prevent any new ones.  Note that you might
not get exactly 75 or 125 with these rules, but you could add in some
extra recent matches to slow down the rate of SYN packets.

I have no idea if the parts of Close50 that try to destroy existing
over-hard-limit connections actually work.  If you get a chance to test
it, I'd like to hear about the results.  ;-)

Be warned that I have not actually tested the following:

        # iptables ruleset by Zygo "sooner or later Netfilter will grow
        # into a JIT-compiled scripting language, there is no other
        # path it could follow" Blaxell

        iptables -N TcpLimit
        iptables -N Over75
        iptables -N Under75
        iptables -N Close50


        # I put all this into its own chain.  You can call the 
        # chain from either INPUT or FORWARD.

        # Although you can call this from an OUTPUT chain, it is difficult
        # to see how it would be useful (maybe if you have a few
        # class-C-nets' worth of IP aliases?).
        # The 'recent' match checks the source address (and only the
        # source address, a serious limitation), and you are likely to
        # have the same source IP for all of your outbound connections.  ;-)

        # I have left spaces for extra matching rules in '...'.

        # I have no idea what happens if you use these rules
        # on non-TCP "connections" (like UDP or ICMP), so I restrict to 
        # TCP only at the start.

        # Put these in your existing ruleset at some convenient point.
        # Note that these rules never ACCEPT anything, so your existing
        # access controls, IP/port blocking, etc. is unaffected.
        iptables -A INPUT ... --proto tcp -j TcpLimit
        iptables -A FORWARD ... --proto tcp -j TcpLimit





        # This is your TcpLimit table.

        # First of all, you need to see all TCP RST and FIN packets,
        # otherwise your clients won't be able to close their connections
        # in some of the limit conditions.
        iptables -A TcpLimit ... --proto tcp --tcp-flags rst rst -j RETURN
        iptables -A TcpLimit ... --proto tcp --tcp-flags fin fin -j RETURN

        # If there are more than 75 connections, jump to soft-limit chain.
        iptables -A TcpLimit ... -m iplimit --iplimit-above 75 -j Over75

        # If there are fewer than 75 connections, clear any recent table
        # entry that might be known for this IP.
        iptables -A TcpLimit ... -m iplimit ! --iplimit-above 75 -j Under75



        # This is the Over75 chain, which sorts out which connections have
        # soft limit and which have hard.  Every packet processed by this
        # chain is from an IP address that has more than 75 open
        # connections.

        # I use two recent tables here:  one to find out when the last
        # packet was received, and one for the first.  They are called,
        # strangely enough, Last and First.  Everyone in the First table
        # who was not added to that table in the last 60 seconds has the
        # lower limit (75), otherwise they have the higher limit (125).  
        # A client has to be out of the Last table for more than 60
        # seconds to get their entries removed from both tables.

        # Record the last time we received a packet from this IP
        # with more than 75 connections established.
        iptables -A Over75 ... -m recent --name Last --set

        # If there are more then 125 connections, ignore any further SYNs,
        # which will prevent more connections from being established.

        # If you use DROP, the client will simply be throttled until it
        # closes some connections or gives up trying to establish them. 
        # If you use REJECT instead, the client will see connection
        # refused/network unreachable errors for new connections.

        # Connections that are already established will
        # be unaffected in either case.  Your choice.

        # Note that if iplimit does not count half-opened connections, then
        # the client could open more than 125 connections if they are opened
        # fast enough.  If iplimit does count half-opened connections, your
        # firewall is vulnerable to simple denial-of-service attacks (although
        # it's questionable whether this is actually worse than not having
        # iplimit at all).  

        # My own preference is to remove the '--syn', which will shut down
        # _all_ of the client's connections until enough of them go away
        # to get under the limit again.  Serves them right for taking
        # up so many of my sockets anyway.  You might not be so draconian.
        # ;-P
        iptables -A Over75 ... --proto tcp --syn -m iplimit \
                --iplimit-above 125 -j DROP

        # If we first saw this IP within the last minute, then we implement
        # the soft-limit 125.  In other words, we do nothing.  ;-)
        iptables -A Over75 ... -m recent --name First --seconds 60 \
                --rcheck -j RETURN

        # If we have seen this IP, but the first time we saw it was
        # not in the last minute, implement lower connection limit.
        # This means we have to close some of the connections, which
        # is what the Close50 chain is for.

        # If you do jump to another chain, make sure you don't come
        # back here, otherwise your timing will get messed up.
        iptables -A Over75 ... -m recent --name First --rcheck -j Close50
        iptables -A Over75 ... -m recent --name First --rcheck -j RETURN

        # If none of the other rules are matched, we have not seen this
        # IP before, so we can now record the time of the first packet
        # received with more than 75 connections established.
        iptables -A Over75 ... -m recent --name First --set




        # This is your Close50 chain, which will attempt to close connections
        # between the old soft limit (125) and the new one (75), hence the
        # name (125 - 75 = 50).

        # There are two things you can do here.  One of them is to just
        # not allow any new connections to be established.  The same notes
        # about DROP vs. REJECT above also apply here.

        # We want to do this in any case, so here it is.
        iptables -A Close50 ... --proto tcp --syn -j DROP

        # Whether you want to do more depends on your preferences.
        # You could leave out the rest of the Close50 rules here, which
        # would leave existing connections untouched but prevent new ones.

        # We close connections by sending TCP RST packets, which will give
        # the client a "connection reset by peer" message.  This in turn
        # will remove the connection from ip_conntrack, which means that
        # the other end of the connection will also get a TCP RST message.

        # Note that this will start killing connections at random until
        # the client goes below the soft limit; however, iplimit might not
        # see that the connections have gone away until the client sends
        # its own RST back (now that I think about it...*does* the client
        # send an RST back?), so it may kill more connections than
        # are strictly required to get to the soft limit.  This is 
        # where rate limiting comes in:  we only close 10 connections 
        # per second, which limits the damage.

        # It would be cool if we could apply this rule in OUTPUT or
        # POSTROUTING, since the RST packets would hit the firewall
        # and clean out the ip_conntrack table automagically.

        # Also note we use another 'recent' table here, named Close.
        iptables -A Close50 ... --proto tcp -m recent --name Close \
                --update ! --hitcount 10 --seconds 1 --j REJECT \
                --reject-with tcp-reset

        # You don't want to accept extra packets if you're 
        # calling from the FORWARD chain, since the packets might still
        # cost you when they go upstream.

        # Note that all traffic from the client will be stopped until the
        # client gives up 50 connections, so adjust this rule and the
        # previous rule accordingly.  

        # You could also RETURN here, which would let you receive the rest
        # of the packets.  You might do this if you apply these rules to a
        # non-forwarding machine (e.g. a web server) because you're going
        # to receive the packets anyway (the client is going to keep sending
        # them to you until it gets the TCP RST packet), so you might as well
        # get some benefit from them.  Heck, some of the connections might
        # even finish up and close by themselves.  It could happen.  ;-)

        # You might also put in a low '-m limit -j ACCEPT' rule here,
        # so that the existing connections are not completely starved,
        # but don't take up all of your link either.
        iptables -A Close50 ... -j DROP




        # This is your Under75 chain, which resets the timers for your
        # soft limits for IP's that have fewer than 75 open connections.
        # Note that if the client opens 80 connections, then closes them
        # all, then opens 80 again 59 seconds later, then this will
        # be considered "80 connections open for one minute", which may not
        # be what you want.

        # Note that we have to have a little hysteresis here, because
        # Close50 might close too many connections, and if we don't
        # implement a time delay then the soft limit will be raised to
        # 125 immediately after it dropped to 75.  :-P

        # The soft limit will not be raised to 125 connections until
        # the client has had fewer than 75 for a minute.
        iptables -A Under75 ... -m recent --name Last --seconds 60 \
                --rcheck -j RETURN

        # OK, the last >75-connections packet was seen more than a minute ago
        # (or not seen at all), so we clean out the tables.
        iptables -A Under75 ... -m recent --name Last --remove
        iptables -A Under75 ... -m recent --name First --remove


-- 
Zygo Blaxell (Laptop) <[EMAIL PROTECTED]>
GPG = D13D 6651 F446 9787 600B AD1E CCF3 6F93 2823 44AD

Reply via email to