0

I need help, I need a rule to do this:

If more then X packets received in second with same data length so drop them all.

How can i achieve this using IPTables in Ubuntu?

Thanks.

  • What type of packet, UDP, TCP or ? Could it be based on overall length, as contained in the ethernet frame (might be easier) instead of payload length (might be harder, or even not possible)? drop them all (thereafter, I assume) for how long? – Doug Smythies Sep 13 '17 at 15:24
  • Hey @DougSmythies, It's UDP and i need to be based on payload but as you say it maybe not possible so let's make it based on the overall length. Just rule to limit the income udp packets to dest port: 27015 to be if it's same in length 10/s so if for same length it's higher than this in sec to drop all them. – gamer-cs Sep 13 '17 at 15:43
  • For UDP it should be possible. However, do you mean that the length can be a variable? If yes, I don't know how to do that. But if you mean, say for example, a length of 100 bytes, that is possible. For how long after the trigger event do you want to drop them? It is not possible to drop packets from before the trigger event. – Doug Smythies Sep 13 '17 at 18:20
  • Example: UDP packets are coming to my 27015 dest. port. We need to place rule that if there was more than 10 packets/sec with same length so drop all coming packets with same length. The drop maybe forever or if it can be set for time ok. And the length if not accessible to be payload so the total packet length. Anyway if you not get me post me example of what you can do based on what you understand from me. Thanks :) – gamer-cs Sep 13 '17 at 19:29
  • Ok, No problem at all take your time. – gamer-cs Sep 14 '17 at 07:38

2 Answers2

1

Using the iptables recent module, in a two step process, 10 per second can be detected, and then a longer ban time can be set. A script was made for this:

#!/bin/sh
FWVER=0.01
#
# gamer-cs iptables rule example. Smythies 2017.09.13 Ver:0.01
#     Protocl: UDP
#     Destination port: 27015
#     Length: 100 payload. The UDP header is always 8 bytes in length.
#                 The IP header is typically 20 bytes but can be longer.
#
#     Ban by IP or ban all?:
#            Ban by IP via a two step process, can ban for any desired time.
#            Ban all can use the built in rate limit stuff, but then the ban
#            time can not exceed the rate limit window and it has a tendency
#            to block legitamite users.
#
#     Probably needs to be combined with the bigger context of other rules.
#
#     See also:
#     https://askubuntu.com/questions/955425/allow-x-packets-per-second-with-same-data-length-iptables
#            And other questions from gamer-cs.
#
#     https://askubuntu.com/questions/818524/correctly-limit-ip-connections
#     https://chat.stackexchange.com/transcript/51426/2017/1/9
#
#     run as sudo
#

echo "Loading gamer-cs rule example version $FWVER..\n"

# The location of the iptables program
#
IPTABLES=/sbin/iptables

# Some definitions.
# Some of these are for the Smythies test computer. Change as required.
EXTIF="enp9s0"
EXTIP="192.168.111.104"
PORT_TO_CHECK="27015"
UNIVERSE="0.0.0.0/0"

#Clearing any previous configuration
#
echo "  Clearing any existing rules and setting default policies.."
$IPTABLES -P INPUT ACCEPT
$IPTABLES -F INPUT
$IPTABLES -P OUTPUT ACCEPT
$IPTABLES -F OUTPUT
$IPTABLES -P FORWARD ACCEPT
$IPTABLES -F FORWARD
# Otherwise, I can not seem to delete it later on
$IPTABLES -F ADD_TO_LIST
# Delete user defined chains
$IPTABLES -X
# Reset all IPTABLES counters
$IPTABLES -Z

#######################################################################
# USER DEFINED CHAIN SUBROUTINES:
#
# ADD_TO_LIST
# Called from the rate checker.
# Add the IP address to the bad guy list, and DROP the packet.
# If desired, comment out the log rule.
# Rate limit the logging.
$IPTABLES -N ADD_TO_LIST
$IPTABLES -A ADD_TO_LIST -m recent --set --name BADGUY_LIST
$IPTABLES -A ADD_TO_LIST -m limit --limit 3/m --limit-burst 2 -j LOG --log-prefix "BAD_ADD:" --log-level info
$IPTABLES -A ADD_TO_LIST -j DROP


# I (Smythies) need the following rule to prevent my ssh sessions from being locked out
# while testing/debugging
#
$IPTABLES -A INPUT -i $EXTIF -s $UNIVERSE -d $EXTIP -m state --state ESTABLISHED,RELATED -j ACCEPT

# Other INPUT chain rules might be needed before this, not sure.
#
# If on the bad guy list, then drop regardless. Limit logging (If desired, comment out the log rule).
$IPTABLES -A INPUT -i $EXTIF -m limit --limit 3/m --limit-burst 2 -m recent --rcheck --hitcount 1 --seconds 3600 --name BADGUY_LIST -j LOG --log-prefix "BAD GUY:" --log-level info
$IPTABLES -A INPUT -i $EXTIF -m recent --update --hitcount 1 --seconds 3600 --name BADGUY_LIST -j DROP
# Do the actual rate limiting check
$IPTABLES -A INPUT -i $EXTIF --protocol udp --destination-port $PORT_TO_CHECK -m length --length 128 -m recent --update --hitcount 10 --seconds 1 --name BAD_OR_NOT -j ADD_TO_LIST
$IPTABLES -A INPUT -i $EXTIF --protocol udp --destination-port $PORT_TO_CHECK -m length --length 128 -m recent --set --name BAD_OR_NOT -j ACCEPT
# O.K. at this point, carry on with other INPUT chain rules.

echo "gamer-cs rule example version $FWVER done.\n"

Testing was done using hping3 from another computer. First:

sudo hping3 --quiet -c 50 --udp --data 100 --destport 27015 --interval u101000 --spoof 192.168.111.249 192.168.111.104

Sent packets at just under the 10 per second rate limit, The source IP was spoofed to prevent my real IP from getting locked out. Result (hint: exactly as expected):

$ sudo iptables -v -x -n -L
Chain INPUT (policy ACCEPT 10 packets, 984 bytes)
    pkts      bytes target     prot opt in     out     source               destination
      37     2152 ACCEPT     all  --  enp9s0 *       0.0.0.0/0            192.168.111.104      state RELATED,ESTABLISHED
       0        0 LOG        all  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            limit: avg 1/min burst 2 recent: CHECK seconds: 3600 hit_count: 1 name: BADGUY_LIST side: source mask: 255.255.255.255 LOG flags 0 level 6 prefix "BAD GUY:"
       0        0 DROP       all  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            recent: UPDATE seconds: 3600 hit_count: 1 name: BADGUY_LIST side: source mask: 255.255.255.255
       0        0 ADD_TO_LIST  udp  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:27015 length 128 recent: UPDATE seconds: 1 hit_count: 10 name: BAD_OR_NOT side: source mask: 255.255.255.255
      50     6400 ACCEPT     udp  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:27015 length 128 recent: SET name: BAD_OR_NOT side: source mask: 255.255.255.255

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
    pkts      bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 57 packets, 8528 bytes)
    pkts      bytes target     prot opt in     out     source               destination

Chain ADD_TO_LIST (1 references)
    pkts      bytes target     prot opt in     out     source               destination
       0        0            all  --  *      *       0.0.0.0/0            0.0.0.0/0            recent: SET name: BADGUY_LIST side: source mask: 255.255.255.255
       0        0 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0            limit: avg 1/min burst 2 LOG flags 0 level 6 prefix "BAD_GUY:"
       0        0 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0

Second:

sudo hping3 --quiet -c 50 --udp --data 100 --destport 27015 --interval u98000 --spoof 192.168.111.249 192.168.111.104

Sent packets at just over the 10 per second limit. Result (hint: exactly as expected):

$ sudo iptables -v -x -n -L
Chain INPUT (policy ACCEPT 16 packets, 1798 bytes)
    pkts      bytes target     prot opt in     out     source               destination
      55     7648 ACCEPT     all  --  enp9s0 *       0.0.0.0/0            192.168.111.104      state RELATED,ESTABLISHED
       0        0 LOG        all  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            limit: avg 1/min burst 2 recent: CHECK seconds: 3600 hit_count: 1 name: BADGUY_LIST side: source mask: 255.255.255.255 LOG flags 0 level 6 prefix "BAD GUY:"
      39     4992 DROP       all  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            recent: UPDATE seconds: 3600 hit_count: 1 name: BADGUY_LIST side: source mask: 255.255.255.255
       1      128 ADD_TO_LIST  udp  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:27015 length 128 recent: UPDATE seconds: 1 hit_count: 10 name: BAD_OR_NOT side: source mask: 255.255.255.255
      60     7680 ACCEPT     udp  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:27015 length 128 recent: SET name: BAD_OR_NOT side: source mask: 255.255.255.255

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
    pkts      bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 86 packets, 13585 bytes)
    pkts      bytes target     prot opt in     out     source               destination

Chain ADD_TO_LIST (1 references)
    pkts      bytes target     prot opt in     out     source               destination
       1      128            all  --  *      *       0.0.0.0/0            0.0.0.0/0            recent: SET name: BADGUY_LIST side: source mask: 255.255.255.255
       1      128 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0            limit: avg 1/min burst 2 LOG flags 0 level 6 prefix "BAD_GUY:"
       1      128 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0

Third:

$ sudo hping3 --quiet -c 50 --udp --data 100 --destport 27015 --interval 5 --spoof 192.168.111.249 192.168.111.104

Check the limited logging using a slow packet rate, having earlier triggered the blocking rule. Result:

$ sudo iptables -v -x -n -L
Chain INPUT (policy ACCEPT 36 packets, 2671 bytes)
    pkts      bytes target     prot opt in     out     source               destination
     129     7848 ACCEPT     all  --  enp9s0 *       0.0.0.0/0            192.168.111.104      state RELATED,ESTABLISHED
      10     1280 LOG        all  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            limit: avg 3/min burst 2 recent: CHECK seconds: 3600 hit_count: 1 name: BADGUY_LIST side: source mask: 255.255.255.255 LOG flags 0 level 6 prefix "BAD GUY:"
      89    11392 DROP       all  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            recent: UPDATE seconds: 3600 hit_count: 1 name: BADGUY_LIST side: source mask: 255.255.255.255
       1      128 ADD_TO_LIST  udp  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:27015 length 128 recent: UPDATE seconds: 1 hit_count: 10 name: BAD_OR_NOT side: source mask: 255.255.255.255
      60     7680 ACCEPT     udp  --  enp9s0 *       0.0.0.0/0            0.0.0.0/0            udp dpt:27015 length 128 recent: SET name: BAD_OR_NOT side: source mask: 255.255.255.255

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
    pkts      bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 208 packets, 31104 bytes)
    pkts      bytes target     prot opt in     out     source               destination

Chain ADD_TO_LIST (1 references)
    pkts      bytes target     prot opt in     out     source               destination
       1      128            all  --  *      *       0.0.0.0/0            0.0.0.0/0            recent: SET name: BADGUY_LIST side: source mask: 255.255.255.255
       1      128 LOG        all  --  *      *       0.0.0.0/0            0.0.0.0/0            limit: avg 3/min burst 2 LOG flags 0 level 6 prefix "BAD_ADD:"
       1      128 DROP       all  --  *      *       0.0.0.0/0            0.0.0.0/0

The last few related /var/log/syslog entries:

Sep 14 08:52:29 cyd-hp2 kernel: [778611.743160] BAD GUY:IN=enp9s0 OUT= MAC=00:26:9e:90:10:8d:f4:6d:04:65:2d:8e:08:00 SRC=192.168.111.249 DST=192.168.111.104 LEN=128 TOS=0x00 PREC=0x00 TTL=64 ID=16043 PROTO=UDP SPT=1682 DPT=27015 LEN=108
Sep 14 08:52:49 cyd-hp2 kernel: [778631.742076] BAD GUY:IN=enp9s0 OUT= MAC=00:26:9e:90:10:8d:f4:6d:04:65:2d:8e:08:00 SRC=192.168.111.249 DST=192.168.111.104 LEN=128 TOS=0x00 PREC=0x00 TTL=64 ID=41223 PROTO=UDP SPT=1686 DPT=27015 LEN=108
Sep 14 08:53:09 cyd-hp2 kernel: [778651.741012] BAD GUY:IN=enp9s0 OUT= MAC=00:26:9e:90:10:8d:f4:6d:04:65:2d:8e:08:00 SRC=192.168.111.249 DST=192.168.111.104 LEN=128 TOS=0x00 PREC=0x00 TTL=64 ID=37530 PROTO=UDP SPT=1690 DPT=27015 LEN=108

EDIT

If the objective is to NOT be source IP address specific, then version 2 below is suggested. It makes use of the --mask option with a mask of 0.0.0.0 to make the DROP criteria non source IP specific. However then the DROP criteria must include the original conditions, because IP address is no longer a useful bad guy identifier:

#!/bin/sh
FWVER=0.02
#
# gamer-cs iptables rule example. Smythies 2017.09.14 Ver:0.02
#     use the --mask option to eliminate any specific IP address.
#     However, then only DROP packets that meet the criteria,
#     otherwise a mess will occur.
#
# gamer-cs iptables rule example. Smythies 2017.09.13 Ver:0.01
#     Protocl: UDP
#     Destination port: 27015
#     Length: 100 payload. The UDP header is always 8 bytes in length.
#                 The IP header is typically 20 bytes but can be longer.
#
#     Banning is a two step process, using the recent module,
#     and can then ban for any desired time.
#
#     Probably needs to be combined with the bigger context of other rules.
#
#     See also:
#     https://askubuntu.com/questions/955425/allow-x-packets-per-second-with-same-data-length-iptables
#            And other questions from gamer-cs.
#
#     https://askubuntu.com/questions/818524/correctly-limit-ip-connections
#     https://chat.stackexchange.com/transcript/51426/2017/1/9
#
#     run as sudo
#

echo "Loading gamer-cs rule example version $FWVER..\n"

# The location of the iptables program
#
IPTABLES=/sbin/iptables

# Some definitions.
# Some of these are for the Smythies test computer. Change as required.
# The external interface name:
EXTIF="enp9s0"
# The external IP address
EXTIP="192.168.111.104"
# Obvious
PORT_TO_CHECK="27015"
UNIVERSE="0.0.0.0/0"

#Clearing any previous configuration
#
echo "  Clearing any existing rules and setting default policies.."
$IPTABLES -P INPUT ACCEPT
$IPTABLES -F INPUT
$IPTABLES -P OUTPUT ACCEPT
$IPTABLES -F OUTPUT
$IPTABLES -P FORWARD ACCEPT
$IPTABLES -F FORWARD
# Otherwise, I can not seem to delete it later on
$IPTABLES -F ADD_TO_LIST
# Delete user defined chains
$IPTABLES -X
# Reset all IPTABLES counters
$IPTABLES -Z

#######################################################################
# USER DEFINED CHAIN SUBROUTINES:
#
# ADD_TO_LIST
# Called from the rate checker.
# Add the IP/0 address to the bad guy list, and DROP the packet.
# If desired, comment out the log rule.
# Rate limit the logging.
$IPTABLES -N ADD_TO_LIST
$IPTABLES -A ADD_TO_LIST -m recent --mask 0.0.0.0 --set --name BADGUY_LIST
$IPTABLES -A ADD_TO_LIST -m limit --limit 3/m --limit-burst 2 -j LOG --log-prefix "BAD_ADD:" --log-level info
$IPTABLES -A ADD_TO_LIST -j DROP


# I (Smythies) need the following rule to prevent my ssh sessions from being locked out
# while testing/debugging
#
$IPTABLES -A INPUT -i $EXTIF -s $UNIVERSE -d $EXTIP -m state --state ESTABLISHED,RELATED -j ACCEPT

# Other INPUT chain rules might be needed before this, not sure.
#
# If there has been any actitivity on the bad guy list in the timeout time, then DROP any packet that meets the crieria. Limit logging (If desired, comment out the log rule).
$IPTABLES -A INPUT -i $EXTIF --protocol udp --destination-port $PORT_TO_CHECK -m length --length 128 -m limit --limit 3/m --limit-burst 2 -m recent --mask 0.0.0.0 --rcheck --hitcount 1 --seconds 3600 --name BADGUY_LIST -j LOG --log-prefix "BAD GUY:" --log-level info
$IPTABLES -A INPUT -i $EXTIF --protocol udp --destination-port $PORT_TO_CHECK -m length --length 128 -m recent --mask 0.0.0.0 --update --hitcount 1 --seconds 3600 --name BADGUY_LIST -j DROP
# Do the actual rate limiting check
$IPTABLES -A INPUT -i $EXTIF --protocol udp --destination-port $PORT_TO_CHECK -m length --length 128 -m recent --mask 0.0.0.0 --update --hitcount 10 --seconds 1 --name BAD_OR_NOT -j ADD_TO_LIST
$IPTABLES -A INPUT -i $EXTIF --protocol udp --destination-port $PORT_TO_CHECK -m length --length 128 -m recent --mask 0.0.0.0 --set --name BAD_OR_NOT -j ACCEPT
# O.K. at this point, carry on with other INPUT chain rules.

echo "gamer-cs rule example version $FWVER done.\n"
derHugo
  • 3,356
  • 5
  • 31
  • 51
Doug Smythies
  • 15,448
  • 5
  • 44
  • 61
  • Thanks for your help, But i have some problems running this script. I put it in file and saved it as test.sh and then uploaded to my /root then try to run using ./test.sh but not running, So how to run it? Also what these things: EXTIF, EXTIP, UNIVERSE? – gamer-cs Sep 14 '17 at 16:59
  • If i understand so this script works like: If one ip send higher than 10 packets/sec with total length 128 his ip will be blocked and drop all packets coming from that ip? – gamer-cs Sep 14 '17 at 17:04
  • 1: Well, like the comment says, those are set for my test computer, you have to change them (well, not UNIVERSE) for your computer. You have to use sudo (mentioned in the header comments) so sudo ./test.sh 2: Yes, for an hour. Any packet in that hour resets the timer. – Doug Smythies Sep 14 '17 at 17:44
  • I give execution permission for that file and then try run it using command you said but i get this sudo: unable to execute ./test.sh: No such file or directory Hangup – gamer-cs Sep 14 '17 at 19:32
  • Ok, I managed to fix this from here: https://unix.stackexchange.com/questions/144718/sudo-unable-to-execute-script-sh-no-such-file-or-directory Just i used these 3 commands: sudo apt-get install dos2unix -y dos2unix test.sh sudo chmod u+x test.sh && sudo ./test.sh But get this iptables: No chain/target/match by that name. – gamer-cs Sep 14 '17 at 19:39
  • Another question, It's not possible to drop like: more than 10 packets/sec with same length from random ips (Assume it's 10 ips) if this event happened so drop all packets with length 10 from any source for 10 seconds. – gamer-cs Sep 14 '17 at 19:47
  • From any IP is possible, sort of, but not exactly, like you mention, using the limit module. I'll do another answer soon, based on that method. – Doug Smythies Sep 14 '17 at 21:11
  • O.K., I do not like the limit module solution. However, by using the --mask option with a 0.0.0.0 mask, and changing the generic DROP rule to one that also meets the criteria, a non IP solution is possible. And I have tested it, as best I can. Since you have accepted this answer, I'm not sure if I should edit it, or do a new one. – Doug Smythies Sep 14 '17 at 22:00
  • Very nice, The two answers is needed so it's ok now. Big thanks for you :) – gamer-cs Sep 15 '17 at 05:02
0

Looks like you need to follow this guide:

  1. sudo iptables -A OUTPUT -j REJECT
  2. sudo iptables -A OUTPUT -m limit --limit X/s -j ACCEPT
M. Dm.
  • 1,482