Traffic Control

ipcop Traffic Shaping

#!/bin/sh
#
# ipcop 1.4.0 Traffic Shaping
#
# execute script from command line
# distributed under GPL License
# author: Allan Kissack 2004
#
echo "* Device eth1 (WWW) .... *"
echo "--------------------------"
echo "  Deleting old QOS classes and root filters"
/sbin/tc qdisc del dev eth1  root 2>/dev/null
/sbin/tc filter del dev eth1 parent 1:1  pref 100  2>/dev/null
/sbin/tc filter del dev eth1 parent 1:   pref 100  2>/dev/null
echo "  Initializing Traffic control, building root classes..."
/sbin/tc qdisc add dev eth1 root handle 1: htb default 89 r2q 1
/sbin/tc class add dev eth1 parent 1:0 classid 1:1 htb rate 256kbit burst 8k
echo "  Initializing packet mangling..."
/sbin/iptables -t mangle -F
/sbin/iptables -t mangle -X
/sbin/iptables -F PREROUTING -t mangle
/sbin/iptables -A PREROUTING -t mangle -s 192.168.0.0/24 -j MARK --set-mark 255
echo "  Building Upstream classes..."
echo "    1:10 for ip 192.168.0.10"
/sbin/tc class  add dev eth1 parent 1:1 classid 1:10 htb prio 1 rate 54kbit ceil 219kbit burst 8k
/sbin/tc filter del dev eth1  parent 1:10 pref 100  2>/dev/null
/sbin/iptables -A PREROUTING -t mangle -s 192.168.0.10/32 -j MARK --set-mark 10
/sbin/tc filter add dev eth1 parent 1: protocol ip handle 10 pref 100 fw classid 1:10
/sbin/tc class  add dev eth1 parent 1:10 classid 1:100 htb prio 0 rate 27kbit ceil 219kbit burst 8k
/sbin/tc class  add dev eth1 parent 1:10 classid 1:101 htb prio 1 rate 18kbit ceil 219kbit burst 4k
/sbin/tc class  add dev eth1 parent 1:10 classid 1:102 htb prio 1 rate  8kbit ceil 219kbit burst 2k
/sbin/tc qdisc  add dev eth1 parent 1:100 handle 100: sfq perturb 10
/sbin/tc qdisc  add dev eth1 parent 1:101 handle 101: sfq perturb 10
/sbin/tc qdisc  add dev eth1 parent 1:102 handle 102: sfq perturb 10
/sbin/tc filter add dev eth1 protocol ip parent 1:10 pref 100 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid 1:100
/sbin/tc filter add dev eth1 protocol ip parent 1:10 pref 100 u32 match ip protocol 1 0xff   flowid 1:100
/sbin/tc filter add dev eth1 protocol ip parent 1:10 pref 100 u32 match ip src 0.0.0.0/0 flowid 1:101
echo "    1:11 for ip 192.168.0.11"
/sbin/tc class  add dev eth1 parent 1:1 classid 1:11 htb prio 1 rate 54kbit ceil 219kbit burst 8k
/sbin/tc filter del dev eth1  parent 1:11 pref 100  2>/dev/null
/sbin/iptables -A PREROUTING -t mangle -s 192.168.0.11/32 -j MARK --set-mark 11
/sbin/tc filter add dev eth1 parent 1: protocol ip handle 11 pref 100 fw classid 1:11
/sbin/tc class  add dev eth1 parent 1:11 classid 1:110 htb prio 0 rate 27kbit ceil 219kbit burst 8k
/sbin/tc class  add dev eth1 parent 1:11 classid 1:111 htb prio 1 rate 18kbit ceil 219kbit burst 4k
/sbin/tc class  add dev eth1 parent 1:11 classid 1:112 htb prio 2 rate  8kbit ceil 219kbit burst 2k
/sbin/tc qdisc  add dev eth1 parent 1:110 handle 110: sfq perturb 10
/sbin/tc qdisc  add dev eth1 parent 1:111 handle 111: sfq perturb 10
/sbin/tc qdisc  add dev eth1 parent 1:112 handle 112: sfq perturb 10
/sbin/tc filter add dev eth1 protocol ip parent 1:11 pref 100 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid 1:110
/sbin/tc filter add dev eth1 protocol ip parent 1:11 pref 100 u32 match ip protocol 1 0xff   flowid 1:110
/sbin/tc filter add dev eth1 protocol ip parent 1:11 pref 100 u32 match ip src 0.0.0.0/0 flowid 1:111
echo "    1:12 for ip 192.168.0.234"
/sbin/tc class  add dev eth1 parent 1:1 classid 1:12 htb prio 1 rate 54kbit ceil 219kbit burst 8k
/sbin/tc filter del dev eth1  parent 1:12 pref 100  2>/dev/null
/sbin/iptables -A PREROUTING -t mangle -s 192.168.0.234/32 -j MARK --set-mark 12
/sbin/tc filter add dev eth1 parent 1: protocol ip handle 12 pref 100 fw classid 1:12
/sbin/tc class  add dev eth1 parent 1:12 classid 1:120 htb prio 0 rate 27kbit ceil 219kbit burst 8k
/sbin/tc class  add dev eth1 parent 1:12 classid 1:121 htb prio 1 rate 18kbit ceil 219kbit burst 4k
/sbin/tc class  add dev eth1 parent 1:12 classid 1:122 htb prio 1 rate  8kbit ceil 219kbit burst 2k
/sbin/tc qdisc  add dev eth1 parent 1:120 handle 120: sfq perturb 10
/sbin/tc qdisc  add dev eth1 parent 1:121 handle 121: sfq perturb 10
/sbin/tc qdisc  add dev eth1 parent 1:122 handle 122: sfq perturb 10
/sbin/tc filter add dev eth1 protocol ip parent 1:12 pref 100 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid 1:120
/sbin/tc filter add dev eth1 protocol ip parent 1:12 pref 100 u32 match ip protocol 1 0xff   flowid 1:120
/sbin/tc filter add dev eth1 protocol ip parent 1:12 pref 100 u32 match ip src 0.0.0.0/0 flowid 1:121
echo "    1:90 1:89 defaults"
/sbin/tc class  add dev eth1 parent 1:1 classid 1:90 htb prio 2 rate 12kbit ceil 256kbit burst 2k
/sbin/tc qdisc  add dev eth1 parent 1:90 handle 90: sfq perturb 10
/sbin/tc class  add dev eth1 parent 1:1 classid 1:89 htb prio 1 rate 25kbit ceil 256kbit burst 4k
/sbin/tc qdisc  add dev eth1 parent 1:89 handle 89: sfq perturb 10
/sbin/tc filter add dev eth1 parent 1: protocol ip handle 255 pref 100 fw classid 1:90
echo "* Device eth0 (LAN) .... *"
echo "--------------------------"
echo "  Deleting old QOS classes and filters"
/sbin/tc qdisc del dev eth0 root 2>/dev/null
/sbin/tc filter del dev eth0 parent 1:2  pref 100  2>/dev/null
/sbin/tc filter del dev eth0 parent 1:1  pref 100  2>/dev/null
/sbin/tc filter del dev eth0 parent 1:   pref 100  2>/dev/null
echo "  Initializing Traffic control, building root classes..."
/sbin/tc qdisc add dev eth0 root handle 1: htb default 90 r2q 1
/sbin/tc class add dev eth0 parent 1: classid 1:1 htb rate 512kbit burst 16k
/sbin/tc class add dev eth0 parent 1: classid 1:2 htb rate 100mbit burst 16k
echo "  Building Downstream classes..."
echo "    1:10 for ip 192.168.0.10"
/sbin/tc class  add dev eth0 parent 1:1 classid 1:10 htb prio 1 rate 125kbit ceil 500kbit burst 16k
/sbin/tc filter del dev eth0 parent 1:10 pref 100  2>/dev/null
/sbin/tc filter add dev eth0 protocol ip parent 1:1 pref 100 u32 match ip dst 192.168.0.10/32 flowid 1:10
/sbin/tc class  add dev eth0 parent 1:10 classid 1:100 htb prio 0 rate 62kbit ceil 500kbit burst 16k
/sbin/tc class  add dev eth0 parent 1:10 classid 1:101 htb prio 1 rate 43kbit ceil 500kbit burst 8k
/sbin/tc class  add dev eth0 parent 1:10 classid 1:102 htb prio 1 rate 18kbit ceil 500kbit burst 4k
/sbin/tc qdisc  add dev eth0 parent 1:100 handle 100: sfq perturb 10
/sbin/tc qdisc  add dev eth0 parent 1:101 handle 101: sfq perturb 10
/sbin/tc qdisc  add dev eth0 parent 1:102 handle 102: sfq perturb 10
/sbin/tc filter add dev eth0 protocol ip parent 1:10 pref 100 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid 1:100
/sbin/tc filter add dev eth0 protocol ip parent 1:10 pref 100 u32 match ip protocol 1 0xff   flowid 1:100
/sbin/tc filter add dev eth0 protocol ip parent 1:10 pref 100 u32 match ip dst 0.0.0.0/0 flowid 1:101
echo "    1:11 for ip 192.168.0.11"
/sbin/tc class  add dev eth0 parent 1:1 classid 1:11 htb prio 1 rate 125kbit ceil 500kbit burst 16k
/sbin/tc filter del dev eth0 parent 1:11 pref 100  2>/dev/null
/sbin/tc filter add dev eth0 protocol ip parent 1:1 pref 100 u32 match ip dst 192.168.0.11/32 flowid 1:11
/sbin/tc class  add dev eth0 parent 1:11 classid 1:110 htb prio 0 rate 62kbit ceil 500kbit burst 16k
/sbin/tc class  add dev eth0 parent 1:11 classid 1:111 htb prio 1 rate 43kbit ceil 500kbit burst 8k
/sbin/tc class  add dev eth0 parent 1:11 classid 1:112 htb prio 2 rate 18kbit ceil 500kbit burst 4k
/sbin/tc qdisc  add dev eth0 parent 1:110 handle 110: sfq perturb 10
/sbin/tc qdisc  add dev eth0 parent 1:111 handle 111: sfq perturb 10
/sbin/tc qdisc  add dev eth0 parent 1:112 handle 112: sfq perturb 10
/sbin/tc filter add dev eth0 protocol ip parent 1:11 pref 100 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid 1:110
/sbin/tc filter add dev eth0 protocol ip parent 1:11 pref 100 u32 match ip protocol 1 0xff   flowid 1:110
/sbin/tc filter add dev eth0 protocol ip parent 1:11 pref 100 u32 match ip dst 0.0.0.0/0 flowid 1:111
echo "    1:12 for ip 192.168.0.234"
/sbin/tc class  add dev eth0 parent 1:1 classid 1:12 htb prio 1 rate 125kbit ceil 500kbit burst 16k
/sbin/tc filter del dev eth0 parent 1:12 pref 100  2>/dev/null
/sbin/tc filter add dev eth0 protocol ip parent 1:1 pref 100 u32 match ip dst 192.168.0.234/32 flowid 1:12
/sbin/tc class  add dev eth0 parent 1:12 classid 1:120 htb prio 0 rate 62kbit ceil 500kbit burst 16k
/sbin/tc class  add dev eth0 parent 1:12 classid 1:121 htb prio 1 rate 43kbit ceil 500kbit burst 8k
/sbin/tc class  add dev eth0 parent 1:12 classid 1:122 htb prio 1 rate 18kbit ceil 500kbit burst 4k
/sbin/tc qdisc  add dev eth0 parent 1:120 handle 120: sfq perturb 10
/sbin/tc qdisc  add dev eth0 parent 1:121 handle 121: sfq perturb 10
/sbin/tc qdisc  add dev eth0 parent 1:122 handle 122: sfq perturb 10
/sbin/tc filter add dev eth0 protocol ip parent 1:12 pref 100 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid 1:120
/sbin/tc filter add dev eth0 protocol ip parent 1:12 pref 100 u32 match ip protocol 1 0xff   flowid 1:120
/sbin/tc filter add dev eth0 protocol ip parent 1:12 pref 100 u32 match ip dst 0.0.0.0/0 flowid 1:121
echo "    defaults "
/sbin/tc class add dev eth0 parent 1:1 classid 1:90 htb prio 2 rate 12kbit ceil 512kbit burst 4k
/sbin/tc qdisc add dev eth0 parent 1:90 handle 90: sfq perturb 10
/sbin/tc filter add dev eth0 protocol ip parent 1: pref 100 u32 match ip src 192.168.0.0/16 flowid 1:2
/sbin/tc filter add dev eth0 protocol ip parent 1: pref 100 u32 match ip dst 192.168.0.0/16 flowid 1:1
echo "************************"
echo "*  QOS: init complete  *"
echo "************************"
Register to read more...

Iptables Teaching

> > I believe it would be better to have a different script called from rc.firewall.
> >
> > rc.local is for system wide stuff (not just firewall rules), so calling it from \
> > rc.firewall would not be a good thing. 
> > On my system, I've added the following section to the end of the "start" section \
> > of rc.firewall:
> > # run local firewall configuration, if present
> > if [ -x /etc/rc.d/rc.firewall.local ]; then
> > /etc/rc.d/rc.firewall.local
> > fi
> >
> > Then, I put any rules I want to add to the firewall in \
> > /etc/rc.d/rc.firewall.local. 
> > I also added rc.firewall.local to the backup configuration, so it would be saved \
> > when I backup my system. 
> > I'd love for this to be considered for future releases.
>
> aggreed, as long as the spesial firewall file is included in the floppy
> backup

Committed.

Alan.




Thanks for the answer. I've another question...

this is the code I'm using in /etc/rc.d/rc.firewall.local:

RED_DEV=$(/bin/cat /var/ipcop/red/iface | /usr/bin/tr -d '\012')
/sbin/iptables -I INPUT -i $RED_DEV -p tcp --destination-port 222 -j DSSHD

/sbin/iptables -A DSSHD --source 24.232.0.0/16 -j DROP
/sbin/iptables -A DSSHD --source 61.0.0.0/8 -j DROP

this is my portfw config for ssh:
1,0,tcp,22,192.168.1.3,22,on,0.0.0.0,0.0.0.0/0,


Could you tell me if the '--destination-port 222' is correct for blocking ssh to our internal server?
I suspect it shoud be '--destination-port 22' instead.

Thanks.
Rob



rc.local is a startup file, running any commands you place in there on boot up, including iptable rules.

If you wanted an iptables rule to take effect immediately without rebooting, simply type out the rule (starting with /sbin/iptables) in the console, or via PuTTy.


See: http://www.ipcop.org/cgi-bin/twiki/view/IPCop/IPCopFAQ
Quote:

Add your own local rules to the file /etc/rc.d/rc.firewall.local which is called every time /etc/rc.d/rc.firewall.up runs. You can also add your own commands to /etc/rc.d/rc.local if you want them to run once on startup.
 





Anonymous wrote:
No. Custom commands go into /etc/rc.d/rc.local

Mug

Some things cannot be done in /etc/rc.local.

In the case of the "ping killer" if the commands where placed in "rc.local" where they where only run once earlier on in the boot cycles, possible even before running "/etc/rc.d/rc.firewall start", they would get ERASED when that event happened or the next time then system went off line and came back online by the "/etc/re.d/rc/firewall restart" command happened.

In both cases at one point the following commands happen:
Code:

    # Flush all rules and delete all custom chains
    /sbin/iptables -F
    /sbin/iptables -t nat -F
    /sbin/iptables -X
    /sbin/iptables -t nat -X


Which ERASES any other changes to the iptables state tables.

IOW -- IPCop needs more detecated customizing hooks the just "rc.local"  

So to the other "Guest" question of:
Quote:
Hmm, so is it rc.firewall or rc.local?

It's rc.firewall in this case.

See: http://www.ipcops.net/index.php?name=PNphpBB2&file=viewtopic&t=2294&highlight= The first message from me in that thread for where to place the code.




Guest,  a possible friend who hasn't introduced themself 

Of the AddOn packages listed to only one that does anything with 'iptables' is 'Iptstat' and that's a pair of cron entry of:
Code:

/sbin/iptables -L -v -n           > to some file
/sbin/iptables -L -v -n -t nat   > to some other file


Which are just '--list' requests.

One possible workaround is:
Where you added the PING stopper code it put the following instead
Code:

   /etc/rc.d/rc.filewall.local start
 


Now do:
# > /etc/rc.d/rc.firewall.local
# chmod +x /etc/rc.firewall.local

And then start /etc/rc.d/rc.firewall.local off as follows:Code:
#! /bin/bash
PARM1="$1"
# you could possible use the above to test how this was called.
IFACE=$(/bin/cat /var/ipcop/red/iface | /usr/bin/tr -IFACE=$(/bin/cat /var/ipcop/red/iface | /usr/bin/tr -d '\012')d '\012')
/sbin/iptables -F CUSTOMINPUT

Followed the above by the combined PING stopper code and any 'iptables' code from your current /etc/rc.d/rc.local

It you wish are need to you could also arrange to have it called from /etc/rc.d/rc.local with a different parameter.




The two commands:
Code:

  > /etc/rc.d/rc.firewall.local
  chmod +x /etc/rc.firewall.local
 


Will create the file /etc/rc.d/rc.firewall.local for you.

The lines of code that follow are the beginning lines for that file.

What goes after it the PING/PONG stopper code and any 'iptables' commands from your /etc/rc.d/rc.local file.

When filling in a STATE-TABLE such as that used by 'iptables' the order is as important as what gets put into it.





I am running v1.4 beta and I have tried all sorts IPTABLES configuration, blocking ips.

what works for me is edit rc.local and add: /sbin/iptables -I CUSTOMFORWARD -s 138.247.81.20 -j DROP
This droped any connection from that IP.

Worked like a charm, thanks guys.

FLOODING PROTECTION

## SYN-FLOODING PROTECTION
# This rule maximises the rate of incoming connections. In order to do this
we divert tcp
# packets with the SYN bit set off to a user-defined chain. Up to
limit-burst connections
# can arrive in 1/limit seconds ..... in this case 4 connections in one
second. After this, one
# of the burst is regained every second and connections are allowed again.
The default limit
# is 3/hour. The default limit burst is 5.
#
iptables -N syn-flood
iptables -A INPUT -i $IFACE -p tcp --syn -j syn-flood
iptables -A syn-flood -m limit --limit 1/s --limit-burst 80 -j RETURN
iptables -A syn-flood -j LOG --log-prefix "syn-flood-protection: "
iptables -A syn-flood -j DROP

Neat tricks with iptables

Introduction

The past few months have seen me digging deep into the world of TCP/IP and firewalls. It has been a fascinating journey into packet queueing and TCP headers, three-way handshakes and ICMP broadcasts.

The result of this research has been the ongoing creation of a firewall to protect my laptop against open networks, and my Internet server from port scanning and DoS attacks. I’m pretty certain I haven’t even scratched the surface yet, but I have found some settings to protect against the most common attacks. Below I’ll summarize the major pieces of my new firewall, and the logic behind it.

Address spoofing

The easiest way to fool a server is to construct a packet that whose source address is faked, or spoofed. This is surprisingly easy to do. To craft packets, I use a very powerful network analysis tool called Scapy. Scapy will allow you to create packets on the fly, transmit them, and scan your network for any response.

For example, let’s say I’m on my local network (which I am right now, as I write this), connected via wireless as 192.168.15.113. I’m going to interact with the router, which is at 192.168.15.1. For the purposes of analysis, I’ve also setup a virtual machine running on 192.168.15.114, so I can see what happens when I spoof the packet.

So, let’s say I spoof an ICMP echo-request packet, sent to .1 (router) from .113 (me) but spoofed as if it had come from .114 (virtual machine). In Scapy this is quite easy to do. I run two scapy session in two terminal windows. In the first I type:

>>> send(IP(src="192.168.15.114", dst="192.168.15.1")/ICMP())
.
Sent 1 packets.

Although my machine is at .113, I’m telling scapy to set the source address for the ICMP echo-request packet to .114, which is the host I want to attack. I’m sending this “ping” to the router, which should now send its response back to .114 instead of me.

In my other terminal window, I run scapy again, this time in promiscuous mode as a packet sniffer. Promiscuous mode means that it will capture all packets seen on the network, not just those destined for my own machine. Here’s what I see:

>>> sniff(filter="icmp")
^C<Sniffed: TCP:0 UDP:0 ICMP:2 Other:0>
>>> _.show()
0000 Ether / IP / ICMP 192.168.15.114 > 192.168.15.1 echo-request 0
0001 Ether / IP / ICMP 192.168.15.1 > 192.168.15.114 echo-reply 0

I ran the sniffer, then did the ping, then stopped the sniffer by pressing Control-C. I can see that two ICMP packets were seen during the sniff. By showing the contents of these packets, I can see both the packet that I transmitted, and the response — which came back to .114!

That’s a spoof. How can it be used to attack someone? Read on in the next section, since what we just did forms the basis for a smurf attack.

Some packet spoofs, however, are more obvious. For example, a packet coming from the Internet bound for a private IP address or certain broadcast addresses, such as address beginning with 192.168 or 224. These are never valid, so it’s a good idea to drop such packets immediately upon receipt. Here are the iptables rules to do this:

# Reject packets from RFC1918 class networks (i.e., spoofed)
iptables -A INPUT -s 10.0.0.0/8     -j DROP
iptables -A INPUT -s 169.254.0.0/16 -j DROP
iptables -A INPUT -s 172.16.0.0/12  -j DROP
iptables -A INPUT -s 127.0.0.0/8    -j DROP
iptables -A INPUT -s 224.0.0.0/4      -j DROP
iptables -A INPUT -d 224.0.0.0/4      -j DROP
iptables -A INPUT -s 240.0.0.0/5      -j DROP
iptables -A INPUT -d 240.0.0.0/5      -j DROP
iptables -A INPUT -s 0.0.0.0/8        -j DROP
iptables -A INPUT -d 0.0.0.0/8        -j DROP
iptables -A INPUT -d 239.255.255.0/24 -j DROP
iptables -A INPUT -d 255.255.255.255  -j DROP

Here’s the same thing, now for ipfw users:

# Verify the reverse path to help avoid spoofed packets.  This means any
# packet coming from a particular interface must have an address matching the
# netmask for that interface.
ipfw add 100 deny all from any to any not verrevpath in
# Deny all inbound traffic from RFC1918 address spaces (spoof!)
ipfw add 110 deny all from 192.168.0.0/16 to any in
ipfw add 120 deny all from 172.16.0.0/12 to any in
ipfw add 130 deny all from 10.0.0.0/8 to any in
ipfw add 140 deny all from 127.0.0.0/8 to any in
ipfw add 150 deny all from 224.0.0.0/4 to any in
ipfw add 160 deny all from any to 224.0.0.0/4 in
ipfw add 170 deny all from 240.0.0.0/5 to any in
ipfw add 180 deny all from any to 240.0.0.0/5 in
ipfw add 190 deny all from 0.0.0.0/8 to any in
ipfw add 200 deny all from any to 0.0.0.0/8 in
ipfw add 210 deny all from any to 239.255.255.0/24 in
ipfw add 220 deny all from any to 255.255.255.255 in

Smurf attacks

A smurf attack (which is named after the program people use to perform the attack), consists of three hosts: The attacker, a middle-man, and the victim.

The intention here is to flood the victim with ICMP packets, clogging up their network bandwidth, or exhausting their bandwidth quota with their ISP. The reason for using a middle-man to do this is so that the attacker cannot be identified as the source of the attack.

What the attacker does is to craft an unending stream of ICMP packets, spoofed to appear as if they had originated from the victim. These packets are sent to the middle-man, who responds to each one by sending an ICMP echo-response packet to the victim. The victim, of course, never asked for these packets,but it has no way to stop the unending flood. Even if he calls the middle-man’s ISP, there is no way for the middle-man to easily stop the flood,except by turning off all ICMP traffic. Even if he examines his packet logs,he cannot find out the IP address of the attacker, because the attacker has spoofed the packet.

There is a defense against the smurf attack: rate limit incoming ICMP packets down to an extremely slow trickle. After all, when ICMP traffic is legitimate, it is very low-bandwidth. If someone pings you to see if you’re alive, usually just a few response packets is all that’s necessary. And for the other types of ICMP response — such as when the router informs your network card about routing issues — again only a few packets are needed, not the huge flood that represents a smurf attack.

Here is how to mount a defense using iptables:

# Allow most ICMP packets to be received (so people can check our
# presence), but restrict the flow to avoid ping flood attacks
iptables -A INPUT -p icmp -m icmp --icmp-type address-mask-request -j DROP
iptables -A INPUT -p icmp -m icmp --icmp-type timestamp-request -j DROP
iptables -A INPUT -p icmp -m icmp -m limit --limit 1/second -j ACCEPT

Here we limit ICMP traffic to one packet per second. So, even if someone floods us via smurf, the most packets we’ll ever recieve in a day is just over 86,000. If you want even fewer, increase the limit.

To implement this same rule using ipfw, it’s necessary to use the dummynet traffic shaper to route ICMP packets down a narrow bandwidth channel:

# If you want to make packet decision after pipe inejection, enable this to
# make sure that packets get reinjected into the firewall
#sysctl -w net.inet.ip.fw.one_pass=0
# Rate limit ICMP traffic to avoid line clogging by Smurf attacks.  We
# direct ICMP packets into a 16 Kbit/s link.
ipfw pipe 300 config bw 16Kbit/s queue 1
ipfw pipe 310 config bw 16Kbit/s queue 5
ipfw add 300 drop icmp from any to not me in
ipfw add 310 drop icmp from not me to any out
ipfw add 320 pipe 100 icmp from any to any in
ipfw add 330 pipe 110 icmp from any to any out

Again, the rate limit is configurable, so if you find that 16 Kbit/s is a large percentage of your link (as it may be, say, for a 256 Kbit/s DSL connection), then drop it down to 8 Kbit/s or even 4 Kbit/s.

Bogus packets

Beyond packet spoofing, there are other types of bogus packets an attacker might generate to try to expose flows in your network stack. Take the SYN and FIN flags, for example. TCP SYN is used to request that a TCP connection be opened on a server; TCP FIN is used to terminate an existing connection. So,does it make any sense to send a packet that has both SYN and FIN set together?

Not at all. These kinds of packets are “bogus”, in that they use flag combinations which make no sense. However, some network implementations can be fooled into some strange behavior when such unexpected packets are received. The best defense, then, is just to reject them all. Here’s how to restrict bogus packets using iptables:

# Drop invalid packets immediately
iptables -A INPUT   -m state --state INVALID -j DROP
iptables -A FORWARD -m state --state INVALID -j DROP
iptables -A OUTPUT  -m state --state INVALID -j DROP
# Drop bogus TCP packets
iptables -A INPUT -p tcp -m tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
iptables -A INPUT -p tcp -m tcp --tcp-flags SYN,RST SYN,RST -j DROP

TCP reset attacks

To undestand a TCP reset attack, you first must understand how TCP manages connections. To connect to a remote host, the client initiates a connection using a “three-way handshake”, or sequence of three packets handed back and forth between the client and server, like this:

  1. The client send a TCP SYN packet to the server, with its “seq” field set to a random number.
  2. The server responds with a SYN+ACK packet, whose “ack” field is one greater than the “seq” field of the client’s packet, but whose own “seq” field is another random number, this time chosen by the server.
  3. The client establishes the connection by responding to this packet with an ACK packet whose “ack” field is one greater than the server’s “seq” number, and whose own “seq” field is one greater than the first “seq” value from step 1.

By picking randomized, initial sequence numbers, and then transmitting these sequence numbers along with every packet — incremented once for each packet — the server and client can validate that indeed the next packet received is the correct packet, from the correct sender.

The can best be understand by seeing the actual packets. I will use scapy to manually establish a TCP connection to a server, and show you what the packets look like at each point during the communication:

First, I send the initial SYN packet, using a seq of 0 (instead of a random number). This means that I expect the “ack” field from the server’s response to be set to 1 (seq+1). After sending, I listen for the response packet, the server’s SYN+ACK. This can be done with one command in scapy:

>>> sr1(IP(dst='mail.johnwiegley.com')/TCP(dport=25,flags='S'))
Begin emission:
.Finished to send 1 packets.
..*
Received 4 packets, got 1 answers, remaining 0 packets
<IP version=4L ihl=5L tos=0x0 len=44 id=0 flags=DF frag=0L ttl=51
proto=tcp chksum=0x10d2 src=208.70.150.154 dst=192.168.15.113
options='' |
<TCP sport=smtp dport=ftp_data seq=651538917L ack=1L dataofs=6L
reserved=0L flags=SA window=5840 chksum=0x716b urgptr=0
options=[('MSS', 1452)] |>>

I’ve formatted the results a little, but here you can see the “SA” flags of the response packet (SYN+ACK), and the “ack” field properly set to 1. The seq field has been set to a random number by the server, to 651538917. This means that the packet I send in response must set the “ack” field to 651538918 (seq=ack+1). I can use a little Python magic to make this easier for me, by using the special underbar variable to refer to the details of the received packet:

>>> sr1(IP(dst='mail.johnwiegley.com')/TCP(dport=25,flags='A',ack=_.seq+1,seq=1))
Begin emission:
Finished to send 1 packets.
...............*
Received 16 packets, got 1 answers, remaining 0 packets
<IP version=4L ihl=5L tos=0x0 len=80 id=43334 flags=DF frag=0L ttl=51
proto=tcp chksum=0x6767 src=208.70.150.154 dst=192.168.15.113
options='' |
<TCP sport=smtp dport=ftp_data seq=651538918L ack=1L dataofs=5L
reserved=0L flags=PA window=5840 chksum=0x2cdb urgptr=0
options=[] |
<Raw load='220 mail.johnwiegley.com ESMTP Postfix\r\n' |>>>

Success! The server has responded to our ACK by sending back the initial data packet in the conversation, which contains the opening banner of an SMTP connection. You can see the flags in this answer packet are PA (PSH+ACK), which means that it is an acknowledge of our acknowledge, and that we should consider the data payload immediately rather than waiting for more data to accumulate first. The “seq” field is now one greater than the “seq” field from the SYN+ACK packet (since this is the second packet the server has sent us). Any packet we send back in response must have its “ack” set to one greater than this “seq”.

Now, there are two ways of concluding this connection. If the client wishes to close the connection, he sends a FIN packet, whose “ack” field must contain the proper next value in the sequence. If the server has to finish, he also sends a FIN packet, again whose “ack” must be properly set. If either side must “abort” the connection — usually in order to rebuild it — they send an RST packet instead of a FIN.

This opens up a line of attack, however, since an attacker now only needs to know two things to force us to close our connection: Our IP address, and the next packet number in the sequence. If they have both of these, they can send us a bogus packet, spoofed as if coming from the server, with the RST flag set. If he gets the sequence number right, we have no choice but to assume the server is telling us to tear down our connection.

Of course, guessing the right sequence number is not necessarily easy to do. There are various ways to reduce the number of packets that have to be generated, but a determined attacker will be able to find the right number, if the connection is long-lived. This is the case with some routing hardware, which depends on long-lived connections to work. It will have no effect, of course, on clients that use UDP-based VPNs, because there is no equivalent to the RST flag to disrupt a UDP communication.

How can one defend against a RST attack? The easiest way is just to slow down the receipt of RST packets. Data travels rather quickly on the Internet, and the odds are that the next packet in a sequence will arrive fairly soon. By delaying RST packets by about half a second, it makes it much harder for the attacker to force his packet into the queue before the correct one. It’s not a foolproof defense, but it certainly makes the attacker’s job a great deal more difficult.

This defense cannot be implemented directly in iptables, but requires queueing disciplines to be done correctly. However, a somwhat similar defense can be made simply by rate limiting RST packets, just as we did for ICMP packets above:

# Drop excessive RST packets to avoid SMURF attacks, by given the
# next real data packet in the sequence a better chance to arrive first.
iptables -A INPUT -p tcp -m tcp --tcp-flags RST RST \
-m limit --limit 2/second --limit-burst 2 -j ACCEPT

In ipfw the iplementation is simpler, because we can use the dummynet shaper:

# Delay TCP RESET packets.
ipfw pipe 400 config delay 500
ipfw add 400 pipe 400 tcp from any to any in tcpflags rst

SYN flooding

If you recall the discussion on TCP three-way handshakes in the previous section, we find there is a weak-point in the scheme: For every SYN packet a server receives, he must assign — and remember — the corresponding “seq” value that he sends out with his SYN+ACK, in order to authorize the client’s ACK when it is finally received.

That is, if a client sends a SYN packet, and the server responds with a SYN+ACK packet, it is now waiting for an ACK packet from the client to complete the connection. Until the ACK packet arrives, the server keeps the connection in a “half-open” state, where it keeps track of the “seq” number it assigned to that potential connection, awaiting the client’s ACK packet to complete it.

But this information about the half-open connection takes up memory in the kernel, and the more SYN packets it reecives with hearing an answering ACK, the more half-open connections it will keep reserved. Of course, there is a timeout for these connection, but it is usually large enough that a determined attacker can overwhelm a server’s table space for half-open connections, making it impossible for legitimate clients to connection.

The answer to a SYN flood is to restrict the rate of new connections, since very rarely will a person need to open a flood of new connections all at once. Bare in mind when you set the values for this rule that web pages with tons of tiny icons will prompt an equal number of connection requests from a client every time he access that page.

# Protect against SYN floods by rate limiting the number of new
# connections from any host to 60 per second.  This does *not* do rate
# limiting overall, because then someone could easily shut us down by
# saturating the limit.
iptables -A INPUT -m state --state NEW -p tcp -m tcp --syn \
-m recent --name synflood --set
iptables -A INPUT -m state --state NEW -p tcp -m tcp --syn \
-m recent --name synflood --update --seconds 1 --hitcount 60 -j DROP

The same can be achieved in ipfw using the dummynet shaper:

# Direct SYN
ipfw pipe 500 config bw 64Kbit/s queue 5
ipfw add 500 pipe 500 tcp from any to any in setup

Port scanning

A lot of hosts try to port scan my server these days, looking for open services they can try to exploit. Since I run very few services on my server, what I like to do is look for port connections to a commonly scanned port (port 139, for Windows File Sharing), and then block the hosts who attempt the connection from talking to my server for an entire day. The rule is quite simple using the iptables recent module:

# Anyone who tried to portscan us is locked out for an entire day.
iptables -A INPUT   -m recent --name portscan --rcheck --seconds 86400 -j DROP
iptables -A FORWARD -m recent --name portscan --rcheck --seconds 86400 -j DROP
# Once the day has passed, remove them from the portscan list
iptables -A INPUT   -m recent --name portscan --remove
iptables -A FORWARD -m recent --name portscan --remove
# These rules add scanners to the portscan list, and log the attempt.
iptables -A INPUT   -p tcp -m tcp --dport 139 \
-m recent --name portscan --set -j LOG --log-prefix "Portscan:"
iptables -A INPUT   -p tcp -m tcp --dport 139 \
-m recent --name portscan --set -j DROP
iptables -A FORWARD -p tcp -m tcp --dport 139 \
-m recent --name portscan --set -j LOG --log-prefix "Portscan:"
iptables -A FORWARD -p tcp -m tcp --dport 139 \
-m recent --name portscan --set -j DROP

Unfortunately, there is no way to implement this sandtrap using ipfw alone. It would require a divert rule that sends packets to a user-space daemon, which would keep track of host address and implement the day-long packet drop. Since I don’t use ipfw on any servers at the moment, I haven’t yet written this utility.

Password attacks

Password attacks are becoming a continual nuisance to anyone who runs a server on the Internet. Every day I see hundreds and hundreds of failed login attempts to both my FTP service and my ssh service.

The best way to avoid these logins to the ssh service is to disable password logins entirely. Use public key authentication only, with keys installed by the system administrater when new accounts are created. Then you can simply ignore the failed attempts, as they will never get anywhere.

For FTP, it’s a big tougher, because you want people to be able to login, at least anonymously. For anonymous only logins, I use vsftpd, which lets me disable user-based logins altogether. This also cures the problem, as no failed logins appear in my logfiles anymore.

Conclusion

That’s just a few of the steps I take to protect my server from attacks. Like I said, it’s probably just the tip of the iceberg, but it’s been an enjoyable learning process, and hopefully some good will come of all this arcane knowledge know that I’m finally getting a grasp on it.

ipshaping

#!/bin/sh

# only do this when multiuser
# update-rc.d ipshaping start 12 2 3 4 5 . stop 88 0 1 6 .

tc='/sbin/tc'

stop() {
# ignore errors
$tc qdisc del dev eth0 root >/dev/null 2>&1
$tc qdisc del dev eth0 ingress >/dev/null 2>&1
$tc qdisc del dev ifb0 root >/dev/null 2>&1
}

my_lan='208.69.40.0/24'
sheep_clients='208.69.40.141/32'
sheep_inter='208.69.40.142/32'
mail='208.69.40.137/32'
pry='208.69.40.140/32'
clam='207.241.233.251/32'
mulark='64.81.61.105/32'
# void.arces.net -- sheep upload
void='64.193.138.139/32'

case "$1" in
start|restart|burst)
# monkeybrains lets us burst to 30mbit in the evenings 22:00 to 04:00
if [ "$1" = "burst" ]; then
limit=50mbit
limitmp1=49.9mbit
limitm1=49mbit
else
limit=10mbit
limitmp1=9.9mbit
limitm1=9mbit
fi

# clear any existing lists
stop

# this uses ifb to place a 9.5mbit limit on sheep inbound
echo -n "enabling ip traffic shaping:"
/sbin/modprobe ifb
/sbin/modprobe xt_MARK
/sbin/ifconfig ifb0 up
$tc qdisc add dev eth0 ingress

$tc qdisc add dev ifb0 root handle 1: htb default 2
$tc class add dev ifb0 parent 1: classid 1:1 htb quantum 9000 rate 100mbit ceil 100mbit

# this handles LAN traffic
$tc class add dev ifb0 parent 1:1 classid 1:2 htb quantum 9000 rate 50mbit ceil 50mbit prio 100
$tc filter add dev ifb0 protocol ip pref 100 parent 1: handle 2 fw classid 1:2
$tc qdisc add dev ifb0 parent 1:2 handle 2: pfifo
$tc filter add dev eth0 parent ffff: protocol ip prio 1 u32 \
match ip src $my_lan flowid 1:2 \
action ipt -j MARK --set-mark 2 \
action mirred egress redirect dev ifb0

# this branch is for non-local traffic
$tc class add dev ifb0 parent 1:1 classid 1:3 htb rate 10mbit ceil 10mbit

# non-sheep and ACK inbound
$tc class add dev ifb0 parent 1:3 classid 1:4 htb rate 9mbit ceil 10mbit prio 40
$tc filter add dev ifb0 protocol ip pref 1 parent 1: handle 4 fw classid 1:4
$tc qdisc add dev ifb0 parent 1:4 handle 4: sfq perturb 10 noports
$tc filter add dev eth0 parent ffff: protocol ip prio 10 u32 \
match ip dst 0.0.0.0/0 flowid 1:4 \
action ipt -j MARK --set-mark 4 \
action mirred egress redirect dev ifb0
$tc filter add dev eth0 parent ffff: protocol ip prio 4 u32 \
match u8 0x05 0x0f at 0 \
match u16 0x0000 0xff80 at 2 \
match ip protocol 6 0xff \
match u8 0x10 0xff at 33 \
flowid 1:4 \
action ipt -j MARK --set-mark 4 \
action mirred egress redirect dev ifb0

# sheep inbound
$tc class add dev ifb0 parent 1:3 classid 1:5 htb rate 1mbit ceil 9.5mbit prio 50
$tc filter add dev ifb0 protocol ip pref 2 parent 1: handle 5 fw classid 1:5
$tc qdisc add dev ifb0 parent 1:5 handle 5: sfq perturb 10 noports
$tc filter add dev eth0 parent ffff: protocol ip prio 5 u32 \
match ip dst $sheep_clients flowid 1:5 \
action ipt -j MARK --set-mark 5 \
action mirred egress redirect dev ifb0

# egress
F="$tc filter add dev eth0 protocol ip parent 1:"

$tc qdisc add dev eth0 root handle 1: htb default 3
$tc class add dev eth0 parent 1: classid 1:1 htb quantum 9000 rate 100mbit ceil 100mbit

# this branch is for LAN traffic
$tc class add dev eth0 parent 1:1 classid 1:101 htb quantum 9000 rate 50mbit ceil 50mbit
$tc qdisc add dev eth0 parent 1:101 handle 101: pfifo
$F prio 1 u32 match ip dst $my_lan flowid 1:101

# this branch is for non-local traffic
$tc class add dev eth0 parent 1:1 classid 1:102 htb rate $limit ceil $limit

# mostly interactive stuff
$tc class add dev eth0 parent 1:102 classid 1:2 htb rate 2.0mbit ceil $limitmp1 prio 10
$tc qdisc add dev eth0 parent 1:2 handle 2: sfq perturb 10 noports nosrcip
# all IPTOS_LOWDELAY stuff -- i.e. ssh interactive
# we put ICMP here so that pings show as close to raw unadultered latency as possible
$F prio 9 u32 match ip tos 0x10 0xff flowid 1:2 # tos = 0x10
$F prio 9 u32 match ip protocol 1 0xff flowid 1:2 # ICMP
$F prio 9 u32 match ip sport 995 0xffff flowid 1:2 # imaps
$F prio 9 u32 match ip sport 993 0xffff flowid 1:2 # pop3s
$F prio 9 u32 match ip dport 7326 0xffff flowid 1:2 # icb
$F prio 9 u32 match ip sport 25 0xffff match ip src $mail flowid 1:2 # mail.arctic.org:smtp
$F prio 9 u32 match ip sport 465 0xffff match ip src $mail flowid 1:2 # mail.arctic.org:smtps
$F prio 9 u32 match ip sport 587 0xffff match ip src $mail flowid 1:2 # mail.arctic.org:submission
$F prio 9 u32 match ip sport 53 0xffff flowid 1:2 # dns in
$F prio 9 u32 match ip dport 53 0xffff flowid 1:2 # dns out
$F prio 9 u32 match ip sport 123 0xffff flowid 1:2 # ntp in
$F prio 9 u32 match ip dport 123 0xffff flowid 1:2 # ntp out
$F prio 5 u32 match ip sport 443 0xffff match ip src $sheep_clients flowid 1:2 # ssh port 443 hack
$F prio 6 u32 \
match u8 0x05 0x0f at 0 \
match u16 0x0000 0xff80 at 2 \
match ip protocol 6 0xff \
match u8 0x10 0xff at 33 \
flowid 1:2 # TCP ACKs < 128 bytes

# the default class
$tc class add dev eth0 parent 1:102 classid 1:3 htb rate 1.00mbit ceil $limitmp1 prio 40
$tc qdisc add dev eth0 parent 1:3 handle 3: sfq perturb 10 noports nosrcip
$F prio 10 u32 match ip dst 0.0.0.0/0 flowid 1:3 # default (ftp, smtp)
#$F prio 7 u32 match ip dst $mulark match ip tos 0x08 0xff flowid 1:3

# dhaskovec's queue
$tc class add dev eth0 parent 1:102 classid 1:7 htb rate 1.0mbit ceil $limitmp1 prio 45
$tc qdisc add dev eth0 parent 1:7 handle 7: sfq perturb 10 noports nosrcip
$F prio 6 u32 match ip src $pry flowid 1:7 # twinlark.pry.com

# highbandwidth stuff
$tc class add dev eth0 parent 1:102 classid 1:4 htb rate 1.00mbit ceil $limitmp1 prio 50
$tc qdisc add dev eth0 parent 1:4 handle 4: sfq perturb 10 noports nosrcip
$F prio 8 u32 match ip tos 0x08 0xff flowid 1:4 # any throughput
$F prio 4 u32 match ip dst $clam match ip src $sheep_clients flowid 1:4 # sheep->clam
$F prio 4 u32 match ip dst $void flowid 1:4 # sheep upload

# electricsheep.org, sheepserver.net
$tc class add dev eth0 parent 1:102 classid 1:5 htb rate 0.50mbit ceil $limitm1 prio 60
$tc qdisc add dev eth0 parent 1:5 handle 5: sfq perturb 10 noports nosrcip
$F prio 5 u32 match ip src $sheep_inter flowid 1:5 # sheepserver.net

# sheep clients
$tc class add dev eth0 parent 1:102 classid 1:8 htb rate 0.50mbit ceil $limitm1 prio 60
$tc qdisc add dev eth0 parent 1:8 handle 8: sfq perturb 10 noports nosrcip
$F prio 4 u32 match ip sport 80 0xffff match ip src $sheep_clients flowid 1:8
$F prio 4 u32 match ip sport 29540 0xffff match ip src $sheep_clients flowid 1:8

# the bt seed has arbitrary ports
$tc class add dev eth0 parent 1:102 classid 1:6 htb rate 0.50mbit ceil $limitm1 prio 70
$tc qdisc add dev eth0 parent 1:6 handle 6: sfq perturb 10 noports nosrcip
$F prio 5 u32 match ip src $sheep_clients flowid 1:6

echo " egress."
;;

stop)
stop
;;

status)
# the munin script expects these labels
echo "1:2 interactive, ACKs"
echo "1:3 small www, smtp, remote backups"
echo "1:7 pry.com"
echo "1:4 large www, scp/sftp, sheep->archive/arces"
echo "1:5 sheepserver.net, electricsheep.org"
echo "1:8 sheep clients (web and bt tracker)"
echo "1:6 sheep bittorrent seed"
echo "==="
$tc -s qdisc ls dev eth0
$tc -s class ls dev eth0
;;

ingress_status)
$tc -s qdisc ls dev ifb0
$tc -s class ls dev ifb0
;;
*)
echo "usage: $0 start|stop|restart|status|ingress_status"
;;
esac

exit 0